Hello! I am somewhat new to wiremock and could use...
# general
m
Hello! I am somewhat new to wiremock and could use a little help with the response templating helpers. Specifically the
#each
helper described here. I have a request body with an
persons
array property containing a varied amount of items. The mock should edit these persons in the response by adding a random generated UUID to each. For the example request:
Copy code
{
  persons: [
    {"name": "Andy", "age": 34}, 
    {"name": "Rick", "age": 18}, 
    {"name": "Michael", "age": 48}
  ]
}
The response should be:
Copy code
{
  persons: [
    {"id": "a6aa0b30-8afd-43d6-af25-431b7c3d08ba", "name": "Andy", "age": 34}, 
    {"id": "d2255b1a-d4fa-4b27-a459-69bde072ba36", "name": "Rick", "age": 18}, 
    {"id": "dc644b5a-9a0e-4e3d-b82a-c53e2b7f1561", "name": "Michael", "age": 48}
  ]
}
With the id being randomly generated. What I have managed to do so far is fix the number of persons in the array with the following template:
Copy code
{
  ...
  "response": {
    "jsonBody": {
      "persons": [
        {
          "id": "{{randomValue type='UUID'}}",
          "name": "{{jsonPath request.body '$.persons[0].name'}}"
          "age": "{{jsonPath request.body '$.persons[0].age'}}"
        },
        {
          "id": "{{randomValue type='UUID'}}",
          "name": "{{jsonPath request.body '$.persons[1].name'}}"
          "age": "{{jsonPath request.body '$.persons[1].age'}}"
        },
        {
          "id": "{{randomValue type='UUID'}}",
          "name": "{{jsonPath request.body '$.persons[2].name'}}"
          "age": "{{jsonPath request.body '$.persons[2].age'}}"
        }
      ]
    },
    "transformers": ["response-template"]
  }
}
Copy code
```
This only works for exactly 3 people, though. How do I make use of the
```{{#each request.body.persons as |person|}}
  <voodoo magic here>
{{/each}}
Helper in my json response template file? Any help would be appreciated.
r
Try this:
Copy code
{
  persons: [
      {{#each (jsonPath request.body '$.persons') as |person|}}
      {"id": "{{randomValue type='UUID'}}", "name": "{{ person.name }}", "age": {{ person.age }} }
      {{/each}}
  ]
}
m
This results in malformed json.
r
Oh yes, needs the commas and no trailing comma...
Copy code
{
  "persons": [
      {{#each (jsonPath request.body '$.persons') as |person|}}
      {"id": "{{randomValue type='UUID'}}", "name": "{{ person.name }}", "age": {{ person.age }} }{{#unless @last}},{{/unless}}
      {{/each}}
  ]
}
👀 1
It's just handlebars, full documentation here: https://handlebarsjs.com/guide/builtin-helpers.html
m
Yes but SpringBoot can't parse the json if it contains those handlebars.
r
I ran the output from my second example through a json linter, it's valid json
m
Then I guess my question is how do I tell IntelliJ that it's valid json and make SpringBoot interpret it as such?
r
I don't really understand where Spring Boot comes into it. IntelliJ - just suppress the warning, IntelliJ's wrong to assume that
withBody
should be valid JSON, it can be any String
m
So IntelliJ doesn't let me suppress this warning but that's alright, it can execute my tests regardless. However the substitution of those handlebars does not seem to get applied and I get this error when I run my spring boot tests:
Copy code
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.cloud.contract.wiremock.WireMockConfiguration': Invocation of init method failed; nested exception is com.github.tomakehurst.wiremock.common.JsonException: {
  "errors" : [ {
    "code" : 10,
    "source" : {
      "pointer" : "/response/jsonBody"
    },
    "title" : "Error parsing JSON",
    "detail" : "Unexpected character ('{' (code 123)): was expecting double-quote to start field name\n at [Source: (String)\"{\r\n  \"scenarioName\": \"CREATE_PERSONS\",\r\n ....
}
r
Are you enabling response templating for that stub?
m
Am I missing somethign here? I assumed it was due to SpringBoot but perhaps I was wrong.
I have applied the following transformer:
"transformers": ["response-template"]
r
I'm not immediately familiar with what's going on with SpringBoot creating a WireMockConfiguration - I'll have a look
Have you got the full stack there?
m
Hm let me see what I can share here
The stack trace is too long for Slack but I pasted it here . Does this help?
r
Yes, thanks - so how are you specifying the stub mapping? Can you paste your code?
m
I have removed some irrelevant properties from the response but the gist is this:
Copy code
{
  "scenarioName": "CREATE_PERSONS",
  "requiredScenarioState": "CREATE",
  "request": {
    "method": "POST",
    "url": "/groups"
  },
  "response": {
    "status": 201,
    "jsonBody": {
      "persons": [
        {{#each (jsonPath request.body '$.persons') as |person|}}
        {
          "id": "{{randomValue type='UUID'}}", 
          "name": "{{ person.name }}", 
          "age": {{ person.age }} a
        }{{#unless @last}},{{/unless}}
        {{/each}}
      ],
      "auditDetails": {
        "version": 0
      }
    },
    "headers": {
      "Content-Type": "application/json"
    },
    "transformers": ["response-template"]
  }
}
That's in a json file which i just point the autoconfiguration to with
Copy code
@AutoConfigureWireMock(port = 0, stubs = "classpath:/wiremock_stubs/Groups")
r
OK, you can't use
jsonBody
there because it's a template, not json. Try just
body
Which will then require you to escape the contents.
So it will look like this:
Copy code
{
  "scenarioName": "CREATE_PERSONS",
  "requiredScenarioState": "CREATE",
  "request": {
    "method": "POST",
    "url": "/groups"
  },
  "response": {
    "status": 201,
    "body": "{\"persons\": [{{#each (jsonPath request.body '$.persons') as |person|}}{\"id\": \"{{randomValue type='UUID'}}\", \"name\": \"{{ person.name }}\",\"age\": {{ person.age }} }{{#unless @last}},{{/unless}}{{/each}}],\"auditDetails\": {\"version\": 0}}",
    "headers": {
      "Content-Type": "application/json"
    },
    "transformers": [
      "response-template"
    ]
  }
}
m
which contents need to be escaped? Do you mean I have to wrap them all in a String ("<escaped content>")?
ah right
r
You might prefer to use
bodyFileName
and point to a separate file so that you don't have to put the whole thing on one line and don't have to escape anything.
Then you can have: `/wiremock_stubs/Groups`:
Copy code
{
  "scenarioName": "CREATE_PERSONS",
  "requiredScenarioState": "CREATE",
  "request": {
    "method": "POST",
    "url": "/groups"
  },
  "response": {
    "status": 201,
    "bodyFileName": "Groups.json.handlebars",
    "headers": {
      "Content-Type": "application/json"
    },
    "transformers": [
      "response-template"
    ]
  }
}
/wiremock_stubs/Groups.json.handlebars
Copy code
{
  "persons": [
      {{#each (jsonPath request.body '$.persons') as |person|}}
      {"id": "{{randomValue type='UUID'}}", "name": "{{ person.name }}", "age": {{ person.age }} }{{#unless @last}},{{/unless}}
      {{/each}}
  ]
}
I might have got the relative paths wrong there, but you get the idea
m
I'll try it out
is the file ending ".handlebars" important here?
Because I am getting another Json parsing error but this time with fields declared before the persons array.
So it is able to load the json template file anyway. But it doesn't recognize it as valid json
r
No, it shouldn't matter what the file ending is
Is the new error on creation of the stub, or when your client tries to parse what WireMock returns when the actual http request is made?
m
yeah I thought so. jsonlint.com does not see
Copy code
{
  "persons": [
      {{#each (jsonPath request.body '$.persons') as |person|}}
      {"id": "{{randomValue type='UUID'}}", "name": "{{ person.name }}", "age": {{ person.age }} }{{#unless @last}},{{/unless}}
      {{/each}}
  ]
}
as valid json.
The error is during stub creation
r
A response template won't be valid json, it's handlebars
It generates valid json (or should)
What is trying to parse it as json?
m
ah ok so those are not the same. aparently it's trying to parse it as json
Copy code
"errors" : [ {
    "code" : 10,
    "source" : {
      "pointer" : "/groupName"
    },
    "title" : "Error parsing JSON",
    "detail" : "Unrecognized field \"groupName\" (class com.github.tomakehurst.wiremock.stubbing.StubMapping), not marked as ignorable"
  } ]
groupName is a simple string property of the response object I ommitted above, it's on the same level as persons
r
That looks like you have
groupName
in the stub mapping json...
m
yes but it's a simple property that's part of the stub response template
it's in the template file, not in the mapping json file
r
Perhaps the classpath resource hasn't updated? You would get that error if you had json that looked like this:
Copy code
{
  "scenarioName": "CREATE_PERSONS",
  "requiredScenarioState": "CREATE",
  "request": {
    "method": "POST",
    "url": "/groups"
  },
  "response": {
    "status": 201,
    "bodyFileName": "Groups.json.handlebars",
    "headers": {
      "Content-Type": "application/json"
    },
    "transformers": [
      "response-template"
    ]
  },
  "groupName": "s"
}
m
Hm no that's not the case
I'll post both files here
👍 1
mapping.json:
Copy code
{
  "scenarioName": "CREATE_PERSONS",
  "requiredScenarioState": "CREATE",
  "request": {
    "method": "POST",
    "url": "/groups"
  },
  "response": {
    "status": 201,
    "bodyFileName": "createGroupResponseTemplate.json",
    "headers": {
      "Content-Type": "application/json"
    },
    "transformers": [
      "response-template"
    ]
  }
}
createGroupResponseTemplate.json
Copy code
{
  "groupName": "{{jsonPath request.body '$.groupName'}}",
  "department": "{{jsonPath request.body '$.department'}}",
  "persons": [
      {{#each (jsonPath request.body '$.persons') as |person|}}
        {
          "id": "{{randomValue type='UUID'}}", 
          "name": "{{ person.name }}", 
          "age": {{ person.age }} 
        }{{#unless @last}},{{/unless}}
      {{/each}}
  ]
}
I wouldn't even know where to begin debugging this, I don't feel like I am doing that much wrong here. Going off the wiremock documentation doesn't get me any farther either.
r
Oh, I see - the problem is that your
createGroupResponseTemplate.json
is in the directory that it's loading stubs from so it's treating it as a stub json and it isn't
m
oooh that makes a lot of sense.
Let me check
r
I guess that
@AutoConfigureWireMock(port = 0, stubs = "classpath:/wiremock_stubs/Groups")
just takes all json files in
/wiremock_stubs/Groups
and tries to turn them into stubs. I don't know if Spring will recurse into subdirs? If not you could put your body into
/wiremock_stubs/Groups/bodies/createGroupResponseTemplate.json
and it might work with
"bodyFileName": "bodies/createGroupResponseTemplate.json"
Or perhaps just naming it
createGroupResponseTemplate.json.handlebars
would be enough to stop Spring treating it as a stub?
I've never used Spring's WireMock stuff
m
I have it in a subdir already, it does recurse and take all files it can find in the folder.
Looks like putting
createGroupResponseTemplate.json
in
src/test/resources/__files
might work.
m
Well
com.github.tomakehurst.wiremock.security.NotAuthorisedException: Access to file ../../../wiremock_templates/personsResponseTemplate.json is not permitted
I don't understand where to put it so it doesn't view it as a stub but still has authorization to read it
it's under /test/resources/wiremock_templates/
and the stub points to it via relative path
r
Did you follow this bit of the Spring docs? "To customize this location you can set the files attribute in the @AutoConfigureWireMock annotation to the location of the parent directory"
Otherwise it should be in
src/test/resources/__files
m
Oh so that's a specific directory name then? I'll see what the Config lets me do
Getting there
Somewhere it found an illegal char
Illegal unquoted character ((CTRL-CHAR, code 13)): has to be escaped using backslash to be included in string value
But it seems to be reading the template file and the response file now
It receives the request, matches it to the correct response stub but the response is malformed aparently. The content is escaped though.
I don't see any char code 13 in the response message though
Ah that's some carriage return
Hm could it be the file encoding?
Alright anyway, thank you very much for your help. I would never have gotten to this point on my own!
I'll be heading out now, have a great weekend @Rob Elliot
r
Thanks, glad it's nearly there. Have a good weekend.
m
Hi Rob, you seemed interested in this issue so I thought I'd give you an update. It turns out the whole template thing does not work for me because when parsing the template, new lines are not escaped and it looks like Spring cannot handle literal \n characters in the response body string. At this point I gave up and have to just live with tests using 3 fixed persons in the response array.
The only solution I can think of trying is removing all new lines from the template file itself, which would make it impossible to maintain so I am not going to even attempt that.
r
I'm a bit confused again - the body file is being loaded but something doesn't like the
\r
carriage return character. What is it that doesn't like it? The waters are quite muddy because of having Spring in the mix, so stack traces with errors are going to be vital to understanding what's going ion.
m
Correct, the body file is being loaded and the whole file is parsed as a String. However Spring seems to be unable to handle raw /n characters in that string and would need them to be escaped or more likely even completely removed before it can parse the json into a response object. Here is the relevant part of the stack trace: https://pastebin.com/wgEgH1Rr Also this is the wiremock output for the matched response definition it found:
Copy code
Matched response definition:
{
  "status" : 201,
  "body" : "{\n  \"groupName\": \"Mock Group Name\",\n  \"department\": \"Mock Department\",\n  \"persons\": [\n      {\n        \"id\": \"d822a6b5-8b5e-4f97-9690-dc87b3e6654e\", \n        \"name\": \"Mock Name P1\", \n        \"age\": \"23\"\n      },\n      {\n        \"id\": \"e9ddf0f5-1d49-4bd9-8a89-975a82b0a966\", \n        \"name\": \"Mock Name P2\", \n        \"age\": \"32\"\n      }\n  ]\n}\n",
  "bodyFileName" : "createGroup.wiremock",
  "headers" : {
    "Content-Type" : "application/json"
  },
  "transformers" : [ "response-template" ]
}
r
Why have you got
body
and
bodyFileName
? You should have one or the other (in this case
bodyFileName
).
m
Well my mappings file only has bodyFileName. In the log output it adds the body as the parsed string of the template file.
So it seems to parse the template file and add it as the body of the response in the response definition
I am not quite sure what exactly is happening behind the scenes to be honest, like i said I'm quite new at this. \
r
Oh, OK, sorry, quite likely I should have known that
m
No worries
r
It's a little hard to tell - am I right that the exception is happening when your code is parsing the response from WireMock?
m
yes that looks correct
r
I've put together my own test of this and it doesn't reproduce it (Wiremock 2.35.0) From the error it appears that WireMock is responding with a body that is not valid json, because the
items
key should be a string but contains an unescaped new line. As there is no
items
key in the Matched response definition you've sent I guess that it's not actually the real values. But it's probably that your
createGroup.wiremock
handlebars file does not resolve to valid json. You could try copying the value of the
body
key in the Matched response definition out, sticking it through a json unescaper, and saving the result into a json file - IntelliJ will probably then show you what's wrong with it.
m
You are a wizard, Rob 😄
100% spot on.
I had a typo in the template file
It works now. Thank you so much for your time and help and patience and everything else.
r
Good, glad we got there!