Calling a Spring controller from itself based on the response - rest

We have a requirement where...when we get a request to our controller we make a call to another api and based on the other api response we have to retrigger the request to controller with different parameter
#RequestMapping(value = "/search", method = RequestMethod.GET, produces = "application/json")
public Map<String, Account> searchEndpoint(#RequestParam(name = "query", required = true) String query) {
RequestPayload req = new RequestPayload(query);
SearchResponse searchResponse = http.executeSecurePost(this.searchUrl, req, SearchResponse.class);
if(("particularModel").equalsIgnoreCase(searchResponse.getModel()))
{
String differentQueryParameter = searchResponse.getName();
searchEndpoint(differentQueryParameter);
}
else {
//do something else
}
return json response;
}
when i do like this ...it's working fine where it's making the second call and getting the new response and returns ...but again a third query where this time is the original query get's fired. Not sure why the third query is fired. Any help will be appreciated.

calling your handler again and again it may result in a recursive function. you could avoid this kind of programming .you put the logic for calling the api in service method and do it .
#RequestMapping(value = "/search", method = RequestMethod.GET, produces = "application/json")public Map<String, Account> searchEndpoint(#RequestParam(name = "query", required = true) String query) {
//put this logic in service
RequestPayload req = new RequestPayload(query);
SearchResponse searchResponse = http.executeSecurePost(this.searchUrl, req, SearchResponse.class);
if(("particularModel").equalsIgnoreCase(searchResponse.getModel()))
{
String differentQueryParameter = searchResponse.getName();
//put this logic in service and just call it
RequestPayload req = new RequestPayload(query);
SearchResponse searchResponse = http.executeSecurePost(this.searchUrl, req, SearchResponse.class)
}
else {
//do something else
}
return json response;
}

Related

How to pass a value from a synchronous method to asynchronous method within an apex class?

