{"id":18875809,"url":"https://github.com/wiremock/wiremock-state-extension","last_synced_at":"2025-04-14T17:31:42.570Z","repository":{"id":180609464,"uuid":"656023419","full_name":"wiremock/wiremock-state-extension","owner":"wiremock","description":"Adds support for transporting state across different API mock stubs","archived":false,"fork":false,"pushed_at":"2024-07-01T04:59:13.000Z","size":1514,"stargazers_count":15,"open_issues_count":7,"forks_count":5,"subscribers_count":4,"default_branch":"develop","last_synced_at":"2024-07-05T16:20:26.862Z","etag":null,"topics":["hacktoberfest","wiremock","wiremock-extension"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/wiremock.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-06-20T05:40:58.000Z","updated_at":"2024-07-01T04:59:15.000Z","dependencies_parsed_at":null,"dependency_job_id":"fd3ad2b6-7cf0-4299-8d27-0323b7d96499","html_url":"https://github.com/wiremock/wiremock-state-extension","commit_stats":null,"previous_names":["wiremock/wiremock-extension-state","wiremock/wiremock-state-extension"],"tags_count":32,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wiremock%2Fwiremock-state-extension","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wiremock%2Fwiremock-state-extension/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wiremock%2Fwiremock-state-extension/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wiremock%2Fwiremock-state-extension/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/wiremock","download_url":"https://codeload.github.com/wiremock/wiremock-state-extension/tar.gz/refs/heads/develop","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":223639348,"owners_count":17177816,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["hacktoberfest","wiremock","wiremock-extension"],"created_at":"2024-11-08T06:09:14.699Z","updated_at":"2024-11-08T06:09:15.335Z","avatar_url":"https://github.com/wiremock.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# WireMock State extension\n\n[![GitHub release (latest by date)](https://img.shields.io/github/v/release/wiremock/wiremock-state-extension)](https://github.com/wiremock/wiremock-state-extension/releases)\n[![Maven Central](https://img.shields.io/maven-central/v/org.wiremock.extensions/wiremock-state-extension)](https://central.sonatype.com/artifact/org.wiremock.extensions/wiremock-state-extension)\n[![Slack](https://img.shields.io/badge/slack-slack.wiremock.org-brightgreen?style=flat\u0026logo=slack)](https://slack.wiremock.org/)\n[![GitHub contributors](https://img.shields.io/github/contributors/wiremock/wiremock-state-extension)](https://github.com/wiremock/wiremock-state-extension/graphs/contributors)\n![Line Coverage](../badges/line-coverage.svg)\n![Branches Coverage](../badges/branches-coverage.svg)\n\n\u003cp align=\"center\"\u003e\n    \u003ca href=\"https://wiremock.org\" target=\"_blank\"\u003e\n        \u003cimg width=\"512px\" src=\"https://wiremock.org/images/logos/wiremock/logo_wide.svg\" alt=\"WireMock Logo\"/\u003e\n    \u003c/a\u003e\n\u003c/p\u003e\n\nAdds support to transport state across different stubs.\n\n## Feature summary\n\n- Store a state for a context\n    - overwrite states\n    - append new states a state list\n- Delete a state\n    - delete states from state list (first, last, by index, by state propery comparison)\n- Request matching against context existence/non-existence\n- Response templating integration\n    - get state for a given context\n    - get state list entry by index\n- Templating support in all configuration options of this extension\n\n## Glossary\n\n| Term       | Description                                                                                                                                     |\n|------------|-------------------------------------------------------------------------------------------------------------------------------------------------|\n| `context`  | States are scoped by a context. Behavior is similar to a key in a map.                                                                          |\n| `state`    | The actual state. There can be only one per context - but it can be overwritten.                                                                |\n| `property` | A property of a `state`. A state can have multiple properties.                                                                                  |\n| `list`     | Next to the singularic state, a context can have a list of `states`. The list of `states` can be modified but `states` within the `list` can't. |\n\n```mermaid\nclassDiagram\n    direction LR\n    Store \"1\" *-- \"*\" Context\n    Context \"1\" *-- \"1\" State\n    Context \"1\" *-- \"1\" List\n    List \"1\" *-- \"*\" State\n    State \"1\" *-- \"*\" Property\n    class Property {\n        +String key\n        +String value\n    }\n\n```\n\n## Background\n\nWireMock supports [Response Templating](https://wiremock.org/docs/response-templating/) and [Scenarios](https://wiremock.org/docs/stateful-behaviour/)\nto add dynamic behavior and state. Both approaches have limitations:\n\n- `Response templating` only allows accessing data submitted in the same request\n- `Scenarios` cannot transport any data other than the state value itself\n\nIn order to mock more complex scenarios which are similar to a sandbox for a web service, it can be required to use parts of a previous request.\n\n## Example use cases\n\nCreate a sandbox for a webservice. The web service has two APIs:\n\n### CRUD\n\n1. `POST` to create a new identity (`POST /identity`)\n    - Request:\n   ```json\n   {\n    \"firstName\": \"John\",\n    \"lastName\": \"Doe\"\n   }\n    ```\n    - Response:\n   ```json\n    {\n      \"id\": \"kn0ixsaswzrzcfzriytrdupnjnxor1is\", # Random value\n      \"firstName\": \"John\",\n      \"lastName\": \"Doe\" \n   }\n    ```\n2. `GET` to retrieve this value (`GET /identity/kn0ixsaswzrzcfzriytrdupnjnxor1is`)\n\n- Response:\n\n  ```json\n    {\n      \"id\": \"kn0ixsaswzrzcfzriytrdupnjnxor1is\",\n      \"firstName\": \"John\",\n      \"lastName\": \"Doe\"\n    }\n  ```\n\nThe sandbox should have no knowledge of the data that is inserted. While the `POST` can be achieved\nwith [Response Templating](https://wiremock.org/docs/response-templating/),\nthe `GET` won't have any knowledge of the previous post.\n\n### Queue\n\n1. `POST` add a new item (`POST /queue`)\n    - Request:\n   ```json\n   {\n    \"firstName\": \"John\",\n    \"lastName\": \"Doe\"\n   }\n    ```\n    - Response:\n   ```json\n    {\n      \"id\": \"kn0ixsaswzrzcfzriytrdupnjnxor1is\", # Random value\n      \"firstName\": \"John\",\n      \"lastName\": \"Doe\" \n   }\n    ```\n\n2`POST` add another new item (`POST /queue`)\n\n- Request:\n\n   ```json\n   {\n  \"firstName\": \"Jane\",\n  \"lastName\": \"Doe\"\n  }\n   ```\n\n- Response:\n\n   ```json\n   {\n   \"id\": \"54owywgurlqepq1wc5xvyc2hipe8xp4u\",  # Random value\n   \"firstName\": \"Jane\",\n   \"lastName\": \"Doe\"\n   }\n   ```\n\n3. `GET` to retrieve the first value (`GET /queue`)\n\n- Response:\n\n  ```json\n  {\n  \"id\": \"kn0ixsaswzrzcfzriytrdupnjnxor1is\",\n  \"firstName\": \"John\",\n  \"lastName\": \"Doe\"\n  }\n  ```\n\n4. `GET` to retrieve the second value (`GET /queue`)\n\n- Response:\n\n  ```json\n  {\n  \"id\": \"54owywgurlqepq1wc5xvyc2hipe8xp4u\",\n  \"firstName\": \"Jane\",\n  \"lastName\": \"Doe\"\n  }\n  ```\n\n# Usage\n\n## Compatibility matrix\n\n| `wiremock-state-extension` version | `WireMock` version |\n|------------------------------------|--------------------|\n| `0.8.0`+                           | `3.7.0`+           |\n| `0.7.0`+                           | `3.6.0`+           |\n| `0.5.1`+                           | `3.3.1`+           |\n| `0.1.0`+                           | `3.0.0`+           |\n| `0.0.6`+                           | `3.0.0-beta-14`+   |\n| `0.0.3`+                           | `3.0.0-beta-11`+   |\n\n## Installation\n\n### Gradle\n\n```groovy\ndependencies {\n    testImplementation(\"org.wiremock.extensions:wiremock-state-extension:\u003cyour-version\u003e\")\n}\n```\n\n### Maven\n\n```xml\n\u003cdependencies\u003e\n  \u003cdependency\u003e\n    \u003cgroupId\u003eorg.wiremock.extensions\u003c/groupId\u003e\n    \u003cartifactId\u003ewiremock-state-extension\u003c/artifactId\u003e\n    \u003cversion\u003eyour-version\u003c/version\u003e\n    \u003cscope\u003etest\u003c/scope\u003e\n  \u003c/dependency\u003e\n\u003c/dependencies\u003e\n```\n\n### GitHub Packages\n\nYou can also install the dependencies from GitHub Packages.\nFollow the instructions on [GitHub Docs](https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-apache-maven-registry) to\nadd authentication to GitHub packages.\n\n\u003cdetails\u003e\n\u003csummary\u003e\nUse GitHub Packages in Gradle\n\u003c/summary\u003e\n\n```groovy\nrepositories {\n    maven {\n        url = uri(\"https://maven.pkg.github.com/wiremock/wiremock-extension-state\")\n    }\n}\n\n\ndependencies {\n    testImplementation(\"org.wiremock.extensions:wiremock-state-extension:\u003cyour-version\u003e\")\n}\n```\n\n\u003c/details\u003e\n    \n\u003cdetails\u003e\n\u003csummary\u003e\nUse GitHub Packages in Maven\n\u003c/summary\u003e\n```xml\n\n\u003crepositories\u003e\n    \u003crepository\u003e\n        \u003cid\u003egithub-wiremock-state-extension\u003c/id\u003e\n        \u003cname\u003eWireMock Extension State Apache Maven Packages\u003c/name\u003e\n        \u003curl\u003ehttps://maven.pkg.github.com/wiremock/wiremock-state-extension\u003c/url\u003e\n    \u003c/repository\u003e\n\u003c/repositories\u003e\n\n\u003cdependencies\u003e\n  \u003cdependency\u003e\n    \u003cgroupId\u003eorg.wiremock.extensions\u003c/groupId\u003e\n    \u003cartifactId\u003ewiremock-state-extension\u003c/artifactId\u003e\n    \u003cversion\u003eyour-version\u003c/version\u003e\n    \u003cscope\u003etest\u003c/scope\u003e\n  \u003c/dependency\u003e\n\u003c/dependencies\u003e\n```\n\n\u003c/details\u003e\n\n## Register extension\n\n### Java\n\nThis extension makes use of WireMock's `ExtensionFactory`, so only one extension has to be registered: `StateExtension`.\nIn order to use them, templating has to be enabled as well. A store for all state data has to be provided. This extension\nprovides a `CaffeineStore` which can be used - or you can provide your own store:\n\n```java\npublic class MySandbox {\n    private final WireMockServer server;\n\n    public MySandbox() {\n        var stateRecordingAction = new StateRecordingAction();\n        var store = new CaffeineStore();\n        server = new WireMockServer(\n            options()\n                .dynamicPort()\n                .templatingEnabled(true)\n                .globalTemplating(true)\n                .extensions(new StateExtension(store))\n        );\n        server.start();\n    }\n}\n```\n\n### Standalone\n\nThis extension uses the `ServiceLoader` extension to be loaded by WireMock. As Standalone version, it will use `CaffeineStore` for\nstoring any data.\n\nThe standalone jar can be downloaded from [GitHub](https://github.com/wiremock/wiremock-extension-state/packages/1902576) .\n\n```bash\njava -cp \"wiremock-state-extension-standalone-0.4.0.jar:wiremock-standalone-3.3.0.jar\" wiremock.Run\n```\n\n### Docker\n\nUsing the extension with docker is similar to its usage with usage [standalone](#standalone): it just has to be available on\nthe classpath to be loaded automatically - it does not have to be added via `--extensions` .\n\n```bash\ndocker run -it --rm \\\n-p 8080:8080 \\\n--name wiremock \\\n-v $PWD/extensions:/var/wiremock/extensions \\\nwiremock/wiremock  \\\n-- --global-response-templating\n```\n\n## Record a state\n\nThe state is recorded in `serveEventListeners` of a stub. The following functionalities are provided:\n\n- `state` : stores a state in a context. Storing the state multiple times can be used to selectively overwrite existing properties.\n    - to delete a selective property, set it to `null` (as string).\n- `list` : stores a state in a list. Can be used to prepend/append new states to an existing list. List elements cannot be modified (only read/deleted).\n\n`state` and `list` can be used in the same `ServeEventListener` (would count as ONE updates). Adding multiple `recordState` `ServeEventListener` is supported.\n\nThe following parameters have to be provided:\n\n\u003ctable\u003e\n\u003ctr\u003e\n\u003cth\u003eParameter\u003c/th\u003e\n\u003cth\u003eType\u003c/th\u003e\n\u003cth\u003eExample\u003c/th\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\n\n`context`\n\n\u003c/td\u003e\n\u003ctd\u003eString\u003c/td\u003e\n\u003ctd\u003e\n\n- `\"context\": \"{{jsonPath response.body '$.id'}}\"`\n- `\"context\": \"{{request.pathSegments.[3]}}\"`\n\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\n\n`state`\n\n\u003c/td\u003e\n\u003ctd\u003eObject\u003c/td\u003e\n\u003ctd\u003e\n\n```json\n{\n  \"id\": \"{{jsonPath response.body '$.id'}}\",\n  \"firstName\": \"{{jsonPath request.body '$.firstName'}}\",\n  \"lastName\": \"{{jsonPath request.body '$.lastName'}}\"\n}\n  ```\n\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\n\n`list`\n\n\u003c/td\u003e\n\u003ctd\u003e\nDictionary\n\n- `addLast` : Adds the object to the end of the list\n- `addFirst` : Adds the object to the front of the list\n\n\u003c/td\u003e\n\u003ctd\u003e\n\n```json\n{\n  \"addLast\": {\n    \"id\": \"{{jsonPath response.body '$.id'}}\",\n    \"firstName\": \"{{jsonPath request.body '$.firstName'}}\",\n    \"lastName\": \"{{jsonPath request.body '$.lastName'}}\"\n  }\n}\n  ```\n\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/table\u003e\n\nTemplating (as in [Response Templating](https://wiremock.org/docs/response-templating/)) is supported for these. The following models are exposed:\n\n- `request`: All model elements of as in [Response Templating](https://wiremock.org/docs/response-templating/)\n- `response`: `body` and `headers`\n\nFull example for storing a state:\n\n```json\n{\n  \"request\": {},\n  \"response\": {},\n  \"serveEventListeners\": [\n    {\n      \"name\": \"recordState\",\n      \"parameters\": {\n        \"context\": \"{{jsonPath response.body '$.id'}}\",\n        \"state\": {\n          \"id\": \"{{jsonPath response.body '$.id'}}\",\n          \"firstName\": \"{{jsonPath request.body '$.firstName'}}\",\n          \"lastName\": \"{{jsonPath request.body '$.lastName'}}\"\n        }\n      }\n    }\n  ]\n}\n```\n\nTo record a complete response body, use (**ATTENTION**: tripple `{{{`):\n\n```json\n{\n  \"request\": {},\n  \"response\": {},\n  \"serveEventListeners\": [\n    {\n      \"name\": \"recordState\",\n      \"parameters\": {\n        \"context\": \"{{jsonPath response.body '$.id'}}\",\n        \"state\": {\n          \"fullBody\": \"{{{jsonPath response.body '$'}}}\"\n        }\n      }\n    }\n  ]\n}\n```\n\nTo delete a selective property, ensure that the field has the value `null` as string, e.g. by specifying `default='null` for `jsonpath`:\n\n```json\n{\n  \"request\": {},\n  \"response\": {},\n  \"serveEventListeners\": [\n    {\n      \"name\": \"recordState\",\n      \"parameters\": {\n        \"context\": \"{{jsonPath response.body '$.id'}}\",\n        \"state\": {\n          \"id\": \"{{jsonPath response.body '$.id'}}\",\n          \"firstName\": \"{{jsonPath request.body '$.firstName' default='null'}}\",\n          \"lastName\": \"{{jsonPath request.body '$.lastName' default='null'}}\"\n        }\n      }\n    }\n  ]\n}\n```\n\nTo append a state to a list:\n\n```json\n{\n  \"request\": {},\n  \"response\": {},\n  \"serveEventListeners\": [\n    {\n      \"name\": \"recordState\",\n      \"parameters\": {\n        \"context\": \"{{jsonPath response.body '$.id'}}\",\n        \"list\": {\n          \"addLast\": {\n            \"id\": \"{{jsonPath response.body '$.id'}}\",\n            \"firstName\": \"{{jsonPath request.body '$.firstName'}}\",\n            \"lastName\": \"{{jsonPath request.body '$.lastName'}}\"\n          }\n        }\n      }\n    }\n  ]\n}\n```\n\n### Accessing the previous state\n\nYou can use the `state` helper to temporarily access the previous state. Use the `state` helper in the same way as you would use it when\nyou [retrieve a state](#retrieve-a-state).\n\n**Note:** This extension does not keep a history in itself but it's an effect of the evaluation order.\nAs templates are evaluated before the state is written, the state you access in `recordState` is the one before you store the new one\n(so there might be none - you might want to use `default` for these cases). In case you have multiple `recordState` `serveEventListeners`, you will have new\nstates\nbeing created in between, thus the previous state is the last stored one (so: not the one before the request).\n\n1. listener 1 is executed\n    1. accesses state n\n    2. stores state n+1\n2. listener 2 is executed\n    1. accesses state n+1\n    2. stores state n+2\n\nThe evaluation order of listeners within a stub as well as across stubs is not guaranteed.\n\n```json\n{\n  \"request\": {},\n  \"response\": {},\n  \"serveEventListeners\": [\n    {\n      \"name\": \"recordState\",\n      \"parameters\": {\n        \"context\": \"{{jsonPath response.body '$.id'}}\",\n        \"state\": {\n          \"id\": \"{{jsonPath response.body '$.id'}}\",\n          \"firstName\": \"{{jsonPath request.body '$.firstName'}}\",\n          \"lastName\": \"{{jsonPath request.body '$.lastName'}}\",\n          \"birthName\": \"{{state context='$.id' property='lastName' default=''}}\"\n        }\n      }\n    }\n  ]\n}\n```\n\n## Deleting a state\n\nSimilar to recording a state, its deletion can be initiated in  `serveEventListeners` of a stub.\n\nThe following parameters have to be provided:\n\n\u003ctable\u003e\n\u003ctr\u003e\n\u003cth\u003eTask\u003c/th\u003e\n\u003cth\u003eParameter\u003c/th\u003e\n\u003cth\u003eType\u003c/th\u003e\n\u003cth\u003eExample\u003c/th\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd rowspan=\"3\"\u003e\ncontext deletion\n\u003c/td\u003e\n\u003ctd\u003e\n\n`context`\u003cbr\u003e\nDeletes a single context.\n\n\u003c/td\u003e\n\u003ctd\u003eString\u003c/td\u003e\n\u003ctd\u003e\n\n- `\"context\": \"{{jsonPath response.body '$.id'}}\"`\n- `\"context\": \"{{request.pathSegments.[3]}}\"`\n\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\n\n`contexts`\nDeletes all contexts specified in the array.\n\n\u003c/td\u003e\n\u003ctd\u003eArray\u003cbr\u003e\nAn empty array or unknown contexts are silently ignored.\n\u003c/td\u003e\n\u003ctd\u003e\n\n- `\"contexts\": [\"{{jsonPath response.body '$.firstContext'}}\", \"{{jsonPath response.body '$.secondContext'}}\"]`\n- `\"contexts\": [\"a\", \"b\", \"c\"]`\n\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\n\n`contextsMatching`\nDeletes all contexts matching the regex.\n\u003c/td\u003e\n\u003ctd\u003eString (regex)\u003cbr\u003e\nAn invalid regex results in an exception. If there are no matches, this is silently ignored.\n\u003c/td\u003e\n\u003ctd\u003e\n\n- `\"contextsMatching\": \".*userNa.*\"`\n- `\"contextsMatching\": \".*(john|jane).*\"`\n- `\"contextsMatching\": \".*\"` (delete all contexts)\n\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003eList entry deletion\u003c/td\u003e\n\u003ctd\u003e\n\n- `context` (string): the context to delete the list entry from\n- `list` (dictionary, see next column)\n\nIf `list` is specified and `context` is missing, an error is thrown. \n\u003c/td\u003e\n\u003ctd\u003e\nDictionary - only one option is interpreted (top to bottom as listed here)\n\n- `deleteFirst` (Boolean) - deletes first element in the list\n- `deleteLast` (Boolean) - deletes last element in the list\n- `deleteIndex` (Number as String) - deletes element at index (starting with `0` - last element = `-1`).\n  Number has to be represented as String. Supports templating.\n- `deleteWhere` (Object with `property` and `value`) - Deletes first element matching the condition.\n  Both `property` and `value` support templating.\n\n\u003c/td\u003e\n\u003ctd\u003e\n\n- ```json\n  { \n    \"name\": \"deleteState\",\n    \"parameters\": {\n      \"list\": {\n        \"deleteFirst\": true\n      }\n    }\n  }\n  ```\n- ```json\n  { \n    \"name\": \"deleteState\",\n    \"parameters\": {\n      \"list\": {\n        \"deleteLast\": true\n      }\n    }\n  }\n  ```\n- ```json\n  { \n    \"name\": \"deleteState\",\n    \"parameters\": {\n      \"list\": {\n        \"deleteIndex\": \"1\"\n      }\n    }\n  }\n  ```\n- ```json\n  { \n    \"name\": \"deleteState\",\n    \"parameters\": {\n      \"list\": {\n        \"deleteIndex\": \"-1\"\n      }\n    }\n  }\n  ```\n- ```json\n  { \n    \"name\": \"deleteState\",\n    \"parameters\": {\n      \"list\": {\n        \"deleteIndex\": \"{{request.pathSegments.[1]}}\"\n      }\n    }\n  }\n  ```\n- ```json\n  { \n    \"name\": \"deleteState\",\n    \"parameters\": {\n      \"list\": {\n        \"deleteWhere\": {\n          \"property\": \"myProperty\",\n          \"value\": \"{{request.pathSegments.[2]}}\"\n        }\n      }\n    }\n  }\n  ```\n\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/table\u003e\n\nTemplating (as in [Response Templating](https://wiremock.org/docs/response-templating/)) is supported for these. The following models are exposed:\n\n- `request`: All model elements of as in [Response Templating](https://wiremock.org/docs/response-templating/)\n- `response`: `body` and `headers`\n\nFull example:\n\n```json\n{\n  \"request\": {},\n  \"response\": {},\n  \"serveEventListeners\": [\n    {\n      \"name\": \"deleteState\",\n      \"parameters\": {\n        \"context\": \"{{jsonPath response.body '$.id'}}\"\n      }\n    }\n  ]\n}\n\n```\n\n### state expiration\n\nThis extension provides a `CaffeineStore` which uses [caffeine](https://github.com/ben-manes/caffeine) to store the current state and to achieve an expiration (\nto avoid memory leaks).\nThe default expiration is 60 minutes. The default value can be overwritten (`0` = default = 60 minutes):\n\n```java\nint expiration = 1024;\nvar store = new CaffeineStore(expiration);\n```\n\n## Match a request against a context\n\nTo have a WireMock stub only apply when there's actually a matching context, you can use the `StateRequestMatcher` . This helps to model different\nbehavior for requests with and without a matching context. The parameter supports templates.\n\n### Positive context exists match\n\n```json\n{\n  \"request\": {\n    \"method\": \"GET\",\n    \"urlPattern\": \"/test/[^\\/]+\",\n    \"customMatcher\": {\n      \"name\": \"state-matcher\",\n      \"parameters\": {\n        \"hasContext\": \"{{request.pathSegments.[1]}}\"\n      }\n    }\n  },\n  \"response\": {\n    \"status\": 200\n  }\n}\n```\n\n### Property existence match\n\nIn addition to the existence of a context, you can check for the existence or absence of a property\nwithin that context. The following matchers are available:\n\n- `hasProperty`\n- `hasNotProperty`\n\nAs for other matchers, templating is supported.\n\n```json\n{\n  \"request\": {\n    \"method\": \"GET\",\n    \"urlPattern\": \"/test/[^\\/]+/[^\\/]+\",\n    \"customMatcher\": {\n      \"name\": \"state-matcher\",\n      \"parameters\": {\n        \"hasContext\": \"{{request.pathSegments.[1]}}\",\n        \"hasProperty\": \"{{request.pathSegments.[2]}}\"\n      }\n    }\n  },\n  \"response\": {\n    \"status\": 200\n  }\n}\n```\n\n### Full flexible property match\n\nIn case you want full flexibility into matching on a property, you can simply specify `property` and use one of WireMock's built-in matchers, allowing you to\nconfigure\nlogical operators, regex, date matchers, absence and much more. The basic syntax:\n\n```json\n\"property\": {\n\u003cproperty-a\u003e: \u003cmatcher-a\u003e,\n\u003cproperty-b\u003e: \u003cmatcher-b\u003e\n}\n```\n\nExample:\n\n```json\n{\n  \"request\": {\n    \"method\": \"GET\",\n    \"urlPattern\": \"/test/[^\\/]+/[^\\/]+\",\n    \"customMatcher\": {\n      \"name\": \"state-matcher\",\n      \"parameters\": {\n        \"property\": {\n          \"myProperty\": {\n            \"contains\": \"myValue\"\n          }\n        }\n      }\n    },\n    \"response\": {\n      \"status\": 200\n    }\n  }\n```\n\nThe implementation makes use of WireMock's internal matching system and supports any implementation of `StringValuePattern`. As of WireMock 3.3, this includes\n`equalTo`,`equalToJson`,`matchesJsonPath`,`matchesJsonSchema`,`equalToXml`,`matchesXPath`,`contains`,`not`,`doesNotContain`,`matches`,`doesNotMatch`,`before`,\n`after`,`equalToDateTime`,`anything`,`absent`,`and`,`or`,`matchesPathTemplate`.\nFor documentation on using these matchers, check the [WireMock documentation](https://wiremock.org/docs/request-matching/)\n\n### Context update count match\n\nWhenever a request with a serve event listener `recordState` or `deleteState` is processed, the internal context update counter is increased.\nThe update count is increased by one whenever there is at least one change to a context (so: property adding/change, list entry addition/deletion). Multiple\nevent listeners with multiple changes of a single context within a single request only result in an increase by one.\nfor request matching as well. The following matchers are available:\n\n- `updateCountEqualTo`\n- `updateCountLessThan`\n- `updateCountMoreThan`\n\nAs for other matchers, templating is supported. In case the provided value for this check is not numeric, it is handled as non-matching. No error will be\nreported or logged.\n\n```json\n{\n  \"request\": {\n    \"method\": \"GET\",\n    \"urlPattern\": \"/test/[^\\/]+\",\n    \"customMatcher\": {\n      \"name\": \"state-matcher\",\n      \"parameters\": {\n        \"hasContext\": \"{{request.pathSegments.[1]}}\",\n        \"updateCountEqualTo\": \"1\"\n      }\n    }\n  },\n  \"response\": {\n    \"status\": 200\n  }\n}\n```\n\n### List size match\n\nThe list size (which is modified via `recordState` or `deleteState`)  can be used\nfor request matching as well. The following matchers are available:\n\n- `listSizeEqualTo`\n- `listSizeLessThan`\n- `listSizeMoreThan`\n\nAs for other matchers, templating is supported. In case the provided value for this check is not numeric, it is handled as non-matching. No error will be\nreported or logged.\n\n```json\n{\n  \"request\": {\n    \"method\": \"GET\",\n    \"urlPattern\": \"/test/[^\\/]+\",\n    \"customMatcher\": {\n      \"name\": \"state-matcher\",\n      \"parameters\": {\n        \"hasContext\": \"{{request.pathSegments.[1]}}\",\n        \"listSizeEqualTo\": \"1\"\n      }\n    }\n  },\n  \"response\": {\n    \"status\": 200\n  }\n}\n```\n\n### Full flexible list entry property match\n\nSimilar to properties, you have full flexibility into matching on a property of a list entry by specifying `list` and using one of WireMock's built-in matchers\nThe basic syntax:\n\n```json\n\"list\": {\n  \u003cindex-a\u003e: {\n    \u003cproperty-a\u003e: \u003cmatcher-a\u003e,\n    \u003cproperty-b\u003e: \u003cmatcher-b\u003e\n  },\n  \u003cindex-b\u003e: {\n    \u003cproperty-a\u003e: \u003cmatcher-a\u003e,\n    \u003cproperty-b\u003e: \u003cmatcher-b\u003e\n  }\n}\n```\n\nAs index, you can use the actual index as well as `first`, `last`, `-1`.\n\nExample:\n\n```json\n{\n  \"request\": {\n    \"method\": \"GET\",\n    \"urlPattern\": \"/test/[^\\/]+/[^\\/]+\",\n    \"customMatcher\": {\n      \"name\": \"state-matcher\",\n      \"parameters\": {\n        \"list\": {\n          \"1\": {\n            \"myProperty\": {\n              \"contains\": \"myValue\"\n            }\n          }\n        }\n      }\n    },\n    \"response\": {\n      \"status\": 200\n    }\n  }\n```\n\nThe implementation makes use of WireMock's internal matching system and supports any implementation of `StringValuePattern`. As of WireMock 3.3, this includes\n`equalTo`,`equalToJson`,`matchesJsonPath`,`matchesJsonSchema`,`equalToXml`,`matchesXPath`,`contains`,`not`,`doesNotContain`,`matches`,`doesNotMatch`,`before`,\n`after`,`equalToDateTime`,`anything`,`absent`,`and`,`or`,`matchesPathTemplate`.\nFor documentation on using these matchers, check the [WireMock documentation](https://wiremock.org/docs/request-matching/)\n\n\n### Negative context exists match\n\n```json\n{\n  \"request\": {\n    \"method\": \"GET\",\n    \"urlPattern\": \"/test/[^\\/]+\",\n    \"customMatcher\": {\n      \"name\": \"state-matcher\",\n      \"parameters\": {\n        \"hasNotContext\": \"{{request.pathSegments.[1]}}\"\n      }\n    }\n  },\n  \"response\": {\n    \"status\": 400\n  }\n}\n```\n\n## Retrieve a state\n\nA state can be retrieved using a handlebar helper. In the example above, the `StateHelper` is registered by the name `state`.\nIn a `jsonBody`, the state can be retrieved via: `\"clientId\": \"{{state context=request.pathSegments.[1] property='firstname'}}\",`\n\nThe handler has the following parameters:\n\n- `context`:  has to match the context data was registered with\n- `property`: the property of the state context to retrieve, so e.g. `firstName`\n    - `property='updateCount` retrieves the number of updates to a certain state.\n      The number matches the one described in [Context update count match](#context-update-count-match)\n    - `property='listSize` retrieves the number of entries of `list`\n    - `property='list` get the whole list as array, e.g. to use it with [handlebars #each](https://handlebarsjs.com/guide/builtin-helpers.html#each)\n        - this property always has a default value (empty list), which can be overwritten with a JSON list\n- `list`: Getting an entry of the context's `list`, identified via a JSON path. Examples:\n    - getting the first state in the list: `list='[0].myProperty`\n    - getting the last state in the list: `list='[-1].myProperty`\n    - getting an element based on a path segment:: `list=(join '[' request.pathSegments.[1] '].myProperty' '')`\n- `default` (Optional): value to return in case the context or property wasn't found. Without a default value, an error message would be returned instead.\n\nYou have to choose either `property` or `list` (otherwise, you will get a configuration error).\n\nTo retrieve a full body, use tripple braces: `{{{state context=request.pathSegments.[1] property='fullBody'}}}` .\n\nWhen registering this extension, this helper is available via WireMock's [response templating](https://wiremock.org/3.x/docs/response-templating/) as well as\nin all configuration options of this extension.\n\n### List operations\n\nYou can use [handlebars #each](https://handlebarsjs.com/guide/builtin-helpers.html#each) to build a full JSON response with the current list's content.\n\nThings to consider:\n\n- this syntax only works with `body`. It DOES NOT work with `jsonBody`\n    - as this might get ugly, consider using `bodyFileName` / `withBodyFile()` have proper indentation\n- the default response for non-existent context as well as non-existent list in a context is an empty list. These states cannot be differentiated here\n    - if you still want a different response, consider using a [StateRequestMatcher](#negative-context-exists-match)\n- the default value for this property has to be a valid JSON list - otherwise you will get an error log and the empty list response\n- JSON does not allow trailing commas, so in order to create a valid JSON list, use `{{#unless @last}},{{/unless}` before `{{/each}}`\n\nExample with inline body:\n\n```json\n{\n  \"request\": {\n    \"urlPathPattern\": \"/listing\",\n    \"method\": \"GET\"\n  },\n  \"response\": {\n    \"status\": 200,\n    \"body\": \"[\\n{{# each (state context='list' property='list' default='[]') }}  {\\n    \\\"id\\\": \\\"{{id}}\\\",\\n    \\\"firstName\\\": \\\"{{firstName}}\\\",\\n    \\\"lastName\\\": \\\"{{lastName}}\\\"  }{{#unless @last}},{{/unless}}\\n{{/each}}]\",\n    \"headers\": {\n      \"content-type\": \"application/json\"\n    }\n  }\n}\n```\n\nExample with bodyFileName:\n\n```json\n{\n  \"request\": {\n    \"urlPathPattern\": \"/listing\",\n    \"method\": \"GET\"\n  },\n  \"response\": {\n    \"status\": 200,\n    \"bodyFileName\": \"body.json\",\n    \"headers\": {\n      \"content-type\": \"application/json\"\n    }\n  }\n}\n```\n\n```json\n[\n  {{# each (state context='list' property='list' default='[]') }}  \n  {\n    \"id\": {{id}},    \n    \"firstName\": \"{{firstName}}\",   \n    \"lastName\": \"{{lastName}}\"\n  }{{#unless @last}},{{/unless}}\n  {{/each}}\n]\n```\n\n### Missing properties and defaults\n\nMissing Helper properties as well as unknown context properties result in using a built-in default.\n\nYou can also specify a `default` for the state\nhelper: `\"clientId\": \"{{state context=request.pathSegments.[1] property='firstname' default='John'}}\",` .\n\nIf unsure, you may consult the log for to see whether an error occurred.\n\nProperties and their defaults:\n\n| Property                                 | Built-in                                 | Interprets `default`                     |\n|------------------------------------------|------------------------------------------|------------------------------------------|\n| `updateCount`                            | `\"0\"` (0 as string)                      | yes                                      |\n| `listSize` (when context is not present) | `\"0\"` (0 as string)                      | yes                                      |\n| `listSize` (when context is present)     | not applied as list is present but empty | not applied as list is present but empty |\n| `list` (when context is not present)     | `[]` (empty list)                        | yes                                      |\n| `list` (when context is present)         | not applied as list is present but empty | not applied as list is present but empty |\n| any other state property                 | `\"\"` (empty string)                      | yes                                      |\n| any other list property                  | `\"\"` (empty string)                      | yes                                      |\n\nDefaults have to be strings or valid objects in order to result in proper JSONs in all configuration scenarios. In order to create\na JSON response with a `null` property or to ignore unknown properties in your resulting JSON, you may consider using a body file\nwith handlebar logic to create the JSON you need: handlebar [interprets](https://handlebarsjs.com/guide/builtin-helpers.html#if) an empty string as `false`.\n\nbody file with handlebars to create `myProperty=null`:\n\n```\n{\n{{#with (state context=request.pathSegments.[1] property='myProperty') as | value |}}\n\"myProperty\": \"{{value}}\"\n{{else}}\n\"myProperty\": null\n{{/with}}\n}\n```\n\nbody file with handlebars to ignore a missing property:\n\n```\n{\n{{#with (state context=request.pathSegments.[1] property='myProperty') as | value |}}\n\"myProperty\": \"{{value}}\"\n{{else}}\n{{/with}}\n}\n```\n\n# Distributed setups and concurrency\n\nThis extension is at the moment not optimized for distributed setups or high degrees concurrency. While it will basically work, there are some limitations\nthat should be held into account:\n\n- The store used for storing the state is on instance-level only\n  - while it can be exchanged for a distributed store, any atomicity assurance on instance level is not replicated to the distributed setup. Thus concurrent operations on different instances might result in state overwrites\n- Lock-level is basically the whole context store\n  - while the lock time is kept small, this can still impact measurements when being used in load tests\n- Single updates to contexts (property additions or changes, list entry additions or deletions) are atomic on instance level\n- Concurrent requests are currently allowed to change the same context. Atomicity prevents overwrites but does not provide something like a transaction, so: the context can change while a request is performed\n\nFor any kind of usage with parallel write requests, it's recommended to use a different context for each parallel stream.\n\n# Debugging\n\nIn general, you can increase verbosity, either by [register a notifier](https://wiremock.org/3.x/docs/configuration/#notification-logging)\nand setting `verbose=true` or starting WireMock standalone (or docker) with `verbose=true`.\n\n- EventListeners and Matchers report errors with WireMock-internal exceptions. Additionally, errors are logged.\n  In order to see them, [register a notifier](https://wiremock.org/3.x/docs/configuration/#notification-logging).\n- Response templating errors are printed in the actual response body.\n- Various actions and decisions of this extensions are logged on info level, along with the context they are happening in.\n\n# Examples\n\nVarious test examples can be found in the [tests](src/test/java/org/wiremock/extensions/state/examples) of this extension.\n\nJSON stub mapping can be found in the resource files of the [tests](src/test/resources) .\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwiremock%2Fwiremock-state-extension","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwiremock%2Fwiremock-state-extension","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwiremock%2Fwiremock-state-extension/lists"}