{"id":21663945,"url":"https://github.com/leviysoft/mockingbird","last_synced_at":"2026-03-01T18:02:42.643Z","repository":{"id":190175623,"uuid":"682081435","full_name":"leviysoft/mockingbird","owner":"leviysoft","description":"Flexible mock server","archived":false,"fork":false,"pushed_at":"2026-02-14T17:44:45.000Z","size":10981,"stargazers_count":17,"open_issues_count":5,"forks_count":3,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-02-14T19:26:42.429Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Scala","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/leviysoft.png","metadata":{"files":{"readme":"readme.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":"CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2023-08-23T11:56:08.000Z","updated_at":"2026-02-14T17:44:49.000Z","dependencies_parsed_at":"2023-10-12T05:28:54.373Z","dependency_job_id":"9d1d391a-5a54-4702-9a58-42f4b1425378","html_url":"https://github.com/leviysoft/mockingbird","commit_stats":null,"previous_names":["leviysoft/mockingbird"],"tags_count":36,"template":false,"template_full_name":null,"purl":"pkg:github/leviysoft/mockingbird","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/leviysoft%2Fmockingbird","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/leviysoft%2Fmockingbird/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/leviysoft%2Fmockingbird/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/leviysoft%2Fmockingbird/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/leviysoft","download_url":"https://codeload.github.com/leviysoft/mockingbird/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/leviysoft%2Fmockingbird/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29977966,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-01T16:35:47.903Z","status":"ssl_error","status_checked_at":"2026-03-01T16:35:44.899Z","response_time":124,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":[],"created_at":"2024-11-25T10:29:36.634Z","updated_at":"2026-03-01T18:02:42.628Z","avatar_url":"https://github.com/leviysoft.png","language":"Scala","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cimg align=\"left\" src=\"img/mascot.png\" height=\"150px\" style=\"padding-right: 50px\"/\u003e\r\n\r\n# mockingbird\r\n\r\nmockingbird - a service for emulating REST services and queue-interface services\r\n\r\n[Installation Guide](deployment.md)\r\n\r\n[Configuration Guide](configuration.md)\r\n\r\n[Working with Message Brokers](message-brokers.md)\r\n\r\n[Working with gRPC](grpc-configuration.md)\r\n\r\n[Readme in Russian](readme_ru.md)\r\n\r\n## Important note\r\n\r\nLeviysoft mockingbird is an independently maintained fork of Tinkoff/mockingbird and is not related to Tinkoff in any kind.\r\n\r\n## General Principles of Operation\r\n\r\nmockingbird supports the following scenarios:\r\n\r\n* Execution of a specific case with a specific set of events and HTTP/GRPC responses\r\n* Constant emulation of a happy-path to ensure autonomy of the stage environment(s)\r\n\r\nTypes of configurations:\r\n* countdown - standalone configurations for testing a specific scenario. They have the highest priority when resolving ambiguities. Each mock is triggered n times (the number is set during creation). Automatically deleted at midnight.\r\n* ephemeral - configurations that are automatically deleted at midnight. If a method/message is called/arrives simultaneously, for which both countdown and ephemeral mocks are suitable - countdown will be triggered.\r\n* persistent - configuration intended for continuous operation. Has the lowest priority\r\n\r\n## Services\r\n\r\nTo organize mocks in the UI and minimize the number of conflict situations, so-called services are implemented in mockingbird. Each mock (both HTTP and scenario) always belongs to one of the services.\r\nServices are created in advance and stored in the database. A service has a suffix (which also serves as the unique service id) and a human-readable name.\r\n\r\n## JSON Templating\r\n\r\nTo achieve flexibility while maintaining the relative simplicity of configurations, a JSON templating feature is implemented in the service. To start, here's a simple example:\r\n\r\nTemplate:\r\n```javascript\r\n{\r\n  \"description\": \"${description}\",\r\n  \"topic\": \"${extras.topic}\",\r\n  \"comment\": \"${extras.comments.[0].text}\",\r\n  \"meta\": {\r\n    \"field1\": \"${extras.fields.[0]}\"\r\n  }\r\n}\r\n```\r\n\r\nValues for substitution:\r\n```javascript\r\n{\r\n  \"description\": \"Some description\",\r\n  \"extras\": {\r\n    \"fields\": [\"f1\", \"f2\"],\r\n    \"topic\": \"Main topic\",\r\n    \"comments\": [\r\n      {\"text\": \"First nah!\"}, {\"text\": \"Okay\"}\r\n    ]\r\n  }\r\n}\r\n```\r\n\r\nResult:\r\n```javascript\r\n{\r\n  \"description\": \"Some description\",\r\n  \"topic\": \"Main topic\",\r\n  \"comment\": \"First nah!\",\r\n  \"meta\": {\r\n    \"field1\": \"f1\"\r\n  }\r\n}\r\n```\r\n\r\nCurrently, the following syntax is supported:\r\n* `${a.[0].b}` - value substitution (JSON)\r\n* `${/a/b/c}` - value substitution (XPath)\r\n\r\nWARNING! DO NOT USE NAMESPACES IN XPATH EXPRESSIONS\r\n\r\n## XML Templating\r\n\r\nTemplate:\r\n```\r\n\u003croot\u003e\r\n    \u003ctag1\u003e${/r/t1}\u003c/tag1\u003e\r\n    \u003ctag2 a2=\"${/r/t2/@a2}\"\u003e${/r/t2}\u003c/tag2\u003e\r\n\u003c/root\u003e\r\n```\r\n\r\nValues for substitution:\r\n```\r\n\u003cr\u003e\r\n    \u003ct1\u003etest\u003c/t1\u003e\r\n    \u003ct2 a2=\"attr2\"\u003e42\u003c/t2\u003e\r\n\u003c/r\u003e\r\n```\r\n\r\nResult:\r\n```\r\n\u003croot\u003e\r\n    \u003ctag1\u003etest\u003c/tag1\u003e\r\n    \u003ctag2 a2=\"attr2\"\u003e42\u003c/tag2\u003e\r\n\u003c/root\u003e\r\n```\r\n\r\n## States (state)\r\n\r\nTo support complex scenarios, the service supports saving arbitrary states. A state is a document with an arbitrary schema, technically a state is a document in MongoDB. Writing new states can occur:\r\n* when writing to state (the persist section) with an empty (or missing) predicate (the state section)\r\n\r\n## State Manipulations\r\n\r\nState is cumulatively appended. Overwriting fields is allowed.\r\n\r\nFields used for searching (used in predicates) must start with \"_\".\r\n\u003e a sparse index will be automatically created for such fields\r\n\r\nPrefixes:\r\n* `seed` - values from the seed block (randomized at the start of the application)\r\n* `state` - the current state\r\n* `req` - the request body (modes json, jlens, xpath)\r\n* `message` - the message body (in scenarios)\r\n* `query` - query parameters (in stubs)\r\n* `pathParts` - values extracted from the URL (in stubs) see `Data Extraction from URL`\r\n* `extracted` - extracted values\r\n* `headers` - HTTP headers\r\n\r\n```javascript\r\n{\r\n  \"a\": \"Just a string\", //The field \"a\" is assigned a constant (can be any JSON value)\r\n  \"b\": \"${req.fieldB}\", //The field \"b\" is assigned the value from the fieldB of the request\r\n  \"c\": \"${state.c}\", //The field \"c\" is assigned the value from the \"c\" field of the current state\r\n  \"d\": \"${req.fieldA}: ${state.a}\" //The field d will contain a string consisting of req.fieldA and state.a\r\n}\r\n```\r\n\r\n## State Search\r\n\r\nPredicates for state search are listed in the `state` block. An empty object (`{}`) in the state field is not allowed.\r\nFor state search, request data (without prefix), query parameters (prefix `__query`), values extracted from the URL (prefix `__segments`), and HTTP headers (prefix `__headers`) can be used\r\n\r\nExample:\r\n\r\n```javascript\r\n{\r\n  \"_a\": \"${fieldB}\", //field from the request body\r\n  \"_b\": \"${__query.arg1}\", //query parameter\r\n  \"_c\": \"${__segments.id}\", //URL segment, see `Data Extraction from URL`\r\n  \"_d\": \"${__headers.Accept}\" //HTTP header\r\n}\r\n```\r\n\r\n## Seeding\r\n\r\nSometimes there is a need to generate a random value and save and/or return it as a result of the mock's operation.\r\nTo support such scenarios, a seed field is provided, allowing to set variables that will be generated\r\nat the mock's initialization. This avoids the need to recreate mocks with hardcoded ids\r\n\r\nJavaScript evaluation is supported in seeds. The following functions are defined for backwards compatibility with \"pseudofunctions\":\r\n* `%{randomString(n)}` - substitution of a random string of length n\r\n* `%{randomString(\"ABCDEF1234567890\", m, n)}` - substitution of a random string consisting of `ABCDEF1234567890` characters in the range [m, n)\r\n* `%{randomNumericString(n)}` - substitution of a random string consisting only of digits, of length n\r\n* `%{randomInt(n)}` - substitution of a random Int in the range [0, n)\r\n* `%{randomInt(m,n)}` - substitution of a random Int in the range [m, n)\r\n* `%{randomLong(n)}` - substitution of a random Long in the range [0, n)\r\n* `%{randomLong(m,n)}` - substitution of a random Long in the range [m, n)\r\n* `%{UUID}` - substitution of a random UUID\r\n* `%{now(yyyy-MM-dd'T'HH:mm:ss)}` - the current time in the specified [format](https://docs.oracle.com/javase/8/docs/api/java/time/format/DateTimeFormatter.html)\r\n* `%{today(yyyy-MM-dd)}` - the current date in the specified [format](https://docs.oracle.com/javase/8/docs/api/java/time/format/DateTimeFormatter.html)\r\n\r\nComplex formatted strings can be defined: `%{randomInt(10)}: %{randomLong(10)} | %{randomString(12)}`, all pseudo-functions from the list above are supported\r\n\r\n## Resolving Stubs/Scenarios\r\n\r\n\u003e Found stubs - candidates remaining after validation of URL, headers, and request body\r\n\u003e Found scenarios - candidates remaining after validation of the message body\r\n\r\n| Found Stubs (Scenarios)      | State Required      | States Found       | Result         |\r\n| ---------------------------- | ------------------- | ------------------ | -------------- |\r\n| №1                           | No                  | -                  | №1 is triggered |\r\n| №1                           | Yes                 | 0                  | Error          |\r\n| №1                           | Yes                 | 1                  | №1 is triggered |\r\n| №1\u003cbr\u003e№2                     | No\u003cbr\u003eNo            | -                  | Error          |\r\n| №1\u003cbr\u003e№2                     | No\u003cbr\u003eYes           | -\u003cbr\u003e0             | №1 is triggered |\r\n| №1\u003cbr\u003e№2                     | No\u003cbr\u003eYes           | -\u003cbr\u003e1             | №2 is triggered |\r\n| №1\u003cbr\u003e№2                     | No\u003cbr\u003eYes           | -\u003cbr\u003e2 (and more)  | Error          |\r\n| №1\u003cbr\u003e№2                     | Yes\u003cbr\u003eYes          | 0\u003cbr\u003e0             | Error          |\r\n| №1\u003cbr\u003e№2                     | Yes\u003cbr\u003eYes          | 0\u003cbr\u003e1             | №2 is triggered |\r\n| №1\u003cbr\u003e№2                     | Yes\u003cbr\u003eYes          | 0\u003cbr\u003e2 (and more)  | Error          |\r\n| №1\u003cbr\u003e№2                     | Yes\u003cbr\u003eYes          | 1\u003cbr\u003e1 (and more)  | Error          |\r\n| №1\u003cbr\u003e№2\u003cbr\u003e№3               | Yes\u003cbr\u003eYes\u003cbr\u003eYes   | 0\u003cbr\u003e1\u003cbr\u003e0        | №2 is triggered |  \r\n| №1\u003cbr\u003e№2\u003cbr\u003e№3               | Yes\u003cbr\u003eYes\u003cbr\u003eYes   | 0\u003cbr\u003e1\u003cbr\u003e1        | Error |    \r\n| №1\u003cbr\u003e№2\u003cbr\u003e№3               | Yes\u003cbr\u003eYes\u003cbr\u003eYes   | 0\u003cbr\u003e2\u003cbr\u003e0        | Error |           \r\n\r\n## Emulating REST Services\r\n\r\nWorkflow:\r\n1. Search for a mock by URL/HTTP-verb/headers\r\n2. Body validation\r\n3. Search for state by predicate\r\n4. Substitution of values in the response template\r\n5. State modification\r\n6. Sending the response\r\n\r\n### Configuration of HTTP Stubs\r\n\r\nHTTP headers are validated for exact match values, extra headers are not considered an error\r\n\r\nRequest body validation in HTTP stubs can work in the following modes:\r\n* no_body - the request must be without a body\r\n* any_body - the request body must be non-empty, while it is not parsed or checked\r\n* raw - the request body is not parsed and is checked for full correspondence with the content of request.body\r\n* json - the request body must be a valid JSON and is checked for correspondence with the content of request.body\r\n* xml - the request body must be a valid XML and is checked for correspondence with the content of request.body\r\n* jlens - the request body must be a valid JSON and is validated according to conditions described in request.body\r\n* xpath - the request body must be a valid XML and is validated according to conditions described in request.body\r\n* web_form - the request body must be in x-www-form-urlencoded format and is validated according to conditions described in request.body\r\n* multipart - the request body must be in multipart/form-data format. Validation rules for parts are configured individually (see the section below)\r\n\r\nATTENTION! multipart requests must be made to a separate method -\r\n/api/mockingbird/execmp\r\n\r\nFor responses, the following modes are supported:\r\n* raw\r\n* json\r\n* xml\r\n* binary\r\n* proxy\r\n* json-proxy\r\n* xml-proxy\r\n* no_body\r\n\r\nThe `no_body` mode in the response is needed if the stub returns a 204 or 304 code. These codes are distinguished from others by the fact that they cannot have any body in the response, this behavior is described in [RFC 7231](https://datatracker.ietf.org/doc/html/rfc7231#section-6.3.5) and [RFC 7232](https://datatracker.ietf.org/doc/html/rfc7232#section-4.1). The `no_body` mode can also be used with other HTTP codes, but it is mandatory for the specified ones.\r\n\r\nRequest and response modes are completely independent of each other (you can configure a response in XML to a JSON request if desired, except for json-proxy and xml-proxy modes).\r\n\r\nIn the delay field, you can pass a correct FiniteDuration no longer than 30 seconds\r\n\r\n### Data Extraction from URL\r\nSometimes, a URL contains an identifier not as a parameter but as a direct part of the path. In such cases, it becomes impossible to describe a persistent stub due to the inability to have a full path match. This is where the `pathPattern` field comes in handy, into which a regex can be passed, and the path will be checked for a match against this regex. It should be noted that although the matching is done in MongoDB in an efficient manner, this feature should not be abused, and the `pathPattern` should not be used if matching by full equality is possible.\r\n\r\nExample:\r\n```javascript\r\n{\r\n  \"name\": \"Sample stub\",\r\n  \"scope\": \"persistent\",\r\n  \"pathPattern\": \"/pattern/(?\u003cid\u003e\\d+)\",\r\n  \"method\": \"GET\",\r\n  \"request\": {\r\n    \"headers\": {},\r\n    \"mode\": \"no_body\",\r\n    \"body\": {}\r\n  },\r\n  \"response\": {\r\n    \"code\": 200,\r\n    \"mode\": \"json\",\r\n    \"headers\": {\"Content-Type\":  \"application/json\"},\r\n    \"body\": {\"id\": \"${pathParts.id}\"}\r\n  }\r\n}\r\n```\r\nAnything that needs to be extracted from the path should be done with a _named_ group, and there can be as many groups as needed. Later on, these can be referred to through `pathParts.\u003cgroup_name\u003e`.\r\n\r\n### Extractors\r\nIn some cases, it's necessary to insert into the response data that cannot be extracted by simple means. For these purposes, extractors have been added.\r\n\r\n#### jcdata Extractor\r\n\r\nExtracts values from JSON located within CDATA.\r\n\r\nConfiguration:\r\n```javascript\r\n{\r\n  \"type\": \"jcdata\",\r\n  \"prefix\": \"/root/inner/tag\", // Path to the tag with CDATA\r\n  \"path\": \"path.to\" // Path to the desired value\r\n}\r\n```\r\n\r\n#### CDATA Inlining\r\nSometimes you have to deal with requests in which XML is nested inside CDATA. In such cases, you can inline the CDATA content using the `inlineCData` parameter (supported in `xpath` and `xml`).\r\n\r\n### Examples\r\n\r\n#### Exact Match, json Mode\r\n\r\n```javascript\r\n{\r\n    \"name\": \"Sample stub\",\r\n    \"method\": \"POST\",\r\n    \"path\": \"/pos-loans/api/cl/get_partner_lead_info\",\r\n    \"state\": {\r\n      // Predicates\r\n    },\r\n    \"request\": {\r\n        \"headers\": {\"Content-Type\": \"application/json\"},\r\n        \"mode\": \"json\",\r\n        \"body\": {\r\n            \"trace_id\": \"42\",\r\n            \"account_number\": \"228\"\r\n        }\r\n    },\r\n    \"persist\": {\r\n      // State modifications\r\n    },\r\n    \"response\": {\r\n        \"code\": 200,\r\n        \"mode\": \"json\",\r\n        \"body\": {\r\n            \"code\": 0,\r\n            \"credit_amount\": 802400,\r\n            \"credit_term\": 120,\r\n            \"interest_rate\": 13.9,\r\n            \"partnum\": \"CL3.15\"\r\n        },\r\n        \"headers\": {\"Content-Type\": \"application/json\"},\r\n        \"delay\": \"1 second\"\r\n    }\r\n}\r\n```\r\n\r\n#### Exact Match, raw Mode\r\n\r\n```javascript\r\n{\r\n    \"name\": \"Sample stub\",\r\n    \"method\": \"POST\",\r\n    \"path\": \"/pos-loans/api/evil/soap/service\",\r\n    \"state\": {\r\n      // Predicates\r\n    },\r\n    \"request\": {\r\n        \"headers\": {\"Content-Type\": \"application/xml\"},\r\n        \"mode\": \"raw\",\r\n        \"body\": \"\u003cxml\u003e\u003crequest type=\\\"rqt\\\"\u003e\u003c/request\u003e\u003c/xml\u003e\"\r\n    },\r\n    \"persist\": {\r\n      // State modifications\r\n    },\r\n    \"response\": {\r\n        \"code\": 200,\r\n        \"mode\": \"raw\",\r\n        \"body\": \"\u003cxml\u003e\u003cresponse type=\\\"rqt\\\"\u003e\u003c/response\u003e\u003c/xml\u003e\",\r\n        \"headers\": {\"Content-Type\": \"application/xml\"},\r\n        \"delay\": \"1 second\"\r\n    }\r\n}\r\n```\r\n\r\n#### Condition Validation, jlens Mode\r\n\r\n```javascript\r\n{\r\n    \"name\": \"Sample stub\",\r\n    \"method\": \"POST\",\r\n    \"path\": \"/pos-loans/api/cl/get_partner_lead_info\",\r\n    \"state\": {\r\n      // Predicates\r\n    },\r\n    \"request\": {\r\n        \"headers\": {\"Content-Type\": \"application/json\"},\r\n        \"mode\": \"jlens\",\r\n        \"body\": {\r\n            \"meta.id\": {\"==\": 42}\r\n        }\r\n    },\r\n    \"persist\": {\r\n      // State modifications\r\n    },\r\n    \"response\": {\r\n        \"code\": 200,\r\n        \"mode\": \"json\",\r\n        \"body\": {\r\n            \"code\": 0,\r\n            \"credit_amount\": 802400,\r\n            \"credit_term\": 120,\r\n            \"interest_rate\": 13.9,\r\n            \"partnum\": \"CL3.15\"\r\n        },\r\n        \"headers\": {\"Content-Type\": \"application/json\"},\r\n        \"delay\": \"1 second\"\r\n    }\r\n}\r\n```\r\n\r\n#### Condition Validation, xpath Mode\r\n\r\nWARNING! DO NOT USE NAMESPACES IN XPATH EXPRESSIONS\r\n\r\n```javascript\r\n{\r\n    \"name\": \"Sample stub\",\r\n    \"method\": \"POST\",\r\n    \"path\": \"/pos-loans/api/cl/get_partner_lead_info\",\r\n    \"state\": {\r\n      // Predicates\r\n    },\r\n    \"request\": {\r\n        \"headers\": {\"Content-Type\": \"application/xml\"},\r\n        \"mode\": \"xpath\",\r\n        \"body\": {\r\n            \"/payload/response/id\": {\"==\": 42}\r\n        },\r\n        \"extractors\": {\"name\": {...}, ...} //optional\r\n    },\r\n    \"persist\": {\r\n      // State modifications\r\n    },\r\n    \"response\": {\r\n        \"code\": 200,\r\n        \"mode\": \"raw\",\r\n        \"body\": \"\u003cxml\u003e\u003cresponse type=\\\"rst\\\"\u003e\u003c/response\u003e\u003c/xml\u003e\",\r\n        \"headers\": {\"Content-Type\": \"application/xml\"},\r\n        \"delay\": \"1 second\"\r\n    }\r\n}\r\n```\r\n\r\n#### Condition Validation, multipart Mode\r\n\r\nWARNING! multipart requests must be performed on a separate method -\r\n/api/mockingbird/execmp\r\n\r\nPart validation modes:\r\n* `any` - value is not validated\r\n* `raw` - exact match\r\n* `json` - exact match, value parsed as Json\r\n* `xml` - exact match, value parsed as XML\r\n* `urlencoded` - similar to `web_form` mode for validating the entire body\r\n* `jlens` - Json condition check\r\n* `xpath` - XML condition check\r\n\r\n```javascript\r\n{\r\n    \"name\": \"Sample stub\",\r\n    \"method\": \"POST\",\r\n    \"path\": \"/test/multipart\",\r\n    \"state\": {\r\n      // Predicates\r\n    },\r\n    \"request\": {\r\n        \"headers\": {},\r\n        \"mode\": \"multipart\",\r\n        \"body\": {\r\n            \"part1\": {\r\n              \"mode\": \"json\", //validation mode\r\n              \"headers\": {}, //part headers\r\n              \"value\": {} //value specification for the validator\r\n            },\r\n            \"part2\": {\r\n              ...\r\n            }\r\n        },\r\n        \"bypassUnknownParts\": true //flag allowing to ignore all parts not present in the validator's specification\r\n                                   //by default, the flag is enabled, can be passed only to disable (false)\r\n    },\r\n    \"persist\": {\r\n      // State modifications\r\n    },\r\n    \"response\": {\r\n        \"code\": 200,\r\n        \"mode\": \"json\",\r\n        \"body\": {\r\n            \"code\": 0,\r\n            \"credit_amount\": 802400,\r\n            \"credit_term\": 120,\r\n            \"interest_rate\": 13.9,\r\n            \"partnum\": \"CL3.15\"\r\n        },\r\n        \"headers\": {\"Content-Type\": \"application/json\"},\r\n        \"delay\": \"1 second\"\r\n    }\r\n}\r\n```\r\n\r\n#### Simple Request Proxying\r\n\r\n```javascript\r\n{\r\n  \"name\": \"Simple proxy\",\r\n  \"method\": \"POST\",\r\n  \"path\": \"/pos-loans/api/cl/get_partner_lead_info\",\r\n  \"state\": {\r\n      // Predicates\r\n  },\r\n  \"request\": {\r\n    // Request specification\r\n  },\r\n  \"response\": {\r\n    \"mode\": \"proxy\",\r\n    \"uri\": \"http://some.host/api/cl/get_partner_lead_info\"\r\n  }\r\n}\r\n```\r\n\r\n#### Proxying with JSON Response Modification\r\n\r\n```javascript\r\n{\r\n  \"name\": \"Simple proxy\",\r\n  \"method\": \"POST\",\r\n  \"path\": \"/pos-loans/api/cl/get_partner_lead_info\",\r\n  \"state\": {\r\n      // Predicates\r\n  },\r\n  \"request\": {\r\n    // Request specification, mode json or jlens\r\n  },\r\n  \"response\": {\r\n    \"mode\": \"json-proxy\",\r\n    \"uri\": \"http://some.host/api/cl/get_partner_lead_info\",\r\n    \"patch\": {\r\n      \"field.innerField\": \"${req.someRequestField}\"\r\n    }\r\n  }\r\n}\r\n```\r\n\r\n#### Proxying with XML Response Modification\r\n\r\n```javascript\r\n{\r\n  \"name\": \"Simple proxy\",\r\n  \"method\": \"POST\",\r\n  \"path\": \"/pos-loans/api/cl/get_partner_lead_info\",\r\n  \"state\": {\r\n      // Predicates\r\n  },\r\n  \"request\": {\r\n    // Request specification, mode xml or xpath\r\n  },\r\n  \"response\": {\r\n    \"mode\": \"xml-proxy\",\r\n    \"uri\": \"http://some.host/api/cl/get_partner_lead_info\",\r\n    \"patch\": {\r\n      \"/env/someTag\": \"${/some/requestTag}\"\r\n    }\r\n  }\r\n}\r\n```\r\n\r\n### DSL for JSON and XML Validation Predicates\r\n\r\nIn jlens and xpath modes, the following is supported:\r\n\r\n```javascript\r\n{\r\n  \"a\": {\"==\": \"some value\"}, //exact match\r\n  \"b\": {\"!=\": \"some value\"}, //not equal\r\n  \"c\": {\"\u003e\": 42} | {\"\u003e=\": 42} | {\"\u003c\": 42} | {\"\u003c=\": 42}, //comparisons, for numbers only, can be combined\r\n  \"d\": {\"~=\": \"\\\\d+\"}, //regexp match\r\n  \"e\": {\"size\": 10}, //length, for arrays and strings\r\n  \"f\": {\"exists\": true} //existence check\r\n}\r\n```\r\nKeys in such objects are either a path in json (\"a.b.[0].c\") or xpath (\"/a/b/c\").\r\nNote: Currently, comparison functions may not work correctly with xpath pointing to XML attributes.\r\nThe problem can be bypassed by checking for existence/non-existence:\r\n```/tag/otherTag/[@attr='2']\": {\"exists\": true}```\r\n\r\nIn jlens mode, the following operations are additionally supported:\r\n```javascript\r\n{\r\n    \"g\": {\"[_]\": [\"1\", 2, true]}, //the field must contain one of the listed values\r\n    \"h\": {\"![_]\": [\"1\", 2, true]}, //the field must NOT contain any of the listed values\r\n    \"i\": {\"\u0026[_]\": [\"1\", 2, true]} //the field must be an array containing all listed values (order does not matter)\r\n}\r\n```\r\n\r\nIn xpath mode, the following operations are additionally supported:\r\n```javascript\r\n  \"/some/tag\": {\"cdata\": {\"==\": \"test\"}}, //validation for exact match of CDATA, argument must be a STRING\r\n  \"/some/tag\": {\"cdata\": {\"~=\": \"\\d+\"}}, //CDATA regex validation, argument must be a STRING\r\n  \"/some/tag\": {\"jcdata\": {\"a\": {\"==\": 42}}}, //validating CDATA content as JSON, all available predicates are supported\r\n  \"/other/tag\": {\"xcdata\": {\"/b\": {\"==\": 42}}} //validating CDATA content as XML, all available predicates are supported\r\n```\r\n\r\nIn web_form mode, ONLY the following operations are supported:\r\n`==`, `!=`, `~=`, `size`, `[_]`, `![_]`, `\u0026[_]`\r\n\r\n## Emulating GRPC Services\r\n\r\nHow it works under the hood:\r\nWhen creating a mock, the proto files nested in the request are parsed and transformed into a json representation of the protobuf schema. The database stores the json representation, not the original proto file. The first triggering of the mock may take a little longer than subsequent ones because a protobuf message decoder is generated from the json representation on the first trigger. After decoding, the data is transformed into json, which is checked by json predicates specified in the requestPredicates field. If the conditions are met, then the json from response.data (in fill mode) is serialized into protobuf and returned as a response.\r\n\r\nWorkflow:\r\n\r\n1. Search for mocks by method name\r\n2. Body validation\r\n3. Search for state by predicate\r\n4. Substituting values in the response template\r\n5. State modification\r\n6. Response delivery\r\n\r\n### Configuration of GRPC Stubs\r\n\r\n[Working with gRPC](grpc-configuration.md)\r\n\r\n## Emulating Bus Services\r\n\r\nWorkflow:\r\n\r\n1. Search for the mock by source.\r\n2. Search for state by predicate.\r\n3. Validate incoming message.\r\n4. Substitute values into the response template.\r\n5. Modify state.\r\n6. Send response.\r\n7. Execute callbacks (see the \"callbacks configuration\" section).\r\n\r\n### Configuration\r\n\r\n[Working with Message Brokers](message-brokers.md)\r\n\r\n### Mock Configuration\r\n\r\nSupported modes for input:\r\n* raw\r\n* json\r\n* xml\r\n* jlens\r\n* xpath\r\n\r\nSupported modes for output:\r\n* raw\r\n* json\r\n* xml\r\n\r\n```javascript\r\n{\r\n  \"name\": \"Spring has come\",\r\n  \"service\": \"test\",\r\n  \"source\": \"rmq_example_autobroker_decision\", //source from the config\r\n  \"input\": {\r\n    \"mode\": .. //as for HTTP stubs\r\n    \"payload\": .. //as body for HTTP stubs\r\n  },\r\n  \"state\": {\r\n    // Predicates\r\n  },\r\n  \"persist\": { //Optional\r\n    // State modifications\r\n  },\r\n  \"destination\": \"rmq_example_q1\", // destination from the config, optional\r\n  \"output\": { //Optional  \r\n    \"mode\": \"raw\",\r\n    \"payload\": \"..\",\r\n    \"delay\": \"1 second\"\r\n  },\r\n  \"callback\": { .. }\r\n}\r\n```\r\n\r\n### Callback Configuration\r\n\r\nTo mimic the behavior of the real world, sometimes it is necessary to call an HTTP service (for example, to fetch GBO when a message arrives) or to send additional messages to queues. For this purpose, callbacks can be used. The result of the service call can be parsed and saved in the state if necessary. Callbacks use the state of the caller.\r\n\r\n#### Calling an HTTP Method\r\n\r\nSupported modes for request:\r\n* no_body\r\n* raw\r\n* json\r\n* xml\r\n\r\nSupported modes for response:\r\n* json\r\n* xml\r\n\r\n\u003ePlease note!\r\n\u003eThe initial state is passed along the entire chain of callbacks, and it is not modified by the persist block (!!!)\r\n\r\n```javascript\r\n{\r\n  \"type\": \"http\",\r\n  \"request\": {\r\n    \"url\": \"http://some.host/api/v2/peka\",\r\n    \"method\": \"POST\",\r\n    \"headers\": {\"Content-Type\": \"application/json\"},\r\n    \"mode\": \"json\",\r\n    \"body\": {\r\n      \"trace_id\": \"42\",\r\n      \"account_number\": \"228\"\r\n    }\r\n  },\r\n  \"responseMode\": \"json\" | \"xml\", //Mandatory only if the persist block is present\r\n  \"persist\": { //Optional\r\n    // State modifications\r\n  },\r\n  \"delay\": \"1 second\", //Delay BEFORE executing the callback, optional\r\n  \"callback\": { .. } //Optional\r\n}\r\n```\r\n\r\n#### Sending a Message\r\n\r\nSupported modes for output:\r\n* raw\r\n* json\r\n* xml\r\n\r\n```javascript\r\n{\r\n  \"type\": \"message\",\r\n  \"destination\": \"rmq_example_q1\", // destination from the config\r\n  \"output\": {\r\n    \"mode\": \"raw\",\r\n    \"payload\": \"..\"\r\n  },\r\n  \"callback\": { .. } //Optional\r\n}\r\n```\r\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fleviysoft%2Fmockingbird","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fleviysoft%2Fmockingbird","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fleviysoft%2Fmockingbird/lists"}