How to change the request host? (Make "http://localhost:9998" be something else)
Closed this issue · 15 comments
I'm generating this snippet:
[source,bash]
----
$ curl 'http://localhost:9998/api/getSomething' -i
----
But I would like host to be my actual API. I found this question explaining how to do that when using Spring Restdocs, but I wasn't able to apply that to RESTDocsEXT. How can I change my test so that the resulting snippet says http://myurl.com/
instead of http://localhost:9998/
?
@stiancognite Hey thanks for your interest in this project. Honestly though, I haven't really been maintaining the project, as I don't use it myself anymore. But for your specific problem, just off the top of my head, one way I can think of is to just use the in-memory provider from Jersey instead of the grizzly provider (this is actually what I was going when I was using it). Using the in-memory provider, you can actually make the request with any host and and the request should still go through. The limitation of this provider is that you will not be able to use an Servlet functionality, for instance if you are trying to inject HttpServletRequest
anywhere in your app. Other than that, it should work just fine.
There may be another way, but I don't really have the time right now to get reacquainted with the project
Thank you for the quick reply! Could I ask what you use instead? If you still use Jersey and have the need for restdocs.
Just to be clear (because I don't know Jersey very well yet): I still want the test to call localhost:9998, but I want to change the host in the snippet so that it looks like that call was to our actual API. When running a test I see that I get a testContainer
that is an instance of JettyTestContainer
. (This has a baseUri
which is http://localhost:9998
.) The client that I'm using is a JerseyClient
(that's what's set as the delegate in RestdocsClient
). So when you say grizzly provider, does that refer to the container that I'm using in the test? Or the http client? (Please excuse my ignorance. :) From what I can understand so far about the subject, we have to use the JettyTestContainer.
It looks to me as if changing JerseyRequestConverter
so that the public OperationRequest convert(ClientRequest clientRequest)
method also takes a overrideUri
parameter, I would get what I need. Changing the uri value there manually while debugging in IntelliJ got me what I wanted - a snippet with a custom url. Or by having a way of setting a custom JerseyRequestConverter
in the JerseyRestDocumentation
class. What would you prefer a PR to do?
I have it working as I want here by changing JerseyRequestConverter
like this:
public class JerseyRequestConverter implements RequestConverter<ClientRequest> {
private String overrideHost;
public JerseyRequestConverter(String overrideHost) {
this.overrideHost = overrideHost;
}
@Override
public OperationRequest convert(ClientRequest request) {
return new OperationRequestFactory().create(
UriBuilder.fromUri(request.getUri()).host(this.overrideHost).port(-1).build(),
HttpMethod.valueOf(request.getMethod()),
extractContent(request), extractHeaders(request.getHeaders()),
extractParameters(request), extractParts(request)
);
}
...
To use it I had to create local copies of JerseyRestDocumentationFilter
(because the constructor wasn't public), JerseyRequestConverter
(because of the change), JerseyResponseConverter
(because it wasn't public). I then use it like this:
JerseyRestDocumentationFilter jerseyRestDocumentationFilter = new JerseyRestDocumentationFilter(
new RestDocumentationGenerator<>(
"getSomething",
new JerseyRequestConverter("my.host.com"),
new JerseyResponseConverter(),
preprocessRequest(
removeHeaders("User-Agent")
),
preprocessResponse(
removeHeaders(
"Server",
"Transfer-Encoding",
"Date",
"Content-Length"
)
),
snippets
)
);
final String response = target("path")
.register(
documentationConfiguration(this.documentationHandler)
)
.register(jerseyRestDocumentationFilter)
.request()
.get(String.class);
Which in the end gets me the snippet:
[source,bash]
----
$ curl 'http://my.host.com/path
----
I'd like to attempt a PR for enabling using your own request/response-converters. Do you have any preferences on how that should be done?
I'd like to attempt a PR for enabling using your own request/response-converters. Do you have any preferences on how that should be done?
Yeah, if you look at the JerseyRestDocumentation, you'll see where the filter is created with the document
static methods. You could just create an overload that accepts a request and response converter. Maybe just allow nulls for either of the arguments, defaulting to using the ones provided in the class.
Sorry you had to resort to copying the whole class. I don't really feel comfortable making the class public at this point. Maybe in the future when I have more time, I can add the functionality for it to behave more like the Spring MVC version where you can just configure the URI.
Also could you add a test? You can just add it to the bottom of the JerseyRestDocumentationIntegrationTest. After I review it, I can go ahead and release it for you.
Hey thanks for the PR (I'm assuming that's you also).
So when you first opened this issue, I tried to figure out how I used to use the in-memory provider to make this work. I couldn't figure it out at the time, so I figured your PR would be a good workaround for the moment. I actually found a project that I used this on, and saw how I did it. All it was, was just a matter of overriding the getBaseUri
on the JerseyTest
. For example
public class MyTest extends JerseyTest {
@Override
public URI getBaseUri() {
return URI.create("http://myapi.com/");
}
}
I tested this on the simple-jersey example and it works fine. This won't work on the Spring Boot example, as it doesn't use the Jersey Test Framework. And I don't know if it will work on the Dropwizard example, as they use the test framework a little differently.
As for your question on the provider, the dependency would be
<dependency>
<groupId>org.glassfish.jersey.test-framework.providers</groupId>
<artifactId>jersey-test-framework-provider-inmemory</artifactId>
<scope>test</scope>
</dependency>
You should also remove the Jetty provider. Also, as I mentioned, the limitation of the provider is that you won't be able to use Servlet APIs, for example if you need to inject HttpServletRequest
into any of your resources. Other than that, the I'm-memory provider works just fine. You can't use the Jetty provider, as it would cause a real network request from the client. You would need to change your hosts file to redirect the myapi.com the localhost. But even after that, from what I tested, you won't be able to connect to the default port 80 (permission issues). You could change the port with your own JettyTestContainerFactory
@Override
public URI getBaseUri() {
return URI.create("http://myapi.com/");
}
@Override
public TestContainerFactory getTestContainerFactory() {
return new MyJettyTestContainerFactory();
}
public static class MyJettyTestContainerFactory extends JettyTestContainerFactory {
@Override
public TestContainer create(URI baseUri, DeploymentContext context) throws IllegalArgumentException {
final URI uriWithPort = UriBuilder.fromUri(baseUri).port(8080).build();
return super.create(uriWithPort, context);
}
}
But from what I tested, this will still leave you with the port in your snippets, which is not what you want.
http://myapi.com:8080
So the only solution at this point is to use the in-memory provider.
I thought again about the PR, and I'm not really a big fan of it, in terms of allowing users to provide their own converters. A lot can go wrong. If you really still want to go this route (if the in-memory provider doesn't work for you), I could still accept the PR, just so you can get to working. But in the future, I may just deprecate and then remove it.
Thanks for taking the time to look at this! :) I'll try the approach with the in-memory provider as soon as I'm able, though I suspect I won't be able to use it.
It looks like I can't use the in-memory provider. Do you have any other suggestions on how I could get the snippets to show a custom host?
@stianlagstad I probably have some time to look over this a little bit more over the holiday weekend. I will see if I can add the support for modifying the host, similar to the way Spring MVC allows you to. I'll get back to you.
Thanks again! I might take a stab at that myself (see how it's happening when it's done in https://stackoverflow.com/a/33449310/1539208).
@stianlagstad Can you check out the latest master. I just want you to test it on your end before I release it. You can just run ./gradlew install
to install it into you local maven repo. Then just use 0.1.2-SNAPSHOT
version.
You can check out the bottom of the integration tests for usage. Basically, you'd just do
.register(documentationConfiguration(this.restDocumentation)
.uris().withScheme("https").withHost("testing.com").removePort())
This works well! :)
Could you release a new version, @psamsotha?
@stiancognite Hey I just released it. I decided to go with 1.0.0 as I didn't see a need to go to 2.0.0. I don't really have any plans to keep the version consistent with the Spring REST Docs version.
Thanks a lot for your contribution and motivating me to keep working on this :-)
Thanks a lot, @psamsotha! :)