I am struggling somewhat to fit the extensions and...
# wiremock-java
a
I am struggling somewhat to fit the extensions and templating to my java-binding of stubs: here is my scenario: • a PUT request "/v1/defs/{name}" allows the user to upload an InputStream with content • a subsequent GET request with same "/v1/defs/{name}" needs to return the payload previously PUT I have defined the stub as follows:
Copy code
mockServer.stubFor(
                    put(urlPathTemplate("/v1/defs/{name}"))
                            .willReturn(putResp)
I am not sure how to define the GET stub to effectively cause the return of the body of the PUT request. I have read the Extensions documentation and I have configured my wireMock as follows:
Copy code
CaffeineStore store = new CaffeineStore();
            options().templatingEnabled(true).globalTemplating(true).extensions(new StateExtension(store));
            WireMockServer wireMockServer = new WireMockServer(wireMockConfig().port(port));
From the documentation examples, which are json (and not Java) I find it hard to map those json definitions to the java counterpart to set in the stub. It seemed to me that involving a .
withServeEventListener()
invocation in the stub should achieve that but I could not figure out the exact incantation. Any help or pointer will be welcome.
Inspecting the materials in the demo exposes the following: https://github.com/wiremock/wiremock-state-extension/blob/develop/src/test/java/or[…]xtensions/state/functionality/RecordStateEventListenerTest.java which seems to point at an appropriate analogous case to what I am trying to do
The following seems to be what I arrived at based on the examples:
Copy code
public static void configureStubs(WireMockServer mockServer) {
            ResponseDefinitionBuilder putResp = aResponse().withStatus(HttpStatus.SC_NO_CONTENT);
            mockServer.stubFor(
                    put(urlPathTemplate("/v1/defs/{name}"))
                            .willReturn(putResp)
                            .withServeEventListener("recordState", 
                                    Parameters.from(
                                        Map.of(
                                                "context", "{{request.path}}",
                                                "state", Map.of(
                                                    "statePayload", "{{request.body}}")
                                        ))));
            ResponseDefinitionBuilder getResp = aResponse().withStatus(HttpStatus.SC_OK);
            mockServer.stubFor(
                    get(urlPathTemplate("/v1/defs/{name}"))
                            .andMatching("state-matcher",
                                    Parameters.from(
                                            Map.of("hasContext", "{{request.path}}")
                                    ))
                            .willReturn(
                                    WireMock.ok()
                                    .withHeader("content-type", "application/json")
                                    .withBody("{{state context=request.path}}")
                            )
            );
        }
I would appreciate some input to indicate whether I am in the right direction
l
@Dirk Bolte would probably be able to tell you for certain but it looks on the right track to me. Have you tested it out ? Is it working as you expect. ?
a
I am getting the following error:
Copy code
java.lang.NoSuchMethodError: 'java.util.Map com.github.tomakehurst.wiremock.extension.responsetemplating.TemplateEngine.buildModelForRequest(com.github.tomakehurst.wiremock.http.Request)'
l
This could be a version issue. What version of WireMock are you using and what version of the extension are you using ?
a
wiremock-state-extension - 0.8.0 wiremock-standalone - 3.0.1
I am wondering since I am embedding in a test program it should not be Wiremock-standalone, but use plain Wiremock. I can see version 3.9.2 is available, also
l
3.10.0
was released recently
a
I think there is still a problem in the definitions: • in the PUT
Copy code
Map.of(
                                                    "context",
                                                    "{{request.path}}",
                                                    "state",
                                                    Map.of("statePayload", "{{request.body}}"))
• In the GET
Copy code
Parameters.from(Map.of("hasContext", "{{request.path}}")))
                            .willReturn(
                                    WireMock.ok()
                                            .withHeader("content-type", "application/json")
                                            .withBody("{{state context=request.path}}")));
