bodyFileName file) directly in the editor: a Response from file note above the response body field shows the file name, the body field is editable in place, and saving writes the edited content back to the body file in the __files directory through the mapping repository (so the in-memory cache stays in sync). When the same body file is shared by other mappings a warning is shown, because saving changes the response for all of them. You can flip a response between inline and file-backed in both directions — Switch to inline detaches the file and stores the body on the mapping, and Switch to file externalises an inline body to a new file under responses/ on save. Binary body files stay read-only and are edited on disk. Previously a file-backed response appeared as an empty body field with no indication it was served from a file.response.bodyFileName file, a Response from file note shows the file name, the body field is editable in place and pre-filled from the __files directory, and saving writes the edited content back to the body file through the mapping repository. A warning is shown when the file is shared by other mappings, and you can flip a response between inline and file-backed in both directions (Switch to inline / Switch to file). Binary body files stay read-only.HTTP 422 naming the file, combining bodyFileName with any other key in the same matcher entry is rejected, and mappings saved to disk keep the bodyFileName reference instead of the file's contents.response.bodyFileName) a warning is shown, because saving changes the response for all of them. Binary files remain read-only and are edited on disk.response.bodyFileName) and a file used as a request body matcher, so a file that the browser's orphaned badge reports as unreferenced (the badge inspects only response references) can still be blocked from deletion when it is genuinely in use, protecting you from silently breaking a request matcher. Overriding the refusal is not currently supported; to delete a referenced file, first remove or repoint the mapping(s) that use it.response.bodyFileName) and a file used as a request body matcher, so a file that the orphaned badge reports as unreferenced (the badge inspects only response references) can still be blocked from renaming when it is genuinely in use. Renaming onto a name that already exists is refused as well, so you never silently overwrite another file. To rename a referenced file, first remove or repoint the mapping(s) that use it; automatically updating the referencing mappings to the new name is not currently supported.nosniff content-type guard and as an attachment, so a file whose bytes happen to contain active content (such as a script-bearing SVG) can never run in your browser from the preview.verify() DSL onto Traffic Parrot's request journal: assert a request was made, was made exactly N times, or was NOT made via POST /api/http/requests/count, inspect captured bodies via POST /api/http/requests/find, list unmatched requests and near misses, and reset the journal — with a WireMock-to-Traffic-Parrot translation table and copy-paste curl recipes.delayDistribution, a chunkedDribbleDelay or a CONNECTION_RESET_BY_PEER fault, so this also fixes that data loss.query semantically — tolerant of insignificant whitespace, selection-set field order, and argument order, so differently formatted but equivalent queries select the same response (aliases and directives stay significant). Its object form matches on operationName alone to disambiguate operations on one URL, with an optional variables object to also assert on the request's variables. Select matches GraphQL in the Request body matcher dropdown on the HTTP Add/Edit mapping form to author the full-query form; the operation-name form is authored by importing mapping JSON. A request that is not valid GraphQL simply does not match (it is never an error), and a mapping whose own GraphQL query is invalid is rejected at save/import time.0 (no drift), 1 (drift detected) or 2 (usage or configuration error, including a files-root with no mappings). Operations you deliberately do not mock can be excluded with an exclude.operations list in an openapi-coverage.properties file in the files-root; an allowlist entry that matches no operation in the specification is itself reported as drift so the list cannot quietly go stale.response read back from stdout (status, headers, body); request mutation is not supported in this release. The feature is off by default (trafficparrot.http.externalprocess.enabled) and confined to a single operator-vetted directory (trafficparrot.http.externalprocess.allowedDir) — the executable runs as the Traffic Parrot OS user with no sandbox, so enable it only on trusted hosts. Each mapping configures the executable, optional args, a per-invocation timeoutMs (default 5000, the process is killed on expiry), and an onError fallback (fail-open returns the original response, fail-closed returns HTTP 500) applied on non-zero exit, timeout, malformed output, or a missing/disallowed executable.executable, args (space or newline separated), timeoutMs, and onError (fail-open/fail-closed). The option is offered only when the feature is enabled, and selecting it does not bypass the trafficparrot.http.externalprocess.allowedDir allow-list that still gates which executables may run. Saving writes the same transformerParameters.external-process JSON the editor, the mapping file on disk, and the WireMock Admin API all share. See Configuring it in the editor GUI.OPTIONS) is auto-answered HTTP 200 with Access-Control-Allow-Methods, Access-Control-Allow-Headers and Access-Control-Max-Age, and the request Origin is reflected into Access-Control-Allow-Origin on the actual response — with no flag and no per-stub configuration (unlike WireMock's off-by-default --enable-stub-cors). It also shows how to pin a specific origin by setting Access-Control-Allow-Origin in a stub's response.headers, and clarifies that this CORS behaviour is independent of the trafficparrot.http.optionsResponse.enabled property (which emits only an Allow header).Content-Length header and a body truncated to that length — instead of returning an HTTP 500 error.priority field when resolving match precedence. Previously, when two file-message mappings both matched an inbound message, priority was ignored and the mapping loaded first always won; HTTP, JMS and IBM MQ mappings already honoured priority correctly.width, height, and overlay text arguments — JDK-only, no external dependencies, deterministic within a single JVMbase64-decode-body built-in response transformer that Base64-decodes the rendered response body and serves it as raw bytes, enabling binary responses (PNG, PDF, ZIP, etc.) from JSON mappings when paired with helpers such as {{image}}response.transformerParameters JSON block (used by HmacSigningTransformer and other parameter-driven response transformers) can be authored and edited in the GUI instead of only in the mapping JSON file. The editor auto-formats the JSON on blur (2-space indent) and validates on save (invalid JSON prevents submit with an inline error). Existing mappings round-trip semantically through the form (whitespace is normalised; field order is preserved). See Editing transformer parameters in the GUI for details.queryParameters JSON; loading an existing mapping with a queryParameters block populates the rows on the form. This replaces the previous workarounds of putting the full query string in urlEqualTo or hand-writing a lookahead regex in urlMatching — both of which continue to work for existing mappings.TRAFFICPARROT_LICENSE environment variable (base64-encoded), so it can be injected at runtime from a Kubernetes Secret, Docker secret, or secrets manager instead of being baked into a Docker image. The system property -Dtrafficparrot.license.content takes precedence over the environment variable, which takes precedence over the classpath trafficparrot.license file; if neither is set, the classpath file is read as before (no change for existing installations). A set-but-invalid value fails fast with a clear error rather than silently falling back to the file.bodyFileName file, making it clear that the body is loaded from the __files directory rather than typed inline.SoapMessageSigner entry in the trafficparrot.http.responsetransformers startup property (see Properties) and the per-mapping Response transformers multiselect.hostFilter, methodFilter, statusFilter, and urlFilter parametersswiftField Handlebars helper for extracting SWIFT MT message field values in dynamic response templatescontains, matches (regex), and doesNotMatch matchers in addition to the default equalTo.m3u8 playlists and .ts video segments from a single wildcard mapping, including multi-variant adaptive-bitrate (ABR) playlists with scenario-state-driven variant switching.desc and .protoset files) via the gRPC skeleton import UI, in addition to .proto source filesdata/ directory in the exported ZIP, so dataSource-backed mappings (CSV lookup, Excel lookup, object store) work correctly when imported on another instancetrafficparrot.virtualservice.handlebars.throwErrors, which causes helper errors to throw exceptions (HTTP 500 or messaging failures) instead of embedding inline error strings in responsesscanConsistency parameter to the Couchbase N1QL dataSource helper, allowing you to choose between REQUEST_PLUS (consistent, default) and NOT_BOUNDED (faster) query scan consistency, plus improved error handling that returns error messages instead of exceptionsmanageState Handlebars helper using namespace/variableName syntax, allowing state isolation between different test scenariosPUT /api/state/{namespace}/{name}GET /api/state/{namespace}/{name}GET /api/state/{namespace}DELETE /api/state/{namespace}/api/state/global/{name} routes remain unchanged for backward compatibilityPUT /api/configuration/files/{file} endpoint for creating new configuration files programmatically (returns 201 Created, or 409 Conflict if file already exists)externalValue URLs in named examples, using the fetched response body as the mock response instead of a placeholder messagetransformerParameters.hmacSigning in the mapping JSON, supporting HmacSHA1, HmacSHA256, and HmacSHA512 algorithms for simulating webhook and API gateway authenticationtrafficparrot.virtualservice.openapi.check.mapping.schema.on.startup=truereplyToQueueManagerName, replyToQueueName) in mapping JSON files with Handlebars template support, allowing dynamic control of response message routing headerstrafficparrot.ibmmq.connect.options.accessQueue.input property to configure how IBM® MQ queues are opened for input (consumer) operations, mirroring the existing accessQueue.output property for producersobjectStore Handlebars helper to ensure file access stays within the data directory(binary) placeholder instead of corrupted text$ref schema references in OpenAPI importstrafficparrot.virtualservice.grpc.tls.enabled and trafficparrot.virtualservice.grpc.non.tls.enabled properties to allow independently enabling/disabling each gRPC server portTransformedRequest.like(Request) static factory method in place of the deprecated TransformedRequest public constructor; the deprecated constructor remains available for backwards compatibilitycom.ibm.mq.allclient to com.ibm.mq.jakarta.client. If upgrading, replace com.ibm.mq.allclient.jar with com.ibm.mq.jakarta.client.jar in your lib/external/ folder — see JMS and IBM MQ documentation for details. Existing javax.jms plugins continue to work without source changes; support for javax-based plugins will be removed in a future major release.trafficparrot.virtualservice.httpRequestLoggingDisabled=true trafficparrot.virtualservice.accessLogsEnabled=false trafficparrot.virtualservice.handlebars.maxCacheEntries=10000
trafficparrot.openapi.import.on.startup=true
{{jwt maxAge='12 days'}}
{{jwt exp=(parseDate '2041-02-23T21:22:23Z')}}
{{jwt nbf=(parseDate '2019-02-23T21:22:23Z')}}
{{jwt iss='https://issuer.trafficparrot.com/'}}
{{jwt aud='https://audience.trafficparrot.com/'}}
{{jwt sub='subject'}}
{{jwt alg='RS256'}}
{{jwt
customBoolClaim=true
customIntClaim=23
customStringClaim='example@x.y.z'
customDateClaim=(parseDate '2024-01-02T03:04:05Z')
}}
{{jwks}}{
"settings" : {
"extended" : {
"jwt" : {
"hs256Secret" : "...",
"rs256PublicKeyId" : "...",
"rs256PublicKey" : "-----BEGIN RSA PUBLIC KEY-----\n...\n-----END RSA PUBLIC KEY-----\n",
"rs256PrivateKey" : "-----BEGIN RSA PRIVATE KEY-----\n...\n-----END RSA PRIVATE KEY-----\n"
}
}
}
}
trafficparrot.http.jwt.enabled=true
curl -v http://localhost:8080/thrift/management/sendThriftMessage -d send-mapping-id=fc7ee4ad-e247-49a8-8528-af20845adde9 -d send-to-host-port=localhost:5562
"postServeActions" : [ {
"name" : "send-jms-message",
"parameters" : {
"jmsConnectionId" : "(connection id from jms-connections.json)",
"destination" : {
"name" : "queue-name",
"type" : "QUEUE"
},
"variables" : {
"id" : "{{randomValue length=24 type='ALPHANUMERIC'}}"
},
"properties" : {
"id" : "{{variables.id}}"
},
"jsonBody" : {
"id" : "{{variables.id}}",
"fieldFromRequest" : "{{originalRequest.jsonBody.requestField}}",
"fieldFromResponse" : "{{originalResponse.jsonBody.responseField}}"
},
"delayDistribution" : {
"type" : "fixed",
"milliseconds" : 500
}
}
} ]trafficparrot.http.optionsResponse.enabled=true
trafficparrot.gui.forwardToVirtualService.enabled=true
trafficparrot.okta.enabled=false
trafficparrot.okta.clientId=some_client_id
trafficparrot.okta.clientSecret=some_client_secret
trafficparrot.okta.oktaAuthorizeUri=https://${yourOktaDomain}/oauth2/default/v1/authorize
trafficparrot.okta.oktaTokenUri=https://${yourOktaDomain}/oauth2/default/v1/token
trafficparrot.okta.oktaIssuerUri=https://${yourOktaDomain}/oauth2/default
trafficparrot.okta.oktaAudience=api://default
trafficparrot.okta.oktaRedirectUri=https://some-TP-deployment-uri
trafficparrot.virtualservice.containerThreads=100
{{#if (jsonPath request.body '$.[?(@.field)]') }}field is present{{/if}}
{{#if (not (jsonPath request.body '$.[?(@.field)]') ) }}field is not present{{/unless}}
{{#if (jsonPath request.body '$.[?(!(@.field))]') }}field is not present{{/unless}}
{{#unless (jsonPath request.body '$.[?(@.field)]') }}field is not present{{/unless}}
{
"connectionId": "couchbase.db",
"type": "COUCHBASE_CONNECTION",
"connectionString": "couchbase://localhost:32784",
"username": "Administrator",
"password": "password",
"warmupQuery": "SELECT COUNT(*) FROM bucket_a UNION SELECT COUNT(*) FROM bucket_b",
"enableDnsSrv": true,
"networkResolution": "auto"
}
# OFF will turn off validation # DEFAULT_VALIDATION will turn on validation and provide default responses when not annotated # ONLY_ANNOTATED will turn on validation only for annotated specifications trafficparrot.openapi.request.validation.mode=OFF
paths:
/items:
get:
parameters:
# standard OpenAPI parameters with schema definitions
responses:
'400':
content:
application/json:
schema:
# standard OpenAPI response schema definition
x-traffic-parrot-validation: # array of validations that trigger this response codex-traffic-parrot-validation:
- type: schema
response: ${in} parameter ${name} has invalid value ${value} because ${reason}x-traffic-parrot-validation:
- type: schema
response:
code: INVALID_REQUEST
message: ${in} parameter ${name} has invalid value ${value} because ${reason}x-traffic-parrot-validation:
- type: schema
parameters:
- name: style
in: query
- name: limit
in: query
- name: id
in: path
- name: X-Request-Label
in: header
response:
code: INVALID_REQUEST_PARAMETER
message: ${in} parameter ${name} has invalid value ${value} because ${reason}x-traffic-parrot-validation:
- type: schema
parameters:
- name: *
in: requestBody
response:
code: REQUEST_BODY_INVALID
message: Request body has invalid value ${value} because ${reason}x-traffic-parrot-validation:
- type: schema
parameters:
- name: style
in: query
response:
code: INVALID_QUERY
message: query parameter ${name} has invalid value ${value} because ${reason}
- type: schema
response:
code: INVALID_REQUEST
message: ${in} parameter ${name} has invalid value ${value} because ${reason}
{{ modifyResponse 'headerValue' 'header-name-1' 'header-value-1' }}
{{ modifyResponse 'headerValue' 'header-name-2' 'header-value-2' }}
trafficparrotserver.logging.properties.filename=trafficparrotserver.logback.xml
{{ randomValue length=10 type='ALPHANUMERIC' mixedcase=true }}{{ modifyResponse 'headerValue' 'header-name' 'header-value' }}trafficparrot.openapi.import.mode=SELECT_RESPONSE_STATUS trafficparrot.openapi.skeletons.mode=SELECT_RESPONSE_STATUS
trafficparrot.gui.security.mode=LOGIN_PROPERTIES
admin=password,traffic-parrot-gui-role,traffic-parrot-gui-edit-role readonly=password,traffic-parrot-gui-role
{{ dataSource 'couchbase.db' 'INSERT INTO PERSON(KEY, VALUE) VALUES ("$id", {"id" : $id,"name" : $name})' id=1000 name='some-name' syntax='N1QL' }}
{{ dataSource 'couchbase.db' 'SELECT name FROM PERSON USE KEYS "$id"' id=1000 single=true syntax='N1QL' }}
{{ dataSource 'couchbase.db' 'INSERT INTO PERSON(KEY, VALUE) VALUES ("$id", $object)' id=1000 object=example syntax='N1QL' }}
{{#times 10}}{{@index}}{{#unless @last}},{{/unless}}{{/times}}DELETE http://localhost:8080/api/http/mappings/bulk/UUID1,UUID2,UUID3
DELETE http://localhost:8080/api/grpc/mappings/bulk/UUID1,UUID2,UUID3
com.example.Service/method -> host1:port1 com.example.packageA.* -> host2:port2 com.example.packageB.* -> host3:port3
trafficparrot.virtualservice.handlebars.now.provider=HANDLEBARS
trafficparrot.virtualservice.handlebars.now.provider=WIREMOCK
trafficparrot.virtualservice.handlebars.now.dynamic=true
{{now offset='2 years' format='epoch' provider='WIREMOCK'}}{{now format='short' provider='HANDLEBARS'}}trafficparrot.gui.security.mode=LOGIN_PROPERTIES
admin=password,traffic-parrot-gui-role
2020-10-07 20:09:32,330 INFO Request to 'GET /test123' was received on '2020-10-07T19:09:32.326Z' from '127.0.0.1'. Response was sent on '2020-10-07T19:09:32.397Z' to '127.0.0.1'. Total processing time 71ms
trafficparrot.virtualservice.handlebars.select.indexAndCacheCsvFiles=truethe CSV file loading performance will be significantly improved.
javax.servlet.ServletException: java.lang.IllegalStateException: Insufficient configured threads: required=212 < max=200 for QueuedThreadPool[qtp318353283]@12f9af83{STARTED,8<=168<=200,i=0,r=20,q=0}[ReservedThreadExecutor@71b3bc45{s=0/20,p=0}]
The fix is to increase the number of threads Jetty can spin up. This was done by exposing two properties to configure HTTP Jetty server thread queues:
trafficparrot.gui.http.queuedThreadPool.maxThreads=200 trafficparrot.gui.http.queuedThreadPool.minThreads=8
"sslCipherSuite": "TLS_RSA_WITH_AES_128_CBC_SHA", "sslPeerName": "OU=TP IBM MQ"To provide the server and client certificates you can add the following config to jvm.args:
-Djavax.net.ssl.trustStore=certificates/ca-chain.jks -Djavax.net.ssl.trustStorePassword=trafficparrot -Djavax.net.ssl.keyStore=certificates/mq-client.jks -Djavax.net.ssl.keyStorePassword=trafficparrot -Dcom.ibm.mq.cfg.useIBMCipherMappings=false
For more details see Connect to the queue manager via SSL/TLS channels
trafficparrot.ibmmq.logger.logMessageBodyAsPrintableCharacters=false
test@test-pcs:~/Downloads/trafficparrot-no-jre-5.x.y$ export TP_STARTUP_WAIT_MILLIS=180000 test@test-pcs:~/Downloads/trafficparrot-no-jre-5.x.y$ ./start.sh /optf/programs/jdk1.8.0_181/bin/java Picked up environment startup timeout in milliseconds 180000
trafficparrot.ibmmq.start.queue.replay.on.startup.script=classpath:start-ibmmq-queue-replay-on-startup.txt
trafficparrot.ibmmq.connect.options.accessQueue.output=MQOO_OUTPUT|MQOO_FAIL_IF_QUIESCING
trafficparrot.ibmmq.logger.logMessageBody=true
trafficparrot.virtualservice.mapping.cache.milliseconds=0 trafficparrot.virtualservice.mapping.cache.populate.on.startup=false
"readConnectionsToOpen": 5, "writeConnectionsToOpen": 5
"receiveThreads" : 5, "sendThreads" : 1
"maxMessagesInProgress": 1000000
# # This is a sample comment 1 # RequestQueueManager:'Request QM1' ResponseQueueManager:'Response QM1' RequestQueueNames:'REQ_1_A','REQ_1_B' # # This is a sample comment 2 # RequestQueueManager:'Request QM2' ResponseQueueManager:'Response QM2' RequestQueueNames:'REQ_2_A','REQ_2_B'
# # This is a sample comment # QueueManager:'Local Docker MQ 9' ProxyRequestQueue:'PROXY_PROCESS_PAYMENT' LiveRequestQueue:'PROCESS_PAYMENT' LiveResponseQueue:'PAYMENT_PROCESSESED' ProxyResponseQueue:'PROXY_PAYMENT_PROCESSESED' # # This is a sample comment # QueueManager:'Local Docker MQ 9' ProxyRequestQueue:'PROXY_CREATE_ORDER' LiveRequestQueue:'CREATE_ORDER' LiveResponseQueue:'ORDER_CREATED' ProxyResponseQueue:'PROXY_ORDER_CREATED'
This means that the standard WireMock client libraries can be used when connecting to the API port e.g. when using WireMock.configureFor("localhost", 8080, "/api/http");
Custom HTTP clients or a browser may continue to drop the /__admin section of the path so that API calls such as http://localhost:8080/api/http/mappings can be made
#
# Template used for gRPC mapping file names
#
# Available properties:
# {{ mapping.id }}
# {{ mapping.package }}
# {{ mapping.service }}
# {{ mapping.method }}
# {{ mapping.isSuccess }}
# {{ mapping.status.name }}
# {{ mapping.status.code }}
#
# Available commands:
# {{ countGrpcMappings package=mapping.package service=mapping.service method=mapping.method success=true offset=1 format='%02d' }}
trafficparrot.virtualservice.grpc.saved.mapping.file.name.template={{ mapping.package }}.{{ countGrpcMappings package=mapping.package offset=1 format='%02d' }}.{{ mapping.service }}-{{ mapping.method }}[{{ mapping.status.name }}]
trafficparrot.virtualservice.grpc.saved.mapping.file.name.collision.suffix.template=-{{ mapping.id }}
UNKNOWN: Traffic Parrot Virtual Service: No responses matched the given request. See the Traffic Parrot logs for more details.
Request was not matched
=======================
-----------------------------------------------------------------------------------------------------------------------
| Closest stub | Request |
-----------------------------------------------------------------------------------------------------------------------
|
helloworldgreetersayhello-38d8ac56-38fe-4a57-89b3-5af8368 |
874a5.json |
|
ANY | ANY
helloworld.Greeter/SayHello | helloworld.Greeter/SayHello
|
Protocol: GRPC | Protocol: GRPC
|
{ | { <<<<< Body does not match [equalToJson]
"inputName" : "example" | "inputName" : "not matching"
} | }
|
-----------------------------------------------------------------------------------------------------------------------
Configure how long to cache the proto files during replay using the trafficparrot.virtualservice.grpc.server.replay.proto.cache.milliseconds property.
A value of 0 means do not cache at all.
Configure the number of boss and worker threads of the gRPC server using the trafficparrot.virtualservice.grpc.server.boss.threads and trafficparrot.virtualservice.grpc.server.worker.threads properties.
The special value DOUBLE_NUMBER_OF_PROCESSORS means use double the number of processors.