How to pass request param using feign client? - spring-cloud-feign

I am currentlt using Feign Client to call an end point to get outlook mails. But the request parameter are not passing correctly in the api.
#FeignClient(name = "email", url = "${BASE.URI}")
public interface EmailClient {
#GetMapping("/mailfolders/Inbox/messages")
EmailRequestNew getMessages(#RequestHeader HashMap<String, Object> headers,
#RequestParam String filter);
Through service I am calling this Email client to get Mails and passing the filter as
below where from and to are datetime
String param = "$filter=receivedDateTime ge " + from + " and receivedDateTime lt " + to +
"&$expand=singleValueExtendedProperties($filter=id+eq+'String+0x0070')";
but the actual api which are calling is not accurate
assume BASE.URI is something like (10.0.0.120:8080)
https://BASE.URI/mailfolders/Inbox/messages?param=%24filter%3DreceivedDateTime%20ge%202022-11-18T05%3A32%3A56Z%20and%20receivedDateTime%20lt%202022-11-18T09%3A32%3A56Z%26%24expand%3DsingleValueExtendedProperties%28%24filter%3Did%20eq%20%27String%200x0070%27%29
but I want my complete api to be like below when I hardcoded the Request param in the GetMapping
(#GetMapping("/mailfolders/Inbox/messages$filter=receivedDateTime ge 2022-11-18T05:32:56Z and receivedDateTime lt 2022-11-18T09:32:56Z&$expand=singleValueExtendedProperties($filter=id+eq+'String+0x0070')"))
https://dev-api.bhspecialty.com/xchange/v1/mailfolders/Inbox/messages?%24filter=receivedDateTime%20ge%202022-11-18T04:16:58Z%20and%20receivedDateTime%20lt%202022-11-18T08:16:58Z&%24expand=singleValueExtendedProperties($filter=id+eq+'String+0x0070')
How can I achive this.
I tried URL Encoding/Decoding but it is not working.
Example:
URLDecoder.decode(param,"UTF-8")
UriUtils.encodePath(param, "UTF-8");
But nothing is working.

So I was able to do this by creating a RequestInterceptor and then decoding the URI and also change my EmailClient to take PathVariable instead of RequestParam.
#GetMapping(value = "/mailfolders/Inbox/messages?$expand={expand}&$filter={filter}", consumes = MediaType.APPLICATION_JSON_VALUE)
EmailRequestNew getMessages(#RequestHeader HashMap<String, Object> headers,
#PathVariable String filter, #PathVariable String expand);
#Component
public class FeignClientRequestInterceptor implements RequestInterceptor {
private static Logger logger = LogManager.getLogger(FeignClientRequestInterceptor.class);
#Override
public void apply(RequestTemplate template) {
try {
template.uri(URLDecoder.decode(template.request().url(), "UTF-8"));
logger.info("FeignClientRequestInterceptor: " + URLDecoder.decode(template.request().url(), "UTF-8") );
} catch (UnsupportedEncodingException e) {
logger.log(Level.INFO,"Error in FeignClientRequestInterceptor: " + template.request().url() );
throw new RuntimeException(e);
}
}
}
This is the final uri which is created:
https://BASE.URI/mailfolders/Inbox/messages?%24expand=singleValueExtendedProperties($filter=id%20eq%20'String%200x0070')&%24filter=receivedDateTime%20ge%202022-11-21T08:17:59Z%20and%20receivedDateTime%20lt%202022-11-21T12:17:59Z

Related

RestAssured delete method returns status code 405

RestAssured Delete method returns status code as 405 but when I try from Postman it returns 202 ( which is as expected )
In Postman :
Method : DELETE
PATH : .../rest/end1/end2?name=xyz
Code :
String name = "xyz";
String baseURI = System.getProperty("environmentPathUrl");
String path = "/rest/end1";
public void deleteName(String baseURI, String path, String name) {
String Resp = RestAssured.given().baseUri(baseURI).basePath(path).queryParam("name", name).when()
.delete("/end2").then().assertThat().statusCode(202).and().extract().response().asString();
System.out.println("Response is\t" + Resp);
}
You're making a mistake in the Rest Assured code, Add a .log().all() after given() to see the request traffic and you will be able to see your mistake
I've made few changes to the code and this should work for you hopefully
public static void deleteName() {
String name = "xyz";
String baseURI = System.getProperty("environmentPathUrl");
String path = "/rest/end1";
String Resp = RestAssured.given().log().all().baseUri(baseURI).basePath(path).queryParam("name", name).when()
.delete("/end2").then().assertThat().statusCode(202).and().extract().response().asString();
System.out.println("Response is\t" + Resp);
}
public static void main(String[] args) {
deleteName();
}

OAuthTAI Authentication failed with Status = 401, WWW-Authenticate: Bearer realm="imfAuthentication", scope="UserAuthRealm" in MFP Version 7.1

Problem:
I am using MFP 7.1 and oauth.tai_1.0.0.jar in my android application for app authenticity and have defined the realm on MFP's end. Each time I try to register to the application I see in the log
OAuthTAI Authentication failed with Status = 401, WWW-Authenticate: Bearer realm="imfAuthentication", scope="UserAuthRealm"
This is not preventing the application flow. I am only getting this error in the log and this error is seen before the realm class's init method is initialized and after that everything works fine.
I am wondering why I am getting this error.
Analysis:
I have checked the challenge handler in android, it is fine. I also did a fresh installation of the app in order to be sure of a new access token being sent from MFP.
I had also checked in MFP' Oauth jar and checked the 401 error case, it checks for invalid_token and invalid_authorization. But in my case, none of these two are there as I am not getting this in error description. I have the custom authenticator class defined which is mapped to UserAuthReal, code below:
CustomUserAuthenticator.java
package com.ibm.mfp;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Logger;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.worklight.core.auth.impl.AuthenticationContext;
import com.worklight.server.auth.api.AuthenticationResult;
import com.worklight.server.auth.api.AuthenticationStatus;
import com.worklight.server.auth.api.MissingConfigurationOptionException;
import com.worklight.server.auth.api.UserIdentity;
import com.worklight.server.auth.api.WorkLightAuthenticator;
public class CustomUserAuthenticator implements WorkLightAuthenticator {
private static final long serialVersionUID = -548850541866024092L;
private static final Logger logger = Logger.getLogger(CustomUserAuthenticator.class.getName());
private String pin;
private String userName;
private String uniqueID;
private String userNumber;
private String userAuthFlag;
private String registrationNumber;
protected Map<String, Object> authenticationData;
public void init(Map<String, String> options) throws MissingConfigurationOptionException {
logger.info("CustomUserAuthenticator initialized");
}
public AuthenticationResult processRequest(HttpServletRequest request, HttpServletResponse response,
boolean isAccessToProtectedResource) throws IOException, ServletException {
String clientID = AuthenticationContext.getCurrentClientId();
logger.info("CustomUserAuthenticator :: processRequest : clientID : " + clientID);
String requestURI = request.getRequestURI();
logger.info("CustomUserAuthenticator :: processRequest : request.getRequestURI() :" + requestURI);
String requestQueryString = request.getQueryString();
requestQueryString = null;
logger.info("CustomUserAuthenticator :: processRequest : request.getQueryString() :" + requestQueryString);
// Request the epin from the user
if (request.getRequestURI().contains("/ADIBMBA/auth/v2/auth")) {
this.pin = request.getParameter("pin");
this.userName= request.getParameter("userName");
this.uniqueID = request.getParameter("uniqueID");
this.userNumber = request.getParameter("userNumber");
this.userAuthFlag = request.getParameter("userAuthFlag");
this.registrationNumber = request.getParameter("registrationNumber");
if (null != this.customerNumber) {
logger.info(
"CustomUserAuthenticator :: processRequest : request.getRequestURI() : getParameter customerNumber : "
+ this.customerNumber);
}
if (null != pin && pin.length() > 0) {
return AuthenticationResult.createFrom(AuthenticationStatus.SUCCESS);
} else {
response.setContentType("application/json; charset=UTF-8");
response.setHeader("Cache-Control", "no-cache, must-revalidate");
response.getWriter().print("{\"authStatus\":\"required\", \"errorMessage\":\"Please enter epin\"}");
return AuthenticationResult.createFrom(AuthenticationStatus.CLIENT_INTERACTION_REQUIRED);
}
}
if (!isAccessToProtectedResource) {
return AuthenticationResult.createFrom(AuthenticationStatus.REQUEST_NOT_RECOGNIZED);
}
response.setContentType("application/json; charset=UTF-8");
response.setHeader("Cache-Control", "no-cache, must-revalidate");
response.getWriter().print("{\"authStatus\":\"required\"}");
return AuthenticationResult.createFrom(AuthenticationStatus.CLIENT_INTERACTION_REQUIRED);
}
public boolean changeResponseOnSuccess(HttpServletRequest request, HttpServletResponse response)
throws IOException {
String requestURI2 = request.getRequestURI();
logger.info("CustomUserAuthenticator :: changeResponseOnSuccess : request ");
logger.info("CustomUserAuthenticator :: changeResponseOnSuccess : response ");
// first worked partially with if
// (request.getRequestURI().contains("/ADIBMBA/auth/v2/auth")){
if (request.getRequestURI().contains("/ADIBMBA/mainapps/services/apis/App/iOSnative")
|| (request.getRequestURI().contains("/ADIBMBA/auth/v2/auth"))) {
response.setContentType("application/json; charset=UTF-8");
response.setHeader("Cache-Control", "no-cache, must-revalidate");
response.getWriter().print("{\"authStatus\":\"complete\"}");
return true;
}
return false;
}
public AuthenticationResult processAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
String errorMessage) throws IOException, ServletException {
logger.info("CustomUserAuthenticator :: processAuthenticationFailure");
response.setContentType("application/json; charset=UTF-8");
response.setHeader("Cache-Control", "no-cache, must-revalidate");
response.getWriter().print("{\"authStatus\":\"failed\", \"errorMessage\":" + errorMessage + ","
+ (String) authenticationData.get("error") + "}");
return AuthenticationResult.createFrom(AuthenticationStatus.CLIENT_INTERACTION_REQUIRED);
}
public AuthenticationResult processRequestAlreadyAuthenticated(HttpServletRequest request,
HttpServletResponse response) throws IOException, ServletException {
logger.info("CustomUserAuthenticator :: processRequestAlreadyAuthenticated");
return
AuthenticationResult.createFrom(AuthenticationStatus.
REQUEST_NOT_RECOGNIZED);
}
public Map<String, Object> getAuthenticationData() {
authenticationData = new HashMap<String, Object>();
authenticationData.put("userName", userName);
authenticationData.put("uniqueID", uniqueID);
authenticationData.put("pin", pin);
authenticationData.put("userNumber", userNumber);
authenticationData.put("userAuthFlag", userAuthFlag);
authenticationData.put("registrationNumber", registrationNumber);
return authenticationData;
}
public HttpServletRequest getRequestToProceed(HttpServletRequest request, HttpServletResponse response,
UserIdentity userIdentity) throws IOException {
return null;
}
#Override
public WorkLightAuthenticator clone() throws CloneNotSupportedException {
CustomUserAuthenticator otherAuthenticator = (CustomUserAuthenticator) super.clone();
return otherAuthenticator;
}
}
Summary:
If the application flow is normal then why i am getting this OAuthTAI 401 error in log. Suppose If it is a problem related to token & id token then i should not be able to access protected resource data. Application should not allow me to proceed further.
From the description and comments, it appears you have mixed up Liberty's OAuth TAI and MFP's OAuth security model.
MFP's OAuth security model is used to protect MFP resources ( adapters and runtime endpoints), while Liberty's OAuth TAI is to protect resources deployed on Liberty server ( eg: web applications).
The link you followed details steps using which MFP server can act as the OAuth server for resources deployed in Liberty server.
From the description and the custom Authenticator that extends WorklightAuthenticator you are depending on the security validation to be done by MFP's security framework. If the requirement is for your MFP adapters to be protected by OAuth security and the device begins by obtaining an OAuth token from MFP server, then you should use MFP's OAuth security and not resort to Liberty OAuth TAI. MFP's OAuth security framework works out of the box, without any need to configure TAI.
Refer to the following links for a better understanding and working samples:
a) MFP OAuth security model
b) Java adapters
c) Custom Authenticator

