Mapping files
- JSON schema
- Request-response information on Project View file nodes
- Unwrap single stub mapping in multi-mapping definitions
- Create mapping file from template
- Split multi-mapping file into multiple single-mapping files
- Merge a collection of stub mapping files
- Move stub-mapping object up-down
- Preview generated Java code
- Custom mapping root dirs
- Mapping directory suggestions
JSON schema
A JSON schema is bound to WireMock JSON mapping files (based on the schemas in the WireMock GitHub repository), which automatically provides validation and code completion. It works both for single- and multi-mapping files.
The schema is based on the original WireMock schemas inthe WireMock GitHub repository.
Since v1.0.10, different schema files are provided based on the version of WireMock used in each module of a project. Currently, the following ones are available:
- one for versions up to 2.35.0,
- one from 3.0.0 to 3.4.2,
- one for 3.5.0 and up.
Alpha, beta, etc. releases are not supported, e.g. 3.0.0-beta5 is recognized as 3.0.0 for simplicity.
When the WireMock version used by a project module changes, the schema used for a given file is updated automatically according to that new version. Also, in case no WireMock version is found for a module, a default schema is assigned to the corresponding files.
NOTE: the validation and auto-completion features are not implemented in this plugin, but in the IntelliJ platform itself. It simply provides the configuration for the IntelliJ platform, so that it knows what conditions must a file meet to assign a certain schema file to it.
JetBrains WireMock plugin
Since the WireMock plugin developed by JetBrains also provides a stub mapping schema, a plugin settings option is available to switch between the one in that plugin, and the ones provided by WireMocha (this is the default state).
Request-response information on Project View file nodes
To provide a basic overview of what data stub mapping files hold, this node decorator adds some basic information to stub mapping file nodes in the Project View.
It provides information on the following parts:
- request method: the value of the
request.method
property - request url: the value of, any one of, the following properties first found:
request.url
,request.urlPath
,request.urlPattern
,request.urlPathPattern
- response status: the value of the
response.status
property
Enable/disable node decoration
There is a toolbar action in the Project View to enable/disable the node decoration. By default, it is turned off. It saves the enabled/disable state per project.
Classification
Mapping files are classified as follows:
- no mapping
- single mapping
- multiple mappings
The way and cases which category and what information is displayed also aims to uncover potentially erroneous mapping files and missing properties.
No mapping
A mapping file is considered empty, and the file node is decorated with the text no mapping, when:
- the file is completely empty,
- the root-level value is not an object, for instance the content is an array:
["some", "array"]
, - There is no stub mapping inside a
mappings
property:{ "mappings": [ ] }
Single mapping
A mapping file is considered having a single mapping when:
- There is one empty or non-empty mapping in the file:
{
"request": { ... },
"response": { ... }
}
- There is a
mappings
property, and there is only one stub mapping inside.
{
"mappings": [
{
"request": { ... },
"response": { ... }
}
]
}
In this case, the file is decorated with the following texts, based on the input values:
request.method | request.url* | response.status | Node decoration text |
---|---|---|---|
“” | “” | 301 | ANY <any url> -> 301 |
“” | “/url” | “” | ANY /url -> 200 |
“” | “/url” | 301 | ANY /url -> 301 |
PUT | “” | “” | PUT <any url> -> 200 |
PUT | “” | 301 | PUT <any url> -> 301 |
PUT | “/url” | “” | PUT /url -> 200 |
PUT | “/url” | 301 | PUT /url -> 301 |
PUT | “/really-very-very-long-url” | 301 | PUT /really-very-very-lo… -> 301 |
The last example shows that url values are truncated at 20 characters to keep the node decoration concise.
Missing information tags
When there is information missing, to properly display the node decoration, the mapping properties are substituted with the following information:
- no
request
: ANY <any url> - no
request.method
: ANY - no
request.url
: <any url> - no
response
: 200 - no
response.status
: 200
The resulting node decoration text can be like ANY /some/url -> 200.
Multiple mappings
A mapping file is considered having multiple mappings when there is a mappings
property with more than one stub mapping inside:
{
"mappings": [
{
"request": { ... },
"response": { ... }
},
{
"request": { ... },
"response": { ... }
}
]
}
In this case, the file node is decorated with the text multiple.
Indexing
The displayed data is coming from a custom index implementation, so if you have the node decoration enabled when the IDE or a project launches, and you have some mapping folders open, the files inside won’t be visible until the IDE finishes indexing.
If you don’t use the node decoration feature, it is recommended to disable it to minimize any performance/resource impact it may have on the IDE.
Unwrap single stub mapping in multi-mapping definitions
This inspection can report mappings
properties in JSON mapping files that contain only a single stub mapping. In that case, specifying the mappings
property is not necessary, the stub mapping object itself can be the sole content of the mapping file.
It also provides a quick fix that unwraps the stub mapping, and essentially removes the wrapping around it.
Whether that child mapping has any content doesn’t matter, the property is reported regardless. However, nothing is reported when mappings
has a meta
sibling property specified, since that one doesn’t have a counterpart in the individual stub mapping schema.
Example:
{
"mappings": [ //"mappings" property name is reported
{
"request": { ... },
"response": { ... }
}
]
}
becomes
{
"request": { ... },
"response": { ... }
}
Create mapping file from template
The Create Mapping File action is available in the Project view context menu on directories called mappings
and on subdirectories of them. It uses File Templates to pre-populate mapping files with predefined contents.
You can select from two templates (both editable in Settings > Editor > File and Code Templates > Files). The provided file name must include the .json extension too.
- single request-response mapping
{
"request": {
"method": "GET",
"url": "/"
},
"response": {
"status": 200
}
}
- multiple request-response mappings
{
"mappings": [
{
"request": {
"method": "GET",
"url": "/"
},
"response": {
"status": 200
}
},
{
"request": {
"method": "GET",
"url": "/"
},
"response": {
"status": 200
}
}
]
}
Split multi-mapping file into multiple single-mapping files
This action is available in the Project view context menu at WireMock > Split Mapping File
. The action is visible regardless of the contents of the mapping files, but not all mapping files can be/make sense to be split.
Given the following mapping file, it splits this mapping file:
# filename: multi_mapping.json
{
"mappings": [
{
"request": {
"method": "GET"
}
"response": { ... }
},
{
"request": {
"method": "POST"
}
"response": { ... }
}
]
}
into the following ones:
# filename: multi_mapping_0.json
{
"request": {
"method": "GET"
}
"response": { ... }
}
# filename: multi_mapping_1.json
{
"request": {
"method": "POST"
}
"response": { ... }
}
The filenames are index-postfixes starting from 0. The original mapping file is removed after the new files are created.
Mapping files in certain cases cannot be split. If that is the case, a notification balloon is displayed in the Project View, on the mapping file node, to let you know why the split cannot happen:
- no indexed data for the file (ideally this should not happen),
- there is no mapping in the file
- there is only a single mapping in the file
- there are multiple mappings, but at least one of them doesn’t specify either the
request
orresponse
property.
Merge a collection of stub mapping files
These actions are available in the Project view context menu under the WireMock submenu.
They merge all JSON mapping files in a selected collection in three phases, and provide a progress bar in case there is a large number of files to merge:
- Phase 1: collects the stub mapping JSON objects. The mappings are collected in alphabetical order of their containing files’ names, and in the order of their presence in those files. Non-mapping files, and files that don’t contain a stub mapping, are excluded.
- Phase 2: merges the collected mappings into a new JSON file, whose name may or may not have be specified by the user, depending on the action.
- NOTE: the .json extension must be included when specifying the file name.
- Phase 3: deletes all mapping files from the selected directory:
- NOTE: mapping files that don’t contain any mapping, are deleted too. At the moment, there is no option to exclude them from the deletion.
There are popup balloons displayed when the whole merge process, or a part of it, couldn’t happen due to some precondition, or an error:
- No mapping file to merge.: when there is no actual mapping file in the directory, even if there are other JSON files present.
- A single mapping file won’t be merged.: when there is only a single mapping file in the directory.
- Could not get the content of some mapping files. See IDE logs.: there is a problem reading the content of a file.
- Could not delete some mapping files. See IDE logs.: speaks for itself
- Could not merge files. See IDE logs.: a catch-all message if there was any other kind of issue during any of the phases that is not caught/handled by the merge logic.
When the balloon message includes See IDE logs, there is further stacktrace information and reasoning available in the IDE’s idea.log file.
These merge actions are designed in a way, that with an Undo action invoked in the Project View, you can undo the entire merge operation.
IMPORTANT:
Although these actions have automated tests for various different inputs, make sure that all your stub mappings are still present before committing your changes to version control, just as you would normally review your changes before committing, in case there is a case I haven’t considered.
Merge stub mapping files in a selected folder
This action is available as Merge Mapping Files in This Directory, when there are at least two JSON files (be it mapping or non-mapping ones)
present in the selected directory.
It merges all JSON mapping files in the selected directory (files in subdirectories are not included) into a new JSON file whose name must be specified by the user
An example for the merge logic:
Given the following two stub mapping files:
single_mapping.json
{
"request": {
"method": "PATCH",
"url": "/single-mapping"
},
"response": {
"status": 200,
"bodyFileName": "a_response_body.json"
}
}
multi_mapping.json
{
"mappings": [
{
"request": {
"method": "GET",
"url": "/multi-mapping"
},
"response": {
"status": 404,
"body": "{{systemValue key='aKey' type='PROPERTY'}}"
}
},
{
"request": {
"method": "POST",
"url": "/multi-mapping"
},
"response": {
"status": 500
}
}
]
}
The merged version will be:
{
"mappings": [
{
"request": {
"method": "GET",
"url": "/multi-mapping"
},
"response": {
"status": 404,
"body": "{{systemValue key='aKey' type='PROPERTY'}}"
}
},
{
"request": {
"method": "POST",
"url": "/multi-mapping"
},
"response": {
"status": 500
}
},
{
"request": {
"method": "PATCH",
"url": "/single-mapping"
},
"response": {
"status": 200,
"bodyFileName": "a_response_body.json"
}
}
]
}
Merge stub mapping files selected by the user
This action is available as Merge Selected Mapping Files, when there are at least two JSON files (be it mapping or non-mapping ones) selected in the same directory. For now, merging files from multiple directories is not supported.
It merges all JSON mapping files selected by the user, into a new JSON file in their containing folder, and whose name must be specified by the user. The merge is performed in alphabetical order of the file names because merging by the order in which the files are selected would produce a result that might confuse users.
For an example of source and merged files, please see the previous section.
Move stub-mapping object up-down
Similarly to how Java, Kotlin, etc. methods can be moved up and down within a class, using Ctrl+Shift+Up/Down, WireMocha recognizes stub-mapping JSON objects in multi-mapping files.
The stub-mapping objects can be moved up and down the same way, when the caret is placed right before/after a mapping’s opening curly brace.
This is useful when you want to reorder the mappings; you don’t have to select the entire mapping to be able to move it, especially if the stub-mapping is long.
At the moment it supports moving only a single mapping.
Preview generated Java code
When editing JSON stub mapping files, a split editor is displayed in which the selected stub mapping is generated as Java code on the fly, as the JSON mapping changes. It is disabled by default, and can be enabled in Settings > Languages & Frameworks > WireMocha
. The preview displays content according to the following:
Single-mapping files, and ones with the mappings
but containing only a single mapping
Shows the generated code for the single stub mapping in the file.
Multi-mapping files
Shows the generated code for the stub mapping on which the caret is currently positioned. The preview is not available (an error message is shown) when there are either multiple carets, or the caret is not placed in a stub mapping object.
To generate code, under the hood, WireMocha uses WireMock’s own deserialization logic for stub mappings, so if a mapping is not syntactically correct according to the that logic, or according to the JSON syntax, an error message is displayed.
Generating additional local variables
There are parts of stub mappings that are represented as custom objects, whose transformation must happen to Map<>
instances. In order to create these Map objects on the Java side, there is a two-part logic incorporated:
- if the number of entries in that object doesn’t exceed 10, the max number of key-value pairs
Map.of()
accepts, they are generated into anMap.of()
initialized variable, - otherwise, a
new HashMap<>()
initialized variable is created before the stub mapping code, and the entries are put into that variable.
Such examples are com.github.tomakehurst.wiremock.common.Metadata
and com.github.tomakehurst.wiremock.extension.Parameters
.
With Map.of()
Map<String, Object> metadata = Map.of("meta1", "value1",
"meta2", <array value>);
stubFor(post(urlPathTemplate("/v1/{some}/path/{parameter}"))
.withMetaData(metadata)
With HashMap<>
Map<String, Object> customMatcherParams = new HashMap<>();
customMatcherParams.put("maxLength", 2048);
customMatcherParams.put("length", <object value>);
//the rest of the Map.put() calls on 'customMatcherParams'...
stubFor(patch(anyUrl())
.andMatching("body-too-long", customMatcherParams)
.willReturn(ok()));
One exception is post serve action, whose conversion supports only the HashMap<>
variant, but not Map.of()
, for now.
Also, since these JSON objects might be converted to any arbitrary Java object that is custom to a domain, project, etc., array and object value parts of those entries are not converted. Instead, placeholder values are generated for them. You can see them in the examples above.
Toolbar actions
There are two additional toolbar actions:
- Copy to Clipboard: copies the currently generated and displayed Java code to the clipboard
- Generate and Copy All to Clipboard: available only in multi-mapping files. It generates Java code for all stub mappings in the file, and copies all generated code to the clipboard.
Limitations
There are a few limitations that has to be mentioned to provide a full picture about what can be generated.
- There is a property called
fromConfiguredStub
incom.github.tomakehurst.wiremock.http.ResponseDefinition
whose configuration is not supported bycom.github.tomakehurst.wiremock.client.ResponseDefinitionBuilder
, thus even if it is present in the JSON mapping file, it is not generated into the Java code at the moment. - When matching a request with
equalToXml()
, using more than 10org.xmlunit.diff.ComparisonType
s is not supported for now, and are not generated into the preview. - When matching a request with
matchingXPath()
, using more than 10 namespaces is not supported for now, and are not generated into the preview. - When using
before()
andafter()
datetime matchers with now offset expressions, an additional (and redundant)expectedOffset()
call is generated due to limited information available in the deserialized mapping objects.
Custom mapping root dirs
In the plugin settings it is possible to specify custom root dir paths. For now, these settings take effect in the IDE Project View by displaying a dedicated icon for those directories, but this configuration also paves the way for potential future features that would require knowing of custom root dirs.
The root dirs specified here:
- can be any directory paths in the project, but are designed to specify actual mapping roots containing
mappings
and/or__files
directories, - are relative to the project root directory path,
- must start with a / and must not end with a /
Mapping directory suggestions
To simplify the creation of mappings
and __files
directories, these directory names are suggested in the New Directory creation popup when the following conditions are met:
Target containing directory | Suggested directory names |
---|---|
Contains neither mappings nor __files | No directory names for now, so that completion items don’t show up at many unnecessary places. |
Contains mappings directory but not __files | __files |
Contains __files directory but not mappings | mappings |
Contains both mappings and __files | No directory name. |