1

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).

kriegaex
  • 63,017
  • 15
  • 111
  • 202
RMorrisey
  • 7,637
  • 9
  • 53
  • 71
  • You need to add `reportToConsole true` to the Ersatz constructor config to get the report written to the console, otherwise its just written to the logs. – cjstehno May 08 '19 at 15:01
  • I would suggest turning on some logging - DEBUG level should give you a lot more information from both HttpBuilder-NG and Ersatz so you can see what both are doing. My thought would be that the problem is with the HttpBuilder-NG config. The `raw` method is really meant to solve url-encoding issues. – cjstehno May 08 '19 at 15:05
  • Lastly, if I remember right, HB-NG does not properly append to a path when the host and partial path are specified in the configuration block. Generally you want to have the host configured there and then the path configured per request. I could be wrong, its been a while. – cjstehno May 08 '19 at 15:07

1 Answers1

0

For me it works to just append the path instead of assigning it:

request.uri.path += 'resource/data'
Ich
  • 1,350
  • 16
  • 27