Yahoo Fantasy Sports API

Is anyone still using the Yahoo Fantasy Sports API? I had an app that worked last year, have not changed my code at all, and now it is returning a 500 internal error when I try to run it.
I used to test things through the YQL Console, but that is no longer available.
https://developer.yahoo.com/yql/
Anyone know how to make authenticated requests on that site above?
My feeling is that Yahoo has just discontinued support for their FantasySports API, I will have to look for other solutions I think.
Wondering if anyone else out there used this API previously and is or is not still having success with it.
I figured out how to use C# core and Yahoo's API. Huge thanks to this guy
Get your api key etc from yahoo.
Make a controller action that redirects to the request URL something like this:
public IActionResult Test()
{
yo.yKey = {your Yahoo API key};
yo.ySecret = {your Yahoo API secret};
yo.returnUrl = {your return URL as set in the API setup, example "https://website.com/home/apisuccess"};
var redirectUrl = "https://api.login.yahoo.com/oauth2/request_auth?client_id=" + yo.yKey + "&redirect_uri=" + yo.returnUrl + "&response_type=code&language=en-us";
return Redirect(redirectUrl);
}
This will send you to a site that authenticates with Yahoo. Upon successful authentication it is going to send you to your redirect site with a string parameter called code, in the example it would be home/apisuccess, so that controller action should look like this:
public async Task<IActionResult> ApiSuccess(string code)
{
List<string> msgs = new List<string>(); //This list just for testing
/*Exchange authorization code for Access Token by sending Post Request*/
Uri address = new Uri("https://api.login.yahoo.com/oauth2/get_token");
HttpWebRequest request = WebRequest.Create(address) as HttpWebRequest;
request.Method = "POST";
request.ContentType = "application/x-www-form-urlencoded";
byte[] headerByte = System.Text.Encoding.UTF8.GetBytes(_yKey + ":" + _ySecret);
string headerString = System.Convert.ToBase64String(headerByte);
request.Headers["Authorization"] = "Basic " + headerString;
/*Create the data we want to send*/
StringBuilder data = new StringBuilder();
data.Append("client_id=" + _yKey);
data.Append("&client_secret=" + _ySecret);
data.Append("&redirect_uri=" + _returnUrl);
data.Append("&code=" + code);
data.Append("&grant_type=authorization_code");
//Create a byte array of the data we want to send
byte[] byteData = UTF8Encoding.UTF8.GetBytes(data.ToString());
// Set the content length in the request headers
request.ContentLength = byteData.Length;
// Write data
using (Stream postStream = await request.GetRequestStreamAsync())
{
postStream.Write(byteData, 0, byteData.Length);
}
// Get response
var vM = new yOauthResponse();
string responseFromServer = "";
try
{
using (HttpWebResponse response = request.GetResponse() as HttpWebResponse)
{
msgs.Add("Into response");
// Get the response stream
StreamReader reader = new StreamReader(response.GetResponseStream());
responseFromServer = reader.ReadToEnd();
msgs.Add(responseFromServer.ToString());
vM = JsonConvert.DeserializeObject<yOauthResponse>(responseFromServer.ToString());
}
}
catch (Exception ex)
{
msgs.Add("Error Occured");
}
ViewData["Message"] = msgs;
return View(vM);
}
Note that I used a json deserializer of this model, but you can do whatever you want with the response to get the data you need out of it. This is my json model:
public class yOauthResponse
{
[JsonProperty(PropertyName = "access_token")]
public string accessToken { get; set; }
[JsonProperty(PropertyName = "xoauth_yahoo_guid")]
public string xoauthYahooGuid { get; set; }
[JsonProperty(PropertyName = "refresh_token")]
public string refreshToken { get; set; }
[JsonProperty(PropertyName = "token_type")]
public string tokenType { get; set; }
[JsonProperty(PropertyName = "expires_in")]
public string expiresIn { get; set; }
}
Once you have that data, the main thing you need is the access_token, and use it as follows in a controller action:
//simple code above removed
var client = new HttpClient()
{
BaseAddress = new Uri({your request string to make API calls})
};
client.DefaultRequestHeaders.Add("Authorization", "Bearer " + accessToken);
HttpResponseMessage response = await client.GetAsync(requestUri);
if (response.IsSuccessStatusCode)
{
//do what you will with the response....
}
//rest of simple code
Hopefully this helps someone somewhere. Happy coding!

