In my Grails app, I have a config property which includes a partial URL path, like this:
http://apis.mycompany.com/my-api/v1/
I want to invoke endpoints using this URL with HttpBuilder:
class MyApiService {
@Value('${myApiUrl}')
String myApiUrl
def http
@PostConstruct()
init() {
http = HttpBuilder.configure {
request.uri = myApiUrl
}
}
def getData() {
http.get() {
// This doesn't work!!
request.uri.path = 'resource/data'
}
}
}
The request.uri.path erases the existing path segment:
java.lang.RuntimeException: java.net.URISyntaxException: Relative path in absolute URI: http://apis.mycompany.comresource/data
I was able to work around this:
request.raw = "${myApiUrl}resource/data"
...but I can't get Ersatz to work with raw in my Spock test:
def setup() {
ersatz = new ErsatzServer()
service.myApiUrl = ersatz.httpUrl + '/'
}
void "getData() - should call the endpoint"() {
given:
def jsonData = '[{"person": "Someone"}]'
ersatz.expectations {
get("/resource/data") {
called 1
responder {
content jsonData, 'application/json;chartset=UTF-8'
}
}
}
when:
def result = service.getData()
then:
result == jsonData
}
The URL being assigned to raw is: http://localhost:-1/resources/data
This results in an exception:
java.lang.IllegalArgumentException: protocol = http host = null
at sun.net.spi.DefaultProxySelector.select(DefaultProxySelector.java:176)
at sun.net.www.protocol.http.HttpURLConnection.plainConnect0(HttpURLConnection.java:1132)
at sun.net.www.protocol.http.HttpURLConnection.plainConnect(HttpURLConnection.java:1032)
at sun.net.www.protocol.http.HttpURLConnection.connect(HttpURLConnection.java:966)
at groovyx.net.http.JavaHttpBuilder$Action.lambda$execute$2(JavaHttpBuilder.java:147)
at groovyx.net.http.JavaHttpBuilder$ThreadLocalAuth.with(JavaHttpBuilder.java:331)
at groovyx.net.http.JavaHttpBuilder$Action.execute(JavaHttpBuilder.java:122)
at groovyx.net.http.JavaHttpBuilder.createAndExecute(JavaHttpBuilder.java:374)
at groovyx.net.http.JavaHttpBuilder.doGet(JavaHttpBuilder.java:381)
at groovyx.net.http.HttpObjectConfigImpl.nullInterceptor(HttpObjectConfigImpl.java:47)
at groovyx.net.http.HttpBuilder.get(HttpBuilder.java:346)
at groovyx.net.http.HttpBuilder.get(HttpBuilder.java:1297)
at com.mycompany.MyApiService.getData(...)
at com.mycompany.MyApiServiceSpec.getData(...)
Is this a bug in HttpBuilder, or am I doing it wrong? Help appreciated!
Update:
I thought Ersatz would start when constructed, but it turns out I was wrong. Calling start() before using the URL fixed the -1 port number:
def setup() {
ersatz = new ErsatzServer()
ersatz.start()
service.myApiUrl = ersatz.httpUrl + '/'
}
Now, the builder is getting a seemingly valid URL:
http://localhost:52180/resources/data
...but the Ersatz server gives a message that the expectation is not matched.
java.lang.AssertionError: Expectations for Expectations (ErsatzRequest): <GET>, "/resources/data" were not met.. Expression: (com.stehno.ersatz.impl.ErsatzRequest -> com.stehno.ersatz.impl.ErsatzRequest) r.verify()
at com.stehno.ersatz.impl.ExpectationsImpl.verify_closure2(ExpectationsImpl.groovy:365)
at com.stehno.ersatz.impl.ExpectationsImpl.verify(ExpectationsImpl.groovy:364)
at com.stehno.ersatz.ErsatzServer.verify(ErsatzServer.groovy:522)
at com.mycompany.MyApiServiceSpec.cleanup(...)
The Ersatz docs indicate that the unmatched request should be printed out, but it only prints the above stack trace (and an HttpNotFoundException).
For me it works to just append the path instead of assigning it: