Micronaut: how to set an HttpClient URL after injection - java

I have the following method:
public HttpResponse<String> sendMessage(#NonNull String url, #NonNull String message) {
try (HttpClient client = HttpClient.create(new URL(url))) { ... }
}
Basically, what I want to achieve is to create the HttpClient with a URL that comes as a parameter to the method. It cannot be a property in a config file since it depends on some external conditions.
If I check the docs for the HttpClient.create() method it says that it should not be used within a Micronaut environment, and that the client should be injected instead. However, the problem is that if it's injected I cannot initialize it with my custom URL.
Another problem is that if I keep it with the HttpClient.create() method, if I want to unit test the class, I cannot mock the HttpClient. The best option would be to inject it via constructor to be able to create the tests.
What options do I have? I haven't been able to find this type of initialization. It looks like everyone uses a fix URL? 😅
Thanks!

I'll answer it myself. Apparently, you can specify in the request passed to the HttpClient methods (retrieve, exchange, etc.) both the relative and full URLs. So it doesn't matter what you put when you inject, instantiate the client.

Related

How to handle real API URL via mock server?

I have a real API (https://profiles.production.service/api/person). And want to mock it using MockServer.
I'm using JUnit 5 in my integration test:
#Rule
val mockServer = MockServerContainer(DockerImageName.parse("jamesdbloom/mockserver:mockserver-5.11.2"))
mockServer.start()
....
MockServerClient("profiles.production.service", mockServer.serverPort)
.`when`(
request()
.withPath("/api/person")
.withQueryStringParameter("name", "peter")
)
.respond(
response()
.withBody("Peter the person!")
)
But actually got error:
org.mockserver.client.SocketConnectionException: Unable to resolve host profiles.production.service/<unresolved>:55070
How can I fix it?
This answer arrives too late for sure. But I am writing, just in case others come here and have a similar problem.
Looking at the documentation of the mockserver you can see an example that would make it work. Your code would need to change to:
MockServerClient(mockServer.getHost(), mockServer.getServerPort())
MockServerContainer is a docker image running on its own. So it will have a different URL than the server you need to mock.
If the problem you have is that you have hardcoded the server name on your class, then you would need to change that class. You will need to inject the server name. In your case
"https://%s:%d/api/person".format(mockServer.getHost(), mockServer.getServerPort())

Java: How do I unit test where there are encapsulated injected or generated dependencies?

SUMMARY:
I have a self-contained class for performing a proxied login and then filling a HttpServletResponse object with authentication content a browser can use. When testing my code, how can I provide mocked services into a class that has no setters?
DETAILS:
I've severely edited my proxied login code into this snippet.
It asks the server for a login form.
It sends back the credentials.
It gets the server's approval and passes it to the browser
(response).
The trimmed code looks like this:
private static final Log log = LogFactory.getLog(MyClass.class);
#Inject()
private UserService userService;
public void performProxyLogin(HttpServletResponse response,
UserDTO userDTO, String url) {
String username = getUsername(userDTO);
String password = getPasswordFromUserService(username);
// MyRequest only has data, organizing a Http Request.
MyRequest myRequest = prepareInitialGetRequest(url);
// processURLRequest() encapsulates use of HttpURLConnection.
// MyResponse only has data, organizing a Http Response.
MyResponse myResponse = processURLRequest(myRequest);
myRequest = prepareLoginRequest(myResponse, username, password);
myResponse = processURLRequest(myRequest);
// Transfer data into the response, and from there into the browser.
fillResponseWithProxiedResult(response, myResponse)
}
To make this work I think I need to inject a mocked Log or LogFactory, a mocked UserService, and a way of getting a mocked HttpURLConnection.
However, all of the advice I've seen involves code with setters, which the test suite can use to plug in mocked objects.
How do I provide my class its needed mocked objects?
Bite the bullet and provide a package-private setter for this field.
If you want to use mocks, there's no value in letting the injection framework set up a mock which you can inject, since you're adding more ceremony and overhead to the set-up of your test.
If you want to validate that you have a service injecting correctly, you wouldn't want to use mocks at all (think "integration test" with real or pseudo-real components).
Many of the other answers hint at it, but I'm going to more explicitly say that yes, naive implementations of dependency injection can break encapsulation.
The key to avoiding this is that calling code should not directly instantiate the dependencies (if it doesn't care about them). This can be done in a number of ways.
The simplest is simply have a default constructor that does the injecting with default values. As long as calling code is only using the default constructor you can change the dependencies behind the scenes without affecting calling code.
This can start to get out of hand if your dependencies themselves have dependencies and so forth. At that point the Factory pattern could come into place (or you can use it from the get-go so that calling code is already using the factory). If you introduce the factory and don't want to break existing users of your code, you could always just call into the factory from your default constructor.
Beyond that there's using Inversion of Control. I haven't used IoC enough to speak too much about it, but there's plenty of questions here on it as well as articles online that explain it much better than I could.
If it should be truly encapsulated to where calling code cannot know about the dependencies then there's the option of either making the injecting (either the constructor with the dependency parameters or the setters) internal if the language supports it, or making them private and have your unit tests use something like Reflection if your language supports it. If you language supports neither then I suppose a possibility might be to have the class that calling code is instantiating a dummy class that just encapsulates the class the does the real work (I believe this is the Facade pattern, but I never remember the names correctly)]

How to use Basic Authentication with Spring's Resource abstraction

I have a file on a server available via Https I want to access using Spring's Resource abstraction. I want Spring to resolve the resource and inject it into the constructor of my Bean like this:
public class MyClass {
public MyClass(
#Value("https://myserver.com/myfile") Resource resource) {
// do something using the resource
}
}
The issue is that I cannot figure out how to include the username and password for basic authentication into this pattern. I tried the "common" style
#Value("https://username:password#myserver.com/myfile")
but it looks like this is not understood correctly. The server is responding with HTTP status 401 - Unauthorized. I copied the string and perfomed the same query using wget and it worked. So there is no issue with my credentials but most likely with the syntax used to define the resource.
Is there a valid syntax for this in Spring or must I fetch the config in an alternative way setting the Authentication header by hand?
This feels wrong, and I'd prefer it if you didn't do it this way...but you can rely on #Value to inject the property value. Note the use of #Autowired here.
#Component
public class MyClass {
private String resourceUrl;
#Autowired
public MyClass(#Value(${external.resource.url}) String resourceUrl) {
this.resourceUrl = resourceUrl;
}
// The rest of your code
}
Then you could place into the property external.resource.url whichever value you liked...including your full URL with username and password.
I want to call attention that this is probably not a desirable thing to do, since you want to be able to inject the URL, username and password as separate things into your application. This gives you an idea of how you can accomplish it with one component, and while I strongly encourage you to split this up instead (and whatever you do, do not check the properties file in with those values into your source control), I leave the mechanical part of splitting this into more values as an exercise for the reader.

Find out which endpoint sent a REST call

I'm trying to create a visualisation of REST calls among several internal and external services/servers. I'd like to know which endpoint called which other endpoint. I figured that the only way to do this is to do this on the caller side, because the receiver does not have any information about the caller endpoint.
Here's my thinking:
I create an object like RestTemplate and call the method.
I create an Interceptor or something like that, which will extract the information from the RestTemplate.
My problem is that I'm not sure how to find out which REST endpoint called the RestTemplate method. The RestTemplate (or other similar object) call could be called in nested methods, so for example the endpoint could invoke a private method, which then calls the external service itself.
Is there any way how to get this information? Or am I maybe just thinking too hard and there is an easier way to do this?
Example:
#GetMapping("/hello")
public String hello() {
methodThatCallsOtherEndpoint("something.com/weather"); // this method inside itself calls an endpoint
logRestCall("localhost:8000/hello", "something.com/weather"); // how do I do this automatically without having to type it myself?
return "hello";
}
Thanks for any help.
If these services/servers have a static IP you can possibly, tag them by their IP address?
You can use Spring Sleuth to trace the relationship between different REST calls.

How do I unit test code which calls the Jersey Client API?

I wrote code which calls the Jersey client API which in turn calls a web service which is out of my control. I do not want my unit test to call the actual web service.
What is the best approach for writing a unit test for code which calls the Jersey client API? Should I use the Jersey server API to write a JAX-RS web service and then use the Jersey Test Framework for the unit test? Or should I mock out the Jersey web service calls? I have access to JMock. Or should I try another approach?
During my research, I found this discussion describing various options, but I did find a complete solution. Are there any code examples available showing a suggested JUnit approach? I could not find any in the Jersey documentation.
Here is the relevant source code:
public String getResult(URI uri) throws Exception {
// error handling code removed for clarity
ClientConfig clientConfig = new DefaultClientConfig();
Client client = Client.create(clientConfig);
WebResource service = client.resource(uri);
String result = service.accept(accept).get(String.class);
return result;
}
Here are examples of test code I would like to pass. I would like to test (1) passing in a valid URI and getting a valid string back and (2) passing in an invalid (for whatever reason -- unreachable or unauthorized) URI and getting an exception back.
#Test
public void testGetResult_ValidUri() throws Exception {
String xml = retriever.getResult(VALID_URI);
Assert.assertFalse(StringUtils.isBlank(xml));
}
#Test(expected = IllegalArgumentException.class)
public void testGetResult_InvalidUri() throws Exception {
retriever.getResult(INVALID_URI);
}
Everything above is the simple description of what my code does. In reality, there is a layer on top of that that accepts two URIs, first tries calling the first URI, and if that URI fails then it tries calling the second URI. I would like to have unit tests covering (1) the first URI succeeds, (2) the first URI fails and the second URI succeeds, and (3) both URIs fail. This code is sufficiently complex that I want to test these different scenarios using JUnit, but to do this I either need to run actual stand-in web services or mock out the Jersey client API calls.
Try to use Mockito or Easymock for mocking service calls. You need to mock only these methods which are actually used - no need to mock every method. You can creat mock object for WebResource class, then mock accept method call.
In #BeforeClass/#Before JUnit test method write something like (Mockito example)
WebResource res = mock(WebResource.class);
when(res.accept(something)).thenReturn(thatWhatYouWant);
Then in your tests you can use res object as if it was real object and call mock method on it. Instead of returning value you can also throw exceptions. Mockito is pretty cool.
Typically what you are really after is "does the way I use the Jersey Client DSL produce a request to the correct URL with the correct payload and URL parameters". Testing this with Mockito is really verbose and the setup code will usually end up looking something like this:
when(authentication.queryParam(eq("sa"), anyBoolean())).thenReturn(testAuthentication);
when(testAuthentication.resolveTemplate("channel", "smf")).thenReturn(testAuthentication);
when(testAuthentication.request(
MediaType.APPLICATION_JSON_TYPE)).thenReturn(mockRequestBuilder);
when(mockRequestBuilder.post(any(Entity.class))).thenReturn(mockResponse);
when(mockResponse.readEntity(ResponseWrapper.class)).thenReturn(successfulAuthResponse());
And this is basically just for a single REST request. It's overly verbose, and instead of testing the hoped outcome you are just replicating the steps you think are correct in using the Jersey Client DSL.
Instead of the above, I would aim for mocking a simple service. For this I've used WireMock which starts a Jetty server and where I can stub things like "expect a request to this URL, respond with this message and verify that the payload is this".
I know this is edging on an integration test and it is a bit slower than just using Mockito but I value testing the real outcome and I value the readability of the tests way more in this case.
Setup for a WireMock based Jersey Client test looks something like this:
#Test
public void exactUrlOnly() {
stubFor(get(urlEqualTo("/some/thing"))
.willReturn(aResponse()
.withHeader("Content-Type", "text/plain")
.withBody("Hello world!")));
assertThat(testClient.get("/some/thing").statusCode(), is(200));
assertThat(testClient.get("/some/thing/else").statusCode(), is(404));
}
Just implement a work-alike service and in your unit test setup start the service using HttpServerFactory.

Categories

Resources