How can I convert an Object to Json in a Rabbit reply?

I have two applications communicating with each other using rabbit.
I need to send (from app1) an object to a listener (in app2) and after some process (on listener) it answer me with another object, now I am receiving this error:
ClassNotFound
I am using this config for rabbit in both applications:
#Configuration
public class RabbitConfiguration {
public final static String EXCHANGE_NAME = "paymentExchange";
public final static String EVENT_ROUTING_KEY = "eventRoute";
public final static String PAYEMNT_ROUTING_KEY = "paymentRoute";
public final static String QUEUE_EVENT = EXCHANGE_NAME + "." + "event";
public final static String QUEUE_PAYMENT = EXCHANGE_NAME + "." + "payment";
public final static String QUEUE_CAPTURE = EXCHANGE_NAME + "." + "capture";
#Bean
public List<Declarable> ds() {
return queues(QUEUE_EVENT, QUEUE_PAYMENT);
}
#Autowired
private ConnectionFactory rabbitConnectionFactory;
#Bean
public AmqpAdmin amqpAdmin() {
return new RabbitAdmin(rabbitConnectionFactory);
}
#Bean
public DirectExchange exchange() {
return new DirectExchange(EXCHANGE_NAME);
}
#Bean
public RabbitTemplate rabbitTemplate() {
RabbitTemplate r = new RabbitTemplate(rabbitConnectionFactory);
r.setExchange(EXCHANGE_NAME);
r.setChannelTransacted(false);
r.setConnectionFactory(rabbitConnectionFactory);
r.setMessageConverter(jsonMessageConverter());
return r;
}
#Bean
public MessageConverter jsonMessageConverter() {
return new Jackson2JsonMessageConverter();
}
private List<Declarable> queues(String... nomes) {
List<Declarable> result = new ArrayList<>();
for (int i = 0; i < nomes.length; i++) {
result.add(newQueue(nomes[i]));
if (nomes[i].equals(QUEUE_EVENT))
result.add(makeBindingToQueue(nomes[i], EVENT_ROUTING_KEY));
else
result.add(makeBindingToQueue(nomes[i], PAYEMNT_ROUTING_KEY));
}
return result;
}
private static Binding makeBindingToQueue(String queueName, String route) {
return new Binding(queueName, DestinationType.QUEUE, EXCHANGE_NAME, route, null);
}
private static Queue newQueue(String nome) {
return new Queue(nome);
}
}
I send the message using this:
String response = (String) rabbitTemplate.convertSendAndReceive(RabbitConfiguration.EXCHANGE_NAME,
RabbitConfiguration.PAYEMNT_ROUTING_KEY, domainEvent);
And await for a response using a cast to the object.
This communication is between two different applications using the same rabbit server.
How can I solve this?
I expected rabbit convert the message to a json in the send operation and the same in the reply, so I've created the object to correspond to a json of reply.
Show, please, the configuration for the listener. You should be sure that ListenerContainer there is supplied with the Jackson2JsonMessageConverter as well to carry __TypeId__ header back with the reply.
Also see Spring AMQP JSON sample for some help.