I have made a REST call to a url and need to pass the jsonresponse obtained from the synchronous method to the below asynchronous method.I have used a static variable - strresponse to do the same.But i am getting a null value when i call this value in the asynchronous method.Could this be because asynchronous methods do not remember the value given by a synchronous method? is there any alternate way this could be done?
code below:
public with sharing class myController {
public Static String strResponse ;
#AuraEnabled
public static Map<String, Object> retriveNews(){
HttpRequest httpRequest = new HttpRequest();
httpRequest.setEndpoint('endpoint URL');
httpRequest.setMethod('GET');
try{
Http http = new Http();
HttpResponse httpResponse = http.send(httpRequest);
if(httpResponse.getStatusCode() == 200){
newsController.strResponse = httpResponse.getBody();
} else {
throw new CalloutException(httpResponse.getBody());
}
} catch(Exception ex){
throw ex;
}
Map<String, Object> newsJsonData = new Map<String, Object>();
if(!String.isBlank(newsController.strResponse)){
System.debug('jsonstring:'+newsController.strResponse);
newsJsonData = (Map<String, Object>)JSON.deserializeUntyped(newsController.strResponse);
}
System.debug('jsonstring in retrivenews:'+newsController.strResponse);
if(!newsJsonData.isEmpty()){
return newsJsonData;
} else {
return null;
}
}
#future(callout=true)
public static void insertnews()
{
List<Newsroom__c> nrmlist = new List<Newsroom__c>();
System.debug('jsonstring in insertnews:'+newsController.strResponse);
JSONParser parser = JSON.createParser(newsController.strResponse);
while (parser.nextToken() != null) {
// Start at the array of invoices.
if (parser.getCurrentToken() == JSONToken.START_ARRAY) {
while (parser.nextToken() != null) {
// Advance to the start object marker to
// find next invoice statement object.
if (parser.getCurrentToken() == JSONToken.START_OBJECT) {
objectne nrm = (objectne)parser.readValueAs(objectne.class);
objectne__c nroom = new objectne__c(Author__c = nrm.Author,Description__c = nrm.Description);
String s = JSON.serialize(nrm);
system.debug('Serialized object: ' + s);
nrmlist.add(nroom);
system.debug('list of nrooms:'+ nrmlist);
}
}
}
}
Database.SaveResult[] lsr = Database.insert(nrmlist, false) ;
System.debug(lsr);
}
Error - when newscontroller.insertnews() is called - null string to parser

I am not able to call a #Future method

I understand that I cannot directly call a future method from a batch class. But from many other answers, I can see that it is possible to do so by creating a helper class and calling the future method there. But it is not working for me. Please check my code below.
Also, I have tried to do it with the queueable class as suggested in this link, but it is not working for me. The error was " Callout not allowed from this future method. Please enable callout by annotating the future method. eg: #Future(callout=true)"
But I am more interested in the first and simpler way to do this.
public class OrdersItemsHelper {
static Document tDoc;
static blob csvBlob;
//prepare csv file to send
public static void CreateCsvFile(List<Order_Line_Items__c> orderItemsList)
{
//Code to create file here
csvBlob = Blob.valueOf(finalstr);
tDoc = new Document();
tDoc.Name = 'sales_items_' +date.today();
tDoc.Type = 'csv';
tDoc.body = csvBlob;
tDoc.FolderId = [select id from folder where name = 'Emarsys Order Files'].Id;
tDoc.ContentType = 'application/vnd.ms-excel';
Insert tDoc;
system.debug('doc inserted');
sendFile();
}
#Future(callout = true)
public static void sendFile()
{
System.debug('I am creating the post request');
Http http = new Http();
HttpRequest request = new HttpRequest();
request.setMethod('POST');
request.setHeader('Authorization','Security Token');
request.setHeader('Accept','text/plain');
request.setHeader('Content-Type', 'text/csv');
request.setHeader('Authorization', 'Bearer ');
request.setBodyAsBlob(csvBlob);
HttpResponse response = http.send(request);
system.debug('response: ' + response);
}
So I tried again by doing it in the queueable apex class. The thing I was missing was "Database.AllowsCallouts" in the class heading. Below is my queueable class which is working with the batch class to send a rest post request.
public class OrderItemFilePostHelper implements System.Queueable,Database.AllowsCallouts
{
private Blob csvBlob;
public EmarsysOrderItemFilePostHelper(Blob csvBlob) {
this.csvBlob = csvBlob;
}
public void execute(System.QueueableContext objContext)
{
System.debug('I am creating the post request');
Http http = new Http();
HttpRequest request = new HttpRequest();
request.setMethod('POST');
request.setHeader('Authorization','Security Token');
request.setHeader('Accept','text/plain');
request.setHeader('Content-Type', 'text/csv');
request.setHeader('Authorization', 'Bearer ');
request.setEndpoint('https://webhook.site/b0746268-e95c-4f94-bcb6-61d4bea54378');
request.setBodyAsBlob(csvBlob);
HttpResponse response = http.send(request);
system.debug('response: ' + response);
}
}

Best practices to handle Web API status codes

I have a web API project done with .NETCore.
My web API receives a request from another Service A, with the information I have I need to do some conversion on the data and send it to another Service B.
I am expecting that Service B send back some response: like OK or NOK. As the number of codes I can get back from Service B are so much. I would like to know which is the best practices to handle those codes?
As you will see in my code, I get the status code in this way:
var status = (int)response.StatusCode;
And the I have some if to handle this. Looking at my code it looks like a very poor status code Handling but at moment it is the best I can do. I am kindly asking suggestions to improve this.
I am using RestSharp.
Following my code:
[HttpPost]
[Produces("application/json", Type = typeof(MyModel))]
public async Task<IActionResult> Post([FromBody]MyModel myModel)
{
try
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var response = (RestResponse) await _restHelper.GetResponse("ServiceB:url", myModel);
if (response != null)
{
var status = (int)response.StatusCode;
//2xx status OK
if (status >= 200 && status < 300)
{
return Ok(response.Content);
}
//Catch all status code
return StatusCode(status, response.Content);
}
//If for some reason, I don't get any response from ServiceB
return NotFound("No response from ServiceB");
}
catch (Exception ex)
{
_logger.LogError("POST_ERROR", "ServiceB-relay/Post UNEXPECTED ERROR", ex.Message);
return StatusCode(500, "Server error, not able to process your request");
}
}
and this is my restHelper
public class RestHelper: IRestHelper
{
private readonly IConfigurationRoot _config;
public RestHelper(IConfigurationRoot config)
{
_config = config;
}
public async Task<IRestResponse> GetResponse(string configKey, object dtoObject)
{
//Get the URL from the config.json
var url = _config[configKey];
//Create rest client and rest request
var restClient = new RestClient(url);
var request = new RestRequest {Timeout = 30000, Method = Method.POST};
//Add header
request.AddHeader("Accept", "application/json");
//convert the dto object to json
var jsonObject = JsonConvert.SerializeObject(dtoObject.ToString(), Formatting.Indented);
request.AddParameter("application/json", jsonObject, ParameterType.RequestBody);
var taskCompletion = new TaskCompletionSource<IRestResponse>();
//Execute async
restClient.ExecuteAsync(request, r => taskCompletion.SetResult(r));
//await the task to finish
var response = (RestResponse) await taskCompletion.Task;
return response;
}
Thanks

Jersey client. MultivaluedMap goes empty

My RESTful client has this method:
public void testGetCateogrywiseData() {
ClientConfig config = new DefaultClientConfig();
Client client = Client.create(config);
client.addFilter(new LoggingFilter(System.out));
WebResource service = client
.resource("http://localhost:8080/MyApp/rest/publicdata");
#SuppressWarnings("rawtypes")
MultivaluedMap queryParams = new MultivaluedMapImpl();
queryParams.add("latitude", "18.522387");
queryParams.add("longitude", "73.878437");
queryParams.add("categoryID", "2");
service.queryParams(queryParams);
ClientResponse response = service.get(ClientResponse.class);
System.out.println(response.getStatus());
System.out.println("Form response " + response.getEntity(String.class));
}
On the server side the method looks like this:
#Path("publicdata")
#GET
#Produces(MediaType.TEXT_HTML)
#Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public String getPublicData() throws JSONException {
MultivaluedMap<String, String> valueMap = uriInfo.getQueryParameters();
Long latString = Long.parseLong(valueMap.getFirst("latitude"));
Long lonString = Long.parseLong(valueMap.getFirst("longitude"));
Long categoryId = Long.parseLong(valueMap.getFirst("categoryID"));
// Do necessary stuff and return json string
return null;
}
My problem is the valueMap at the server end is always empty. It never gets the three parameters that I have sent from the client code. What am I missing?
The problem happens on this line:
service.queryParams(queryParams);
It successfully adds the query params, but it does not change the original service, it returns a new one to you. To make it work you need to change to this:
service = service.queryParams(queryParams);

#ModelAttribute for Rest PUT - request param null

I need to populate my pojo class based on the request param 'type'.
so I have code like
#ModelAttribute
public void getModelObject(HttpServletRequest request, ModelMap modelMap) {
String typeCombo = request.getParameter("type");
System.out.println("typeCombo: " + typeCombo);
if (typeCombo != null) {
if (condition) {
modelMap.addAttribute("modelObj", new ClassB()); //ClassB extends ClassA
} else if (another condition) {
modelMap.addAttribute("modelObj", new ClassC()); //ClassC extends ClassA
} else {
System.out.println("no type found");
}
} else {
System.out.println("typecombo null");
}
}
I use above method to get create correct subclasses which will be used to add / update. The above one works fine in case of "POST" - for creating a record. But for "PUT" request.getParameter("type") always returns null. So for editing, I'm not able to get correct subclasses.
Below are my post and put request mapping:
#RequestMapping(value = "", method = RequestMethod.POST, headers = "Accept=*/*")
#ResponseBody
public String addCredentials(#ModelAttribute("modelObj") Credential credential,
ModelMap modelMap) {
//code
}
#RequestMapping(value = "/edit/{id}", method = RequestMethod.PUT, headers = "Accept=*/*")
#ResponseBody
public Credential editCredential(#ModelAttribute ("modelObj") Credential credential, #PathVariable long id, ModelMap model) {
//code
}
Any help is much appreciated.
Register the filter HttpPutFormContentFilter like this:
<beans:bean id="httpPutFormContentFilter"
class="org.springframework.web.filter.HttpPutFormContentFilter" />