Truecaller web login : Unable to process JSON - Error - truesdk

Unirest.setTimeouts(7000, 7000);
HttpResponse<JsonNode> accessToken = Unirest.post("https://api4.truecaller.com/v1/apps/requests")
.header("Content-Type", "application/json")
.header("Accept", "application/json")
.header("appKey", "app-key-here")
.header("Cache-Control", "no-cache")
.field("phoneNumber", "46760123456")
.asJson();
I am using TrueCaller Web-login API (https://github.com/truecaller/web-login),
The above mentioned is my code (Java-Unirest), please help me to solve this problem (Unable to call API)
Response from truecaller server :
Error: 400
Message: Unable to process JSON.

http://unirest.io/java.html
You have to use body method as per the documentation.
Example
Unirest.setTimeouts(7000, 7000);
HttpResponse<JsonNode> accessToken = Unirest.post("https://api4.truecaller.com/v1/apps/requests")
.header("Content-Type", "application/json")
.header("Accept", "application/json")
.header("appKey", "app-key-here")
.header("Cache-Control", "no-cache")
.body("{\"phoneNumber\":7777777777}")
.asJson();
OR
Unirest.setObjectMapper(new ObjectMapper() {
private com.fasterxml.jackson.databind.ObjectMapper jacksonObjectMapper = new com.fasterxml.jackson.databind.ObjectMapper();
public <T> T readValue(String value, Class<T> valueType) {
try {
return jacksonObjectMapper.readValue(value, valueType);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public String writeValue(Object value) {
try {
return jacksonObjectMapper.writeValueAsString(value);
} catch (com.fasterxml.jackson.core.JsonProcessingException e) {
throw new RuntimeException(e);
}
}
});
class Test implements Serializable {
final public Long phoneNumber;
public Test(Long phoneNumber) {
this.phoneNumber = phoneNumber;
}
}
Test t = new Test(7777777777L);
Unirest.setTimeouts(7000, 7000);
HttpResponse<JsonNode> accessToken = Unirest.post("https://api4.truecaller.com/v1/apps/requests")
.header("Content-Type", "application/json")
.header("Accept", "application/json")
.header("appKey", "app-key-here")
.header("Cache-Control", "no-cache")
.body(t)
.asJson();

Related

How to use #ControllerAdvice to handle webclient errors from the reactive stack (web flux -> spring)

I use webclient from weblux to send a request to a remote server. At this point, I can get error 400. I need to intercept it and send it to the client.
webClient
.post()
.uri(
)
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
.body(
BodyInserters
.fromFormData()
.with()
.with()
)
.retrieve()
.onStatus(
HttpStatus::isError, response -> response.bodyToMono(String.class) // error body as String or other class
.flatMap(error -> Mono.error(new WrongCredentialsException(error)))
)
.bodyToMono(TResponse.class)
.doOnNext(...);
error
#ControllerAdvice
#Slf4j
public class ApplicationErrorHandler {
#ExceptionHandler(WrongCredentialsException.class)
public ResponseEntity<ErrorResponse> handleResponseException(WrongCredentialsException ex) {
// log.error("Error from WebClient - Status {}, Body {}", ex.getRawStatusCode(), ex.getResponseBodyAsString(), ex);
ErrorResponse error = new ErrorResponse();
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(error);
}
#Data
#NoArgsConstructor
#AllArgsConstructor
public class ErrorResponse {
private String errorCode;
private String message;
}
rest api
#PostMapping
public ResponseEntity<String> send(#RequestBody Dto dto) {
log.debug("An notification has been send to user");
return new ResponseEntity<>(HttpStatus.OK);
}
I tried the options from here, but it didn't work out . Can someone explain how it works and how it can be configured for my case?
first case
return Objects.requireNonNull(oauthWebClient
.post()
.uri(uri)
.bodyValue(dto)
.attributes(oauth2AuthorizedClient(authorizedClient))
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.exchangeToMono(response -> {
HttpStatus httpStatus = response.statusCode();
if (httpStatus.is4xxClientError()) {
getErrFromClient(response, httpStatus);
}
if (httpStatus.is5xxServerError()) {
getErrFromServer(response, httpStatus);
}
return Mono.just(ResponseEntity.status(response.statusCode()));
})
.block())
.build();
}
private void getErrFromServer(DtoResponse response, HttpStatus httpStatus) {
String err = response.bodyToMono(String.class).toString();
log.error("HttpStatus: {}, message: {}", httpStatus, err);
HttpHeaders httpHeaders = response.headers().asHttpHeaders();
List<String> errorBody = httpHeaders.get("errBody");
assert errBody != null;
throw new CustomException(
"{ HttpStatus : " + httpStatus + " , message : " + errBody + " }");
}
private void getErrFromClient(DtoResponse response, HttpStatus httpStatus) {
String err = response.bodyToMono(String.class).toString();
log.error("HttpStatus: {}, err: {}", httpStatus, err);
HttpHeaders httpHeaders = response.headers().asHttpHeaders();
List<String> errorBody = httpHeaders.get("errBody");
assert errBody != null;
throw new CustomException(
"{ HttpStatus : " + httpStatus + " , message : " + errBody + " }");
}
and than
#ControllerAdvice
public class HandlerAdviceException {
#ExceptionHandler(CustomException.class)
public ResponseEntity<ErrorResponse> handleCustomException(CustomException e) {
//here your code
//for example:
String errMessage = e.getLocalizedMessage();
return ResponseEntity
.internalServerError()
.body(new ErrorResponse(ErrorCode.INTERNAL_ERROR, errMessage));
}
}
second case
return webClient
.post()
.uri(
properties......,
Map.of("your-key", properties.get...())
)
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
.body(
prepare....()
)
.retrieve()
.bodyToMono(TokenResponse.class)
.doOnSuccess(currentToken::set);
}
Here, if successful, you will get the result you need, but if an error occurs, then you only need to configure the interceptor in the Advice Controller for WebClientResponseException.
#ControllerAdvice
#Slf4j
public class CommonRestExceptionHandler extends ResponseEntityExceptionHandler {
#ExceptionHandler(WebClientResponseException.class)
protected ResponseEntity<ApiErrorResponse> handleWebClientResponseException(WebClientResponseException ex) {
log.error(ex.getClass().getCanonicalName());
String errMessageAdditional = .....
final ApiErrorResponse apiError = ApiErrorResponse.builder()
.message(ex.getLocalizedMessage())
.status(HttpStatus.UNAUTHORIZED)
.build();
//if it needs
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.add(.......);
return new ResponseEntity<>(apiError, httpHeaders, apiError.getStatus());
}
}

WebClient ExchangeFilterFunction JUnit

How do I get code coverage for ClientResponseErrorService.genericClientFilter?
#PostConstruct
public void init() throws SSLException {
SslContext sslContext = SslContextBuilder.forClient().trustManager(InsecureTrustManagerFactory.INSTANCE)
.build();
HttpClient httpClient = HttpClient.create().secure(sslContextSpec -> sslContextSpec.sslContext(sslContext));
ClientHttpConnector restConnector = new ReactorClientHttpConnector(httpClient);
webClient = WebClient.builder()
.baseUrl(basePath)
.defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.defaultHeader(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE)
.filter(ClientResponseErrorService.genericClientFilter)
.clientConnector(restConnector).build();
}
Here is the ClientResponseErrorService class which I am using inside filter function.
public static ExchangeFilterFunction genericClientFilterFunction() {
return ExchangeFilterFunction.ofResponseProcessor(clientResponse -> {
if (clientResponse.statusCode().isError()) {
clientResponse.bodyToMono(String.class).flatMap(clientResponseBody -> {
try {
log.info("Into error Handler");
return Mono.error(getException(clientResponse, clientResponseBody));
} catch (Exception ex) {
return Mono.error(getException(clientResponse, clientResponseBody));
}
});
return Mono.just(clientResponse);
} else {
return Mono.just(clientResponse);
}
});
}
I tried this way by creating a MockWebServer
#Test
public void exchangeFilterFunctionTest() throws Exception {
server.enqueue(new MockResponse().addHeader("Content-Type", "application/json; charset=utf-8")
.setBody("{}").throttleBody(10000, 3, TimeUnit.MILLISECONDS).setResponseCode(400));
server.enqueue(new MockResponse().addHeader("Content-Type", "application/json; charset=utf-8")
.setBody("{}").throttleBody(10000, 3, TimeUnit.MILLISECONDS).setResponseCode(401));
WebClient webClient = WebClient.builder()
.baseUrl("http://localhost:"+server.getPort())
.filter(ClientResponseErrorService.genericClientFilterFunction())
.build();
Mono<String> responseMono = webClient.get()
.uri("http://localhost:"+server.getPort()+"/api")
.retrieve()
.bodyToMono(String.class).retry(1);
StepVerifier.create(responseMono).expectNextCount(1).verifyComplete();
}
but it is giving exception
expectation "expectNextCount(1)" failed (expected: count = 1; actual: counted = 0; signal: onError(org.springframework.web.reactive.function.client.WebClientResponseException$NotFound: 404 Not Found from GET http://localhost:55633/api))
java.lang.AssertionError: expectation "expectNextCount(1)" failed (expected: count = 1; actual: counted = 0; signal: onError(org.springframework.web.reactive.function.client.WebClientResponseException$NotFound: 404 Not Found from GET http://localhost:55633/api))
It is going inside ClientResponseErrorService class but failing at this line clientResponse.bodyToMono(String.class).flatMap(clientResponseBody -> {
Here's an example unit test that tests the scenario when the status code is not an error.
#Test
void doNothing_whenStatus200() {
ClientRequest clientRequest = Mockito.mock(ClientRequest.class);
ExchangeFunction exchangeFunction = Mockito.mock(ExchangeFunction.class);
ClientResponse clientResponse = mock(ClientResponse.class);
given(exchangeFunction.exchange(clientRequest)).willReturn(Mono.just(clientResponse));
given(clientResponse.statusCode()).willReturn(HttpStatus.OK);
ExchangeFilterFunction underTest = ClientResponseErrorService.genericClientFilterFunction();
StepVerifier.create(underTest.filter(clientRequest, exchangeFunction))
.expectNext(clientResponse)
.verifyComplete();
}

How to redirect ftl in spring boot?

I have already created an e mail confirmation in spring boot it is working nicely, and also I created a link when a user click it should say "confirmed"but I did not figure it out how to do that?
E mail sender java class:
#Component
public class EmailSender {
#Autowired
JavaMailSender javaEmailSender;
public void sendEmail(String to, String subject, String text) throws MessagingException {
MimeMessage message = javaEmailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(message,
MimeMessageHelper.MULTIPART_MODE_MIXED_RELATED,
StandardCharsets.UTF_8.name());
helper.setTo(to);
helper.setSubject(subject);
helper.setText(text, true);
helper.addInline("logo.jpg", new ClassPathResource("./images/logo.jpg"));
javaEmailSender.send(message);
}
}
E mail template Loader:
#Component
public class EmailTemplateLoader {
#Autowired
private Configuration freemakerConfiguration;
public String getEmailBody(String name, String confirmationLink) throws TemplateException {
try {
Template template = freemakerConfiguration.getTemplate("EmailConfirmation.ftl");
Map<String, Object> data = new HashMap<String, Object>();
data.put("name", name);
data.put("confirmationLink", confirmationLink);
return FreeMarkerTemplateUtils.processTemplateIntoString(template, data);
} catch (IOException e) {
e.printStackTrace();
} catch (TemplateException e) {
e.printStackTrace();
}
return "";
}
}
My signup Resource :
#Autowired
private SignupService signupService;
#Autowired
private HttpServletRequest httpServletRequest;
#RequestMapping(value = "user/signup", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
#Transactional(rollbackFor = Exception.class)
public ResponseEntity<?> signup(#RequestBody UserType user) throws SignUpException {
URL requestUrl = null;
try {
requestUrl = new URL(httpServletRequest.getRequestURL().toString());
} catch (MalformedURLException e) {
logger.debug("Malformed Request Url");
}
logger.debug(requestUrl.toString());
signupService.signUp(user, requestUrl.getHost());
return new ResponseEntity<>(HttpStatus.OK);
}
#RequestMapping(value = "user/confirmation", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
#Transactional(rollbackFor = Exception.class)
public ResponseEntity<?> confirmSignUp(#RequestParam("u") String loginName, #RequestParam("p") String token) {
try {
signupService.emailConfirmation(loginName, token);
return new ResponseEntity<>(HttpStatus.OK);
} catch (SignUpException e) {
return new ResponseEntity<>(e.getMessage(), HttpStatus.BAD_REQUEST);
}
}
So, As I told I can send an email successfully, but I could not sort how I can write confirmation successfully

Getting TestRestTemplate to work with https

Writing JUnit Integrtaion tests for a REST endpoint which sets secure cookies, can't get past the ResourceAccessException error.
Requirement is to do a https://localhost:8443 request.
Have tried using the customRestTemplate
Getting the folloiwng exception.
org.springframework.web.client.ResourceAccessException: I/O error on GET request for "https://localhost:8443/dcs": Connect to localhost:8443 [localhost/127.0.0.1, localhost/0:0:0:0:0:0:0:1] failed: Connection refused: connect; nested exception is org.apache.http.conn.HttpHostConnectException
Below is the code.
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public class DcsServiceTests {
#Autowired
RestTemplateBuilder restTemplateBuilder;
#Autowired
private TestRestTemplate testRestTemplate;
#Test
public void testGet_ImageResponse() throws Exception {
//Arrange
//Act
ResponseEntity<byte[]> response = testRestTemplate.getForEntity(url, byte[].class);
//Assert
//Response Status
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
//Response has cookie
assertThat(response.getHeaders().containsKey("Set-Cookie")).isTrue();
}
#PostConstruct
public void initialize() {
// Lambda expression not working, TBD - Java version used.
//TrustStrategy acceptingTrustStrategy = (X509Certificate[] chain, String authType) -> true;
final TrustStrategy acceptingTrustStrategy = new TrustStrategy() {
#Override
public boolean isTrusted(java.security.cert.X509Certificate[] arg0, String arg1)
throws CertificateException {
return true;
}
};
HttpComponentsClientHttpRequestFactory requestFactory =
new HttpComponentsClientHttpRequestFactory();
try {
SSLContext sslContext = org.apache.http.ssl.SSLContexts.custom()
.loadTrustMaterial(null, acceptingTrustStrategy)
.build();
SSLConnectionSocketFactory csf = new SSLConnectionSocketFactory(sslContext);
CloseableHttpClient httpClient = HttpClients.custom()
.setSSLSocketFactory(csf)
.build();
requestFactory.setHttpClient(httpClient);
}
catch (Exception e) {
System.out.println("Exception occured creating Request Factory");
}
RestTemplate customTemplate = restTemplateBuilder
.requestFactory(requestFactory)
.rootUri("https://localhost:8443")
.build();
this.testRestTemplate = new TestRestTemplate(
customTemplate,
null,
null, // Not using basic auth
TestRestTemplate.HttpClientOption.ENABLE_COOKIES); // Cookie support
}
}
Disabling SSL and then using testRestTemplate with exchange method worked. Secured cookies works as well, just that the headers needs to be parsed to validate results in Unit test cases
#Bean
public Boolean disableSSLValidation() throws Exception {
final SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, new TrustManager[] { new X509TrustManager() {
#Override
public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
}
#Override
public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
}
#Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
} }, null);
HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());
HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {
public boolean verify(String hostname, SSLSession session) {
return true;
}
});
return true;
}
public void hostNameVerifier() {
final HostnameVerifier defaultHostnameVerifier = javax.net.ssl.HttpsURLConnection.getDefaultHostnameVerifier ();
final HostnameVerifier localhostAcceptedHostnameVerifier = new javax.net.ssl.HostnameVerifier () {
public boolean verify ( String hostname, javax.net.ssl.SSLSession sslSession ) {
if ( hostname.equals ( "localhost" ) ) {
return true;
}
return defaultHostnameVerifier.verify ( hostname, sslSession );
}
};
javax.net.ssl.HttpsURLConnection.setDefaultHostnameVerifier ( localhostAcceptedHostnameVerifier );
}
#Test
public void testGet_ImageResponse() throws Exception {
//Arrange
String url = getUrl() + "/xyz?s_action=test&s_type=i";
//Act
ResponseEntity<byte[]> response = restTemplate.getForEntity(url, byte[].class);
//Assert
//Response Status
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
//Response has cookie
assertThat(response.getHeaders().containsKey("Set-Cookie")).isTrue();
//Extract cookie from header
List<String> cookies = response.getHeaders().get("Set-Cookie");
//Construct cookie from RAW Header Response
Cookie cookie = RawCookieParser.constructCookieFromHeaderResponse(response.getHeaders().get("Set-Cookie").toString());
//Cookies name matches
//Cookie value cannot be matched because value is being set from external JAR
assertEquals(cookie.getName(), appConfig.getName());
//Cookie domain matches
assertEquals(cookie.getDomain(), appConfig.getDomain());
}
public class RawCookieParser {
/*
* Construct a cookie object by parsing the HTTP Header response
*/
public static Cookie constructCookieFromHeaderResponse(String input) throws Exception {
String rawCookie = input.replace("[", "").replace("]", "");
String[] rawCookieParams = rawCookie.split(";");
String[] rawCookieNameAndValue = rawCookieParams[0].split("=");
if (rawCookieNameAndValue.length != 2) {
throw new Exception("Invalid cookie: missing name and value.");
}
String cookieName = rawCookieNameAndValue[0].trim();
String cookieValue = rawCookieNameAndValue[1].trim();
Cookie cookie = new Cookie(cookieName, cookieValue);
for (int i = 1; i < rawCookieParams.length; i++) {
String rawCookieParamNameAndValue[] = rawCookieParams[i].trim().split("=");
String paramName = rawCookieParamNameAndValue[0].trim();
if (rawCookieParamNameAndValue.length == 2) {
String paramValue = rawCookieParamNameAndValue[1].trim();
if (paramName.equalsIgnoreCase("secure")) {
cookie.setSecure(true);
} else if (paramName.equalsIgnoreCase("max-age")) {
int maxAge = Integer.parseInt(paramValue);
cookie.setMaxAge(maxAge);
} else if (paramName.equalsIgnoreCase("domain")) {
cookie.setDomain(paramValue);
} else if (paramName.equalsIgnoreCase("path")) {
cookie.setPath(paramValue);
}
}
}
return cookie;
}
}

Spring MVC - calling methods in #ResponseBody

I am Spring MVC beginner and I want to call rest in #ResponseBody. My external node server doesn't react on that method. I don't got message about request in my server console. Without UserRest it works. I would be grateful for your help
#Controller
public class AjaxController {
#RequestMapping(value= "user", method=RequestMethod.GET)
public #ResponseBody String login (){
UserRest ur = new UserRest();
Response r = ur.getUserName(2);
Gson gs = new Gson();
String str = gs.toJson(r);
return str;
}
}
Response getUserName(int userID){
Response response = new Response();
StringBuilder total = new StringBuilder();
try {
URL url = new URL(Properties.SERVER_SECURE_URL + "users/" + userID);
urlConnection = (HttpsURLConnection) url.openConnection();
urlConnection.setDoOutput(false);
urlConnection.setDoInput(true);
urlConnection.setRequestMethod("GET");
urlConnection.setRequestProperty("Authorization","1Strajk");
response.setMessageCode(urlConnection.getResponseCode());
if(response.getMessageCode()==Response.MESSAGE_OK) {
InputStream in = urlConnection.getInputStream();
BufferedReader r = new BufferedReader(new InputStreamReader(in));
String line;
while ((line = r.readLine()) != null) {
total.append(line);
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if(!total.toString().isEmpty()){
response.setObject(total.toString());
}
urlConnection.disconnect();
}
return response;
}
I resolve it. I forgot about SSL connection. Before calling rest I called that method:
public class SSLUtils {
public static void trustEveryone() {
try {
HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {
public boolean verify(String hostname, SSLSession session) {
return true;
}
});
SSLContext context = SSLContext.getInstance("TLS");
context.init(null, new X509TrustManager[]{new X509TrustManager(){
public void checkClientTrusted(X509Certificate[] chain,
String authType) throws CertificateException {}
public void checkServerTrusted(X509Certificate[] chain,
String authType) throws CertificateException {}
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}}}, new SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(
context.getSocketFactory());
} catch (Exception e) { // should never happen
e.printStackTrace();
}
}
}

Resources