401 (Unauthorized) when accessing JIRA API with query string

I'm following the tutorial found here to create a JWT token to access the REST API of JIRA. I do not have any problem accessing endpoints without passing query strings like /rest/api/2/project and /rest/api/2/issue/ISSUE-KEY but I get 401 Unauthorized when trying to pass query strings, say /rest/api/2/user/assignable/search?project=PROJECT-KEY
I'm guessing I'm missing out something, specificially the generation of canonical URL,
Here is the code that generates the get request and JWT token:
#Override
public CloseableHttpResponse get(String url) throws HttpException,
IOException, NoSuchAlgorithmException, ParseException,
JOSEException {
CloseableHttpClient client = HttpClientBuilder.create()
.setUserAgent("Kevin 6.9").build();
String token = createToken(url, JIRAClient.Method.GET);
HttpGet method = new HttpGet(jwt.getBaseUrl() + url);
method.setHeader("Authorization", "JWT " + token);
return client.execute(method);
}
/**
* Create JWT token
*
* #return
* #throws UnsupportedEncodingException
* #throws NoSuchAlgorithmException
*/
private String createToken(String apiPath, JIRAClient.Method method)
throws UnsupportedEncodingException, NoSuchAlgorithmException {
long issuedAt = System.currentTimeMillis() / 1000L;
long expiresAt = issuedAt + 1000L;
String httpMethod = method.toString();
System.out.println(httpMethod);
String contextPath = "/jira";
JwtJsonBuilder jwtBuilder = new JsonSmartJwtJsonBuilder()
.issuedAt(issuedAt).expirationTime(expiresAt)
.issuer(jwt.getKey());
HashMap<String, String[]> parameters = new HashMap<String, String[]>();
CanonicalHttpUriRequest canonical = new CanonicalHttpUriRequest(
httpMethod, apiPath, contextPath, parameters);
System.out.println("Canonical : " + canonical.getRelativePath());
JwtClaimsBuilder.appendHttpRequestClaims(jwtBuilder, canonical);
JwtWriterFactory jwtWriterFactory = new NimbusJwtWriterFactory();
String jwtbuilt = jwtBuilder.build();
String jwtToken = jwtWriterFactory.macSigningWriter(
SigningAlgorithm.HS256, jwt.getSharedSecret()).jsonToJwt(
jwtbuilt);
return jwtToken;
}
Note that I am passing an empty HashMap<String, String[]> to the CanonicalHttpUriRequest... is this correct?
Apparently the Map<String, String[]> is required to generate the appropriate canonical URI.
Note that I am passing an empty HashMap<String, String[]> to the
CanonicalHttpUriRequest... is this correct?
I modified my method signature so I can pass it as a parameter. Note: createQueryString is a method inside my class that manually creates the query String from the parameter map.
#Override
public CloseableHttpResponse get(String url,
#SuppressWarnings("rawtypes") Map parameters) throws Exception {
CloseableHttpClient client = HttpClientBuilder.create()
.setUserAgent("Kevin 5.0").build();
String token = createToken(url, JIRAClient.Method.GET, parameters);
HttpGet method = new HttpGet(jwt.getBaseUrl() + url
+ createQueryString(parameters));
method.setHeader("Authorization", "JWT " + token);
return client.execute(method);
}
And it works.
#Test
public void testJQL() throws Exception {
HashMap param = new HashMap();
param.put("jql", new String[] {"project=COR"});
param.put("startAt", new String[] {"0"});
HttpResponse response = client.get("/rest/api/2/search", param);
Assert.assertTrue(response.getStatusLine().getStatusCode() == 200);
}

Resources