I'm trying to mock a method call from class called RestClient in another called DiscoveryAdapter but as and when i'm trying to do a doReturn it throws a null pointer exception.
In test i'm trying to spy the restClient object in order to mock the method call in DiscoveryAdapter.discovery method but with it throws a null pointer exception when doReturn is called.
Please check the code below.
RestClient.class
#Component
#Scope("prototype")
public class RestClient {
private static final Logger logger = LoggerFactory.getLogger(RestClient.class);
private RestTemplate restTemplate = null;
private PoolingHttpClientConnectionManager syncConnectionManager;
public void init() {
restTemplate = new RestTemplate(new HttpComponentsClientHttpRequestFactory(buildhttpClient()));
}
public String queryRestEndpoints(HereMapsImpl mapType,String basePath,boolean isSSl) {
URI uriObject = null;
String uriPath = mapType.returnUri();
init();
try {
uriObject = RestUtils.buildUriFromString(basePath, isSSl, uriPath);
} catch (URISyntaxException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
logger.info("rest api is {}",uriObject);
//spring api
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
HttpEntity<String> requestEntity = new HttpEntity<String>("",headers);
//rest api call with uriObject
ResponseEntity<String> responseEntity = restTemplate.exchange(uriObject,
HttpMethod.GET, requestEntity, String.class);
if(responseEntity.getStatusCodeValue() >=400 || responseEntity.getStatusCodeValue() <= 500)
logger.info("failed with status code {}",responseEntity.getStatusCodeValue() );
logger.info("checking my response");
logger.info("checking my response here {}",responseEntity.getBody());
return responseEntity.getBody();
}
private CloseableHttpClient buildhttpClient() {
CloseableHttpClient httpClient = null;
httpClient = HttpClientBuilder.create().setConnectionManager(getSyncConnectionManager())
.build();
return httpClient;
}
public PoolingHttpClientConnectionManager getSyncConnectionManager() {
syncConnectionManager = new PoolingHttpClientConnectionManager();
return syncConnectionManager;
}
DisoveryAdapter.class
#Component
public class DisoveryAdapter {
private static final Logger log = LoggerFactory.getLogger(DisoveryAdapter.class);
#Autowired
private RestClient restClient;
public void discovery() {
log.info("starting thread");
String response = restClient.queryRestEndpoints(eatDrink, completeUriPath, true);
log.info("oeat drink response {}",response);
}
}
DiscoveryAdapterTest.class
class HereMapsCheckImpTest {
private static final Logger log = LoggerFactory.getLogger(HereMapsCheckImpTest.class);
private RestClient restClient;
private HereMapsCheckImp hereMaps;
#Before
public void init() {
hereMaps = new HereMapsCheckImp();
restClient = new RestClient();
}
#Test
public void test() {
RestClient spyRestClient = spy(restClient);
log.info("with spring {}",spyRestClient);
doReturn("{\n" +
" color: \"red\",\n" +
" value: \"#f00\"\n" +
"}").when(spyRestClient).queryRestEndpoints(any(),any(),any());
hereMaps.discovery();
verify(spyRestClient).queryRestEndpoints(any(),any(),any());
}
}
Related
I have created a cloud function to print greetings like "Hello David!". my function below:
public void service(HttpRequest request, HttpResponse response)
throws IOException {
String name = request.getFirstQueryParameter("name").orElse("world");
try {
JsonElement requestParsed = gson.fromJson(request.getReader(), JsonElement.class);
JsonObject requestJson = null;
if (requestParsed != null && requestParsed.isJsonObject()) {
requestJson = requestParsed.getAsJsonObject();
}
if (requestJson != null && requestJson.has("name")) {
name = requestJson.get("name").getAsString();
}
} catch (JsonParseException e) {
logger.severe("Error parsing JSON: " + e.getMessage());
}
var writer = new PrintWriter(response.getWriter());
writer.printf("Hello %s!", name);
}
I have written a integration test like below:
#BeforeEach
public void setUp() throws IOException {
String baseDir = System.getProperty("user.dir");
emulatorProcess = new ProcessBuilder()
.command("./../gradlew", "alo")
.directory(new File(baseDir))
.start();
}
#AfterEach
public void tearDown() throws IOException {
InputStream stdoutStream = emulatorProcess.getInputStream();
ByteArrayOutputStream stdoutBytes = new ByteArrayOutputStream();
stdoutBytes.write(stdoutStream.readNBytes(stdoutStream.available()));
System.out.println(stdoutBytes.toString(StandardCharsets.UTF_8));
if (emulatorProcess.isAlive()) {
emulatorProcess.destroy();
}
}
#Test
void helloHttp_shouldRunWithFunctionsFramework() throws Throwable {
String functionUrl = BASE_URL + "/helloHttp";
HttpRequest getRequest = HttpRequest.newBuilder().uri(URI.create(functionUrl)).GET().build();
RetryRegistry registry = RetryRegistry.of(RetryConfig.custom()
.maxAttempts(6)
.intervalFunction(IntervalFunction.ofExponentialBackoff(200, 2))
.retryExceptions(IOException.class)
.retryOnResult(body -> body.toString().length() == 0)
.build());
Retry retry = registry.retry("my");
String body = Retry.decorateCheckedSupplier(retry, () -> client.send(
getRequest,
HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8)).body()
).apply();
assertThat(body).isEqualTo("Hello world!");
}
So I want to fake a params name in the integration test to pass to my GCP function.
so in my GCP function I can take it like this: name = requestJson.get("name").getAsString();. How do I do it?
I just search it out and the answer is: add post method like this
HttpRequest getRequest = HttpRequest
.newBuilder()
.uri(URI.create(functionUrl))
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString("{\"name\":\"David\"}"))
.build();
This is my AppService Class, where I need to add logic for getting the status for the circuit breaker if it's open or closed but I am unable to find a way. Also, i have made use of ignoreExceptions but seems like trouble is there. Just new to coding and this feature and unable to get an appropriate answer. I am not sure how to use isCircuitBreakerOpen().
#Service
public class AppService {
private static final Logger LOG = LoggerFactory.getLogger(AppService.class);
private final RestTemplate restTemplate;
public AppService(RestTemplate rest) {
this.restTemplate = rest;
}
#HystrixCommand(fallbackMethod = "reliable", commandProperties= {
#HystrixProperty(name = HystrixPropertiesManager.CIRCUIT_BREAKER_ERROR_THRESHOLD_PERCENTAGE, value = "100"),
#HystrixProperty(name = HystrixPropertiesManager.CIRCUIT_BREAKER_SLEEP_WINDOW_IN_MILLISECONDS, value = "10000"),
#HystrixProperty(name = HystrixPropertiesManager.CIRCUIT_BREAKER_REQUEST_VOLUME_THRESHOLD, value = "10")
})
public ResponseEntity<String> answerList() throws Exception {
return callingDownStreamService404();
}
#HystrixCommand(fallbackMethod = "reliable", commandProperties= {
#HystrixProperty(name = HystrixPropertiesManager.CIRCUIT_BREAKER_ERROR_THRESHOLD_PERCENTAGE, value = "100"),
#HystrixProperty(name = HystrixPropertiesManager.CIRCUIT_BREAKER_SLEEP_WINDOW_IN_MILLISECONDS, value = "10000"),
#HystrixProperty(name = HystrixPropertiesManager.CIRCUIT_BREAKER_REQUEST_VOLUME_THRESHOLD, value = "10")
})
public ResponseEntity<String> answerList503() throws Exception {
return callingDownStreamService503();
}
private ResponseEntity<String> callingDownStreamService404() throws Exception {
URI uri = URI.create("http://localhost:8090/recommended/404");
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
HttpEntity<Object> entity = new HttpEntity<Object>(headers);
ResponseEntity<String> out = restTemplate.exchange(uri, HttpMethod.GET, entity, String.class);
System.out.println("Application code : " + out.getStatusCode());
return out;
}
private ResponseEntity<String> callingDownStreamService503() throws Exception {
URI uri = URI.create("http://localhost:8090/recommended/503");
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
HttpEntity<Object> entity = new HttpEntity<Object>(headers);
ResponseEntity<String> out = restTemplate.exchange(uri, HttpMethod.GET, entity, String.class);
System.out.println("Application code : " + out.getStatusCode());
if (out.getStatusCode().toString().startsWith("5")) {
throw new HystrixBadRequestException("bad request messageg");
}
return out;
}
#HystrixCommand(commandKey = "MyHystrixCommand",fallbackMethod = "myHystrixFallback", threadPoolKey = "ThreadPoolKey",commandProperties= {
#HystrixProperty(name = HystrixPropertiesManager.CIRCUIT_BREAKER_ERROR_THRESHOLD_PERCENTAGE, value = "100"),
#HystrixProperty(name = HystrixPropertiesManager.CIRCUIT_BREAKER_SLEEP_WINDOW_IN_MILLISECONDS, value = "10000"),
#HystrixProperty(name = HystrixPropertiesManager.CIRCUIT_BREAKER_REQUEST_VOLUME_THRESHOLD, value = "10")},
ignoreExceptions = {HttpServerErrorException.class, HystrixBadRequestException.class, HttpClientErrorException.class})
public ResponseEntity<String> getServiceCallResponse(String serviceUrl, HttpEntity<?> entity) {
serviceUrl = "http://localhost:8090/recommended/500";
ResponseEntity<String> resp = null;
try {
System.out.println("Calling -----" + serviceUrl);
resp = restTemplate.exchange(serviceUrl, HttpMethod.POST, entity, String.class);
}
catch(RestClientException e) {
System.out.println("Calling -----" + serviceUrl + "Exception is this" + e.getRootCause());
handleExceptionForHystrix("getServiceCallResponse", e);
}
return resp;
}
private void handleExceptionForHystrix(String function, Exception e) {
if (e instanceof HttpStatusCodeException) {
HttpStatus httpStatusCode = ((HttpStatusCodeException)e).getStatusCode();
if(httpStatusCode.equals(HttpStatus.BAD_REQUEST) || httpStatusCode.equals(HttpStatus.INTERNAL_SERVER_ERROR)) {
throw new HystrixBadRequestException("Hystrix Bad Request Exception Occurred" + httpStatusCode, e);
}
throw new RuntimeException(function, e);
}
throw new RuntimeException(function, e);
}
public ResponseEntity<String> myHystrixFallback(String serviceUrl, HttpEntity<?> entity, Throwable hystrixCommandExp) {
return new ResponseEntity<String>(HttpStatus.INTERNAL_SERVER_ERROR);
}
#Recover()
public ResponseEntity<String> reliable() {
return new ResponseEntity<String>(
"The downstream application is unavailable and the circuit is open", HttpStatus.OK);
}
}
this is the main class where i have placed the endpoints. I am also making use of AppStore which is the downstream app and same enpoints are configured there.
#EnableHystrixDashboard
#EnableCircuitBreaker
#RestController
#SpringBootApplication
public class DavinciCircuitbreakerApplication {
#Autowired
private AppService appService;
#Bean
public RestTemplate rest(RestTemplateBuilder builder) {
return builder.build();
}
#RequestMapping("/to-answer/404")
public ResponseEntity<String> toAnswer() {
ResponseEntity<String> response = null;
try{
response = appService.answerList();
}catch(Exception e){
System.out.println("excpetion"+ e.getMessage());
return new ResponseEntity<String>("failure", HttpStatus.valueOf(500));
}
return response;
}
#RequestMapping("/to-answer/503")
public ResponseEntity<String> toAnswer503() {
ResponseEntity<String> response = null;
try{
response = appService.answerList503();
}catch(Exception e){
System.out.println("excpetion"+ e.getMessage());
return new ResponseEntity<String>("failure", HttpStatus.valueOf(503));
}
return response;
}
#RequestMapping("/to-answer/500")
public ResponseEntity<String> toAnswer500() {
ResponseEntity<String> response = null;
try{
response = appService.getServiceCallResponse(null, response);
}catch(Exception e){
System.out.println("excpetion"+ e.getMessage());
return new ResponseEntity<String>("Internal Server Error", HttpStatus.valueOf(500));
}
return response;
}
public static void main(String[] args) {
SpringApplication.run(DavinciCircuitbreakerApplication.class, args);
}
}
You can use HystrixCircuitBreaker.Factory.getInstance(...) to get instance of HystrixCommand. Refer to link for more details.
Further you can call isCircuitBreakerOpen() on this HystrixCommand instance.
I have Azure logic app with request trigger. I want to trigger this logic app from my java application. So, I am trying to call the request trigger url from my java API.
It is working fine if i am using DefaultHttpClient but getting 401 on calling it using RestTemplate in java.
DefaultHttpClient code:
try {
DefaultHttpClient httpClient = new DefaultHttpClient();
HttpGet getRequest = new HttpGet(
"{url of azure logic app trigger}");
//StringEntity input = new StringEntity("{\"qty\":100,\"name\":\"iPad 4\"}");
//input.setContentType("application/json");
//postRequest.setEntity(input);
HttpResponse response = httpClient.execute(getRequest);
BufferedReader br = new BufferedReader(
new InputStreamReader((response.getEntity().getContent())));
String output;
System.out.println("Output from Server .... \n");
while ((output = br.readLine()) != null) {
System.out.println(output);
}
httpClient.getConnectionManager().shutdown();
return("Success");
} catch (ClientProtocolException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return("Error");
} catch (UnsupportedOperationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return("Error");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return("Error");
}
RestTemplate Code
#Service
public class SampleService {
#Autowired HttpClientService<String, String> httpClientService;
public String callURL() {
ResponseErrorHandler responseErrorHandler = new ResponseErrorHandler() {
#Override
public boolean hasError(ClientHttpResponse response) throws IOException {
System.out.print(response.toString());
return false;
}
#Override
public void handleError(ClientHttpResponse response) throws IOException {
// TODO Auto-generated method stub
}
};
UriComponentsBuilder builder = UriComponentsBuilder
.fromUriString("{logic app url}")
// Add query parameter
.queryParam("api-version", {api-version})
.queryParam("sp", {sp})
.queryParam("sv", {sv})
.queryParam("sig",{sig});
RequestDetailsDAO requestDetails = new RequestDetailsDAO(builder.build().toUri().toString(), HttpMethod.GET);
String response = httpClientService.execute(requestDetails, null, responseErrorHandler, String.class);
return response.toString();
HttpClientService.java
#Service
public class HttpClientService<T, V> {
public RestTemplate restTemplate;
public HttpClientService(RestTemplateBuilder restTemplateBuilder) {
this.restTemplate = restTemplateBuilder.setConnectTimeout(Duration.ofSeconds(5)).setReadTimeout(Duration.ofSeconds(5)).build();
}
public V execute(RequestDetailsDAO requestDetails, HttpEntity<T> entity, ResponseErrorHandler errorHandler,
Class<V> genericClass) {
restTemplate.setErrorHandler(errorHandler);
ResponseEntity<V> response = restTemplate.exchange(requestDetails.getUrl(), requestDetails.getRequestType(), entity, genericClass);
return response.getBody();
}
}
RequestDetailsDAO.java
public class RequestDetailsDAO {
private String url;
private HttpMethod requestType;
public RequestDetailsDAO(String url, HttpMethod requestTyp) {
super();
this.url = url;
this.requestType = requestTyp;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public HttpMethod getRequestType() {
return requestType;
}
public void setRequestType(HttpMethod requestType) {
this.requestType = requestType;
}
#Override
public String toString() {
return "RequestDetails [url=" + url + ", requestType=" + requestType + "]";
}
}
So, it seems that there's nothing special about LogicApps being called using RestTemplate.
It's just that RestTemplate by default URL encode the Uri given in parameter, and DefaultHttpClient does not - reference: apache vs resttemplate
In case of LogicApp URL, there is this "sp" parameter which is already URL endcoded, when you copy it from LogicApp -
"%2Ftriggers%2Fmanual%2Frun", so you need to decode that and pass "/triggers/manual/run" to UriComponentsBuilder. And then it works.
My code:
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<Object> request = new HttpEntity<>(requestDto, headers);
UriComponentsBuilder builder = UriComponentsBuilder
.fromUriString("https://prod-123901.westeurope.logic.azure.com:443/workflows/<workflow_id>/triggers/manual/paths/invoke")
.queryParam("api-version", "2016-10-01")
.queryParam("sp", "/triggers/manual/run")
.queryParam("sv", "1.0")
.queryParam("sig", "<your_sig>");
restTemplate.exchange(builder.build().toUri().toString(), HttpMethod.POST, request, Void.class);
update:
requestDto here is your custom dto object that goes in HTTP Body, like for example object of class:
public class RequestDto {
private String id;
private String name;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Please try with the simple standalone code with RestTemplate and check. I provide below small snippet.
try {
ResponseEntity<ResponseVO> response = restTemplate.exchange({uri of azure logic app trigger}, HttpMethod.GET, request, ResponseVO.class);
} catch (HttpStatusCodeException ex) {
int statusCode = ex.getStatusCode().value();
System.out.println("Status Code :"+statusCode);
ResponseEntity<?> resEntity = ResponseEntity.status(ex.getRawStatusCode()).headers(ex.getResponseHeaders())
.body(ex.getResponseBodyAsString())
}
Here ResponseVO.class is the Response to be mapped to an object, in this case, you can set your own class. In this catch block, you can find the exception details.
I have the method:
public HTTPResult get(String url) throws Exception{
try {
ResponseEntity<String> response = restTemplate.getForEntity(url, String.class);
return new HTTPResult(response.getBody(), response.getStatusCode().value());
}
catch (ResourceAccessException e) {
String responseBody = e.getCause().getMessage();
JSONObject obj = new JSONObject(responseBody);
return new HTTPResult(obj.getString("responseBody"), Integer.parseInt(obj.getString("statusCode")));
}
}
I want to do unit testing for it and i am not sure how to proceed:
public class MockHttpServerTest {
private static final int PORT = 51234;
private static final String baseUrl = "http://localhost:" + PORT;
private MockHttpServer server;
private SimpleHttpResponseProvider responseProvider;
private HttpClient client;
#Before
public void setUp() throws Exception {
responseProvider = new SimpleHttpResponseProvider();
server = new MockHttpServer(PORT, responseProvider);
server.start();
client = new DefaultHttpClient();
}
I am getting RED for MockHttpServer & SimpleHttpResponseProvider which should be part of org.apache.wink.client.*; which i am importing. so why do i have red ones? is there some simple way to unit test it?
HTTPResult return me response code and message.
#Configuration
public class CustomRemoteTokenService implements ResourceServerTokenServices {
private static final Logger logger = LoggerFactory.getLogger(CustomRemoteTokenService.class);
#Resource
Environment environment;
private RestOperations restTemplate;
private String checkTokenEndpointUrl;
private String clientId;
private String clientSecret;
private String tokenName = "token";
private AccessTokenConverter tokenConverter = new DefaultAccessTokenConverter();
#Autowired
public CustomRemoteTokenService() {
restTemplate = new RestTemplate();
((RestTemplate) restTemplate).setErrorHandler(new DefaultResponseErrorHandler() {
#Override
// Ignore 400
public void handleError(ClientHttpResponse response) throws IOException {
if (response.getRawStatusCode() != 400
&& response.getRawStatusCode() != 403 /* && response.getRawStatusCode() != 401 */) {
super.handleError(response);
}
}
});
}
public void setRestTemplate(RestOperations restTemplate) {
this.restTemplate = restTemplate;
}
public void setCheckTokenEndpointUrl(String checkTokenEndpointUrl) {
this.checkTokenEndpointUrl = checkTokenEndpointUrl;
}
public void setClientId(String clientId) {
this.clientId = clientId;
}
public void setClientSecret(String clientSecret) {
this.clientSecret = clientSecret;
}
public void setAccessTokenConverter(AccessTokenConverter accessTokenConverter) {
this.tokenConverter = accessTokenConverter;
}
public void setTokenName(String tokenName) {
this.tokenName = tokenName;
}
#Override
public OAuth2Authentication loadAuthentication(String accessToken)
throws AuthenticationException, InvalidTokenException, GenericException {
/*
* This code needs to be more dynamic. Every time an API is added we have to add
* its entry in the if check for now. Should be changed later.
*/
HttpServletRequest request = Context.getCurrentInstance().getRequest();
MultiValueMap<String, String> formData = new LinkedMultiValueMap<String, String>();
formData.add(tokenName, accessToken);
formData.add("api", environment.getProperty("resource.api"));
/* formData.add("api", "5b64018880999103244f1fdd");*/
HttpHeaders headers = new HttpHeaders();
headers.set("Authorization", getAuthorizationHeader(clientId, clientSecret));
Map<String, Object> map = null;
try {
map = postForMap(checkTokenEndpointUrl, formData, headers);
} catch (ResourceAccessException e) {
logger.error("Socket Exception occured at " + System.currentTimeMillis() + "for client_id : " + clientId);
GenericException ge = new GenericException(
"Could not validate your access token. If this occurs too often please contact MapmyIndia support at apisupport#mapmyindia.com");
ge.setHttpErrorCode(504);
ge.setOauthError("Access Token validation failed");
throw ge;
}
if (map.containsKey("error")) {
logger.error("check_token returned error: " + map.get("error") + " for client id : " + clientId);
String temp = map.get("error").toString();
GenericException ge = new GenericException(map.get("error_description").toString());
ge.setHttpErrorCode(Integer.parseInt(map.get("responsecode").toString()));
ge.setOauthError(temp);
switch (temp) {
case "invalid_token":
throw new InvalidTokenException(accessToken);
default:
throw ge;
}
}
Assert.state(map.containsKey("client_id"), "Client id must be present in response from auth server");
return tokenConverter.extractAuthentication(map);
}
#Override
public OAuth2AccessToken readAccessToken(String accessToken) {
throw new UnsupportedOperationException("Not supported: read access token");
}
private String getAuthorizationHeader(String clientId, String clientSecret) {
String creds = String.format("%s:%s", clientId, clientSecret);
try {
return "Basic " + new String(Base64.encode(creds.getBytes("UTF-8")));
} catch (UnsupportedEncodingException e) {
throw new IllegalStateException("Could not convert String");
}
}
private Map<String, Object> postForMap(String path, MultiValueMap<String, String> formData, HttpHeaders headers)
throws RestClientException {
if (headers.getContentType() == null) {
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
}
#SuppressWarnings("rawtypes")
Map map = restTemplate.exchange(path, HttpMethod.POST,
new HttpEntity<MultiValueMap<String, String>>(formData, headers), Map.class).getBody();
#SuppressWarnings("unchecked")
Map<String, Object> result = map;
return result;
}
}
I autowired Environment and getting null when I do environment.getProperty("resource.api");
It is always returning null but in another classes I autowire Environment and successfully retrieve the value from properties.
You have to take this steps :
1.Register a Properties
You need to register you properties file by #PropertySource("classpath:foo.properties") as :
#Configuration
#PropertySource("classpath:foo.properties")
public class CustomRemoteTokenService implements ResourceServerTokenServices {
//...
}
2.Injecting Properties
To obtain the value of a property with the Environment API:
#Autowired
private Environment env;