I am wondering if I am missing using the 'statePayload' field in the Map for the population of the body
Should be something like:
Copy code
{state context=request.path property='statePayload'}
Still not there yet:
Copy code
Request was not matched
                                               =======================

-----------------------------------------------------------------------------------------------------------------------
| Closest stub                                             | Request                                                  |
-----------------------------------------------------------------------------------------------------------------------
                                                           |
PUT                                                        | GET                                                 <<<<< HTTP method does not match
[path template]                                            | /v1/defs/DiagnosticAction
/v1/defs/{name}                                            |
                                                           |
                                                           |
-----------------------------------------------------------------------------------------------------------------------
Is it implying it did not insert the stub for GET or that it does not recognize it ? Any problem with the GET stub definition ?
d
can you post the whole definition? Also, the extension has some logs - you can see them by registering a notifier in the wiremock configuration:
.notifier(new ConsoleNotifier(true))
a
That helped, but still not there: • POST state persistence:
Copy code
2024-12-05 12:09:04.053 Request received:
127.0.0.1 - PUT /v1/defs/DiagnosticAction

Accept: [application/json]
X-Workflow-Client-Version: [13.0.4]
X-Workflow-Client-Host: [<http://amarkel.xxxxxxx.com|amarkel.xxxxxxx.com>]
x-wfaas-domain: [localhost]
opc-client-retries: [false]
opc-request-id: [CC9F11D273854B518C6FAE1266FDC44B]
Content-Type: [application/json]
User-Agent: [Oracle-JavaSDK/2.76.0 (Linux/5.4.17-2136.314.6.3.el7uek.x86_64; Java/17.0.5; Java HotSpot(TM) 64-Bit Server VM/17.0.5+9-LTS-191)]
opc-client-info: [Oracle-JavaSDK/2.76.0]
Host: [localhost:39000]
Connection: [keep-alive]
Content-Length: [1443]
{"<JSON-PAYLOAD>"}



Matched response definition:
{
  "status" : 204
}

Response:
HTTP/1.1 204
Matched-Stub-Id: [92dc08fc-102e-4978-835a-1fb5eeaba509]

2024-12-05 12:09:04.281 Context '/v1/defs/DiagnosticAction': created
2024-12-05 12:09:04.282 Context '/v1/defs/DiagnosticAction': property 'statePayload' updated
• GET retrieval
Copy code
2024-12-05 12:09:04.373 Context '/v1/definitions/DiagnosticAction/2/0': hasContext matched
2024-12-05 12:09:04.395 Request received:
127.0.0.1 - GET /v1/defs/DiagnosticAction

Accept: [application/json]
X-Workflow-Client-Version: [13.0.4]
X-Workflow-Client-Host: [<http://amarkel.xxxxxx.com|amarkel.xxxxxx.com>]
x-wfaas-domain: [localhost]
opc-client-retries: [false]
opc-request-id: [071F0836DB734F7AB08EA22040C5D536]
User-Agent: [Oracle-JavaSDK/2.76.0 (Linux/5.4.17-2136.314.6.3.el7uek.x86_64; Java/17.0.5; Java HotSpot(TM) 64-Bit Server VM/17.0.5+9-LTS-191)]
opc-client-info: [Oracle-JavaSDK/2.76.0]
Host: [localhost:39000]
Connection: [keep-alive]



Matched response definition:
{
  "status" : 200,
  "jsonBody" : "{{state context=request.path} property='statePayload'}",
  "headers" : {
    "content-type" : "application/json"
  }
}

Response:
HTTP/1.1 200
content-type: [application/json]
Matched-Stub-Id: [c2a14f1f-9bf0-4ead-b64c-98edb66a2607]
What seems evident is that there is no 'handlebar' transformation/extraction of the 'statePayload' property here are the updated definitions: PUT:
Copy code
ResponseDefinitionBuilder putResp = aResponse().withStatus(HttpStatus.SC_NO_CONTENT);
            mockServer.stubFor(
                    put(urlPathTemplate("/v1/defs/{name}"))
                            .willReturn(putResp)
                            .withServeEventListener(
                                    "recordState",
                                    Parameters.from(
                                            Map.of(
                                                    "context",
                                                    "{{request.path}}",
                                                    "state",
                                                    Map.of("statePayload", "{{request.body}}")))));
GET:
Copy code
mockServer.stubFor(
                    get(urlPathTemplate("/v1/defs/{name}"))
                            .andMatching(
                                    "state-matcher",
                                    Parameters.from(Map.of("hasContext", "{{request.path}}")))
                            .willReturn(
                                    WireMock.ok()
                                            .withHeader("content-type", "application/json")
                                            .withJsonBody(
                                                    MAPPER.readTree(
                                                            MAPPER.writeValueAsString(
                                                                    "{{state context=request.path} property='statePayload'}")))));
I can now see my mistake - the double handlebar incorrect in the GET stub
Should have been:
"{{state context=request.path property='statePayload'}}")
Still problem exists (after instrumenting to inspect what is extracted from the state:
Copy code
.withJsonBody(
                                                    MAPPER.readTree(
                                                            captureValue(
                                                                    MAPPER.writeValueAsString(
                                                                            "{{state context=request.path property='statePayload'}}"))))));
But in 'captureValue' I don't see the content extracted from state, but the same string in the quotes.
Should this
Copy code
Map.of("statePayload", "{{request.body}}"))))
be
Copy code
Map.of("statePayload", "{{request.body $}}"))))
Inspecting with the debugger in the IDE shows that the store has not cached any values
Inspection of the console output shows that the payload is not there ...
Copy code
statePayload => {{jsonPath request.body '$'}}
I am not sure about the magic incantation to stick the request.body into the state Map
I think I did not pay attention to this:
To record a complete response body, use (ATTENTION: tripple {{{):
still not it ...
Copy code
mockServer.stubFor(
                    put(urlPathTemplate("/v1/defs/{name}"))
                            .willReturn(putResp)
                            .withServeEventListener(
                                    "recordState",
                                    Parameters.from(
                                            Map.of(
                                                    "context",
                                                    "{{request.path}}",
                                                    "state",
                                                    captureValue(
                                                            Map.of(
                                                                    "statePayload",
                                                                    "{{{jsonPath response.body '$'}}}"))))));
The body is not extracted and the Map just has the string
I see the following in the console log:
Copy code
Receiver class org.wiremock.extensions.state.extensions.StateHandlerbarHelper does not define or inherit an implementation of the resolved method &apos;abstract java.lang.Object apply(java.lang.Object, wiremock.com.github.jknack.handlebars.Options)&apos; of interface wiremock.com.github.jknack.handlebars.Helper.
Not clear what it means, what causes it and how to resolve.
I gave up on trying to capture the state Instead, on the getter, I serialize the value that is expected to be returned and not the one that the PUT operation persisted
the put stub has the payload in the request.body not on the response.body. I was unable to have the handlebar parser to extract the value and stick it inside the Store.
d
Sorry for the delay. Will try to have a look at it later today.
a
Thanks,
d
did some trial and error and got it working on wiremock 3.10, state extension 0.8.0 : https://gist.github.com/dirkbolte/358f9b2dd9444d6d6651c66fdf732c33 . What I adapted (might not be complete): • in order to address
{name}
, you have to use
request.pathSegments.[name]
(
request.path
probably caused the error you saw) • thus all contexts had to be:
request.pathSegments.[name]
• the get stub doesn't need the mapper - this is only needed when you use
withJsonBody
. For returning the plain input, using
.withBody("{{{state context=request.pathSegments.[name] property='statePayload' }}}")))
was sufficient the test still fails but just for the purpose on showing requests and responses.
a
Thanks, the main issue is to capture the request.body in the PUT, as it is necessary to return it on the GET