diff --git a/.docker/wiremock/mappings/soap-fault.json b/.docker/wiremock/mappings/soap-fault.json new file mode 100644 index 0000000..e7a6091 --- /dev/null +++ b/.docker/wiremock/mappings/soap-fault.json @@ -0,0 +1,19 @@ +{ + "name": "SOAP (non-WSDL, urn:demo): Fail returns a SOAP fault", + "request": { + "method": "POST", + "urlPath": "/soap", + "headers": { + "SOAPAction": { + "contains": "#Fail" + } + } + }, + "response": { + "status": 500, + "headers": { + "Content-Type": "text/xml; charset=utf-8" + }, + "body": "SOAP-ENV:ServerBook service unavailable" + } +} diff --git a/.docker/wiremock/mappings/soap-get-book.json b/.docker/wiremock/mappings/soap-get-book.json new file mode 100644 index 0000000..dd54d43 --- /dev/null +++ b/.docker/wiremock/mappings/soap-get-book.json @@ -0,0 +1,19 @@ +{ + "name": "SOAP (non-WSDL, urn:demo): GetBook returns a title", + "request": { + "method": "POST", + "urlPath": "/soap", + "headers": { + "SOAPAction": { + "contains": "#GetBook" + } + } + }, + "response": { + "status": 200, + "headers": { + "Content-Type": "text/xml; charset=utf-8" + }, + "body": "It" + } +} diff --git a/.docker/wiremock/mappings/soap-is-available.json b/.docker/wiremock/mappings/soap-is-available.json new file mode 100644 index 0000000..358b002 --- /dev/null +++ b/.docker/wiremock/mappings/soap-is-available.json @@ -0,0 +1,19 @@ +{ + "name": "SOAP (non-WSDL, urn:demo): IsAvailable legitimately returns false", + "request": { + "method": "POST", + "urlPath": "/soap", + "headers": { + "SOAPAction": { + "contains": "#IsAvailable" + } + } + }, + "response": { + "status": 200, + "headers": { + "Content-Type": "text/xml; charset=utf-8" + }, + "body": "false" + } +} diff --git a/config/packages/process/demo.soap.client_headers.yaml b/config/packages/process/demo.soap.client_headers.yaml new file mode 100644 index 0000000..bf3ec11 --- /dev/null +++ b/config/packages/process/demo.soap.client_headers.yaml @@ -0,0 +1,56 @@ +clever_age_process: + configurations: + demo.soap.client_headers: + description: > + RequestTask without soap_call_headers on a client whose headers are set by `calls` + (cleverage/soap-process-bundle#29): the ClientToken header of the client definition must be sent + (the WireMock request journal must count 1 request with it) + help: > + Ex: bin/console cleverage:process:execute demo.soap.client_headers + tasks: + reset: + service: '@CleverAge\RestProcessBundle\Task\RequestTask' + description: Reset the WireMock request journal + options: + client: wiremock_admin + url: '/requests' + method: 'DELETE' + outputs: [ input ] + + input: + service: '@CleverAge\ProcessBundle\Task\ConstantOutputTask' + options: + output: + id: 1 + outputs: [ request ] + + request: + service: '@CleverAge\SoapProcessBundle\Task\RequestTask' + options: + client: wiremock + method: 'GetBook' + outputs: [ no_input ] + + no_input: + service: '@CleverAge\ProcessBundle\Task\ConstantOutputTask' + description: The next request does not take the previous output as input + options: + output: { } + outputs: [ count ] + + count: + service: '@CleverAge\RestProcessBundle\Task\RequestTask' + description: Count the SOAP requests sent with the ClientToken header, must output {"count":1} + options: + client: wiremock_admin + url: '/requests/count' + method: 'POST' + data: + method: 'POST' + url: '/soap' + bodyPatterns: + - contains: 'ClientToken' + outputs: [ debug ] + + debug: + service: '@CleverAge\ProcessBundle\Task\Debug\DebugTask' diff --git a/config/packages/process/demo.soap.false_result.yaml b/config/packages/process/demo.soap.false_result.yaml new file mode 100644 index 0000000..bb7d00b --- /dev/null +++ b/config/packages/process/demo.soap.false_result.yaml @@ -0,0 +1,25 @@ +clever_age_process: + configurations: + demo.soap.false_result: + description: > + SOAP method legitimately returning false (cleverage/soap-process-bundle#31): false must be output, the + call must not be handled as failed + help: > + Ex: bin/console cleverage:process:execute demo.soap.false_result + tasks: + input: + service: '@CleverAge\ProcessBundle\Task\ConstantOutputTask' + options: + output: + id: 1 + outputs: [ request ] + + request: + service: '@CleverAge\SoapProcessBundle\Task\RequestTask' + options: + client: wiremock + method: 'IsAvailable' + outputs: [ debug ] + + debug: + service: '@CleverAge\ProcessBundle\Task\Debug\DebugTask' diff --git a/config/packages/process/demo.soap.fault_without_exceptions.yaml b/config/packages/process/demo.soap.fault_without_exceptions.yaml new file mode 100644 index 0000000..4ae96d4 --- /dev/null +++ b/config/packages/process/demo.soap.fault_without_exceptions.yaml @@ -0,0 +1,18 @@ +clever_age_process: + configurations: + demo.soap.fault_without_exceptions: + description: > + SOAP fault on a client with the exceptions: false option (cleverage/soap-process-bundle#32): the + call must fail, the SoapFault must not be output as a result + help: > + Ex: bin/console cleverage:process:execute demo.soap.fault_without_exceptions + tasks: + request: + service: '@CleverAge\SoapProcessBundle\Task\RequestTask' + options: + client: wiremock_no_exceptions + method: 'Fail' + outputs: [ debug ] + + debug: + service: '@CleverAge\ProcessBundle\Task\Debug\DebugTask' diff --git a/config/packages/process/demo.soap.headers_leak.yaml b/config/packages/process/demo.soap.headers_leak.yaml new file mode 100644 index 0000000..3c6e8c8 --- /dev/null +++ b/config/packages/process/demo.soap.headers_leak.yaml @@ -0,0 +1,76 @@ +clever_age_process: + configurations: + demo.soap.headers_leak: + description: > + RequestTask with soap_call_headers, then the soap_request transformer on the same client + (cleverage/soap-process-bundle#30): the TaskToken header must only be sent by the task (the WireMock + request journal must count 1 request with it) + help: > + Ex: bin/console cleverage:process:execute demo.soap.headers_leak + tasks: + reset: + service: '@CleverAge\RestProcessBundle\Task\RequestTask' + description: Reset the WireMock request journal + options: + client: wiremock_admin + url: '/requests' + method: 'DELETE' + outputs: [ input ] + + input: + service: '@CleverAge\ProcessBundle\Task\ConstantOutputTask' + options: + output: + id: 1 + outputs: [ request ] + + request: + service: '@CleverAge\SoapProcessBundle\Task\RequestTask' + options: + client: wiremock + method: 'GetBook' + soap_call_headers: + TaskToken: + namespace: 'urn:demo' + data: 'from-the-task' + outputs: [ transformer_input ] + + transformer_input: + service: '@CleverAge\ProcessBundle\Task\ConstantOutputTask' + options: + output: + id: 2 + outputs: [ transformer ] + + transformer: + service: '@CleverAge\ProcessBundle\Task\TransformerTask' + options: + transformers: + soap_request: + client: wiremock + method: 'GetBook' + outputs: [ no_input ] + + no_input: + service: '@CleverAge\ProcessBundle\Task\ConstantOutputTask' + description: The next request does not take the previous output as input + options: + output: { } + outputs: [ count ] + + count: + service: '@CleverAge\RestProcessBundle\Task\RequestTask' + description: Count the SOAP requests sent with the TaskToken header, must output {"count":1} + options: + client: wiremock_admin + url: '/requests/count' + method: 'POST' + data: + method: 'POST' + url: '/soap' + bodyPatterns: + - contains: 'TaskToken' + outputs: [ debug ] + + debug: + service: '@CleverAge\ProcessBundle\Task\Debug\DebugTask' diff --git a/config/packages/process/demo.soap.invalid_input.yaml b/config/packages/process/demo.soap.invalid_input.yaml new file mode 100644 index 0000000..b8eb82c --- /dev/null +++ b/config/packages/process/demo.soap.invalid_input.yaml @@ -0,0 +1,24 @@ +clever_age_process: + configurations: + demo.soap.invalid_input: + description: > + RequestTask receiving a string input (cleverage/soap-process-bundle#33): the task only accepts an + array or empty input, it must fail with a clear message + help: > + Ex: bin/console cleverage:process:execute demo.soap.invalid_input + tasks: + input: + service: '@CleverAge\ProcessBundle\Task\ConstantOutputTask' + options: + output: 'GetBook' + outputs: [ request ] + + request: + service: '@CleverAge\SoapProcessBundle\Task\RequestTask' + options: + client: wiremock + method: 'GetBook' + outputs: [ debug ] + + debug: + service: '@CleverAge\ProcessBundle\Task\Debug\DebugTask' diff --git a/config/services.yaml b/config/services.yaml index 8b1d585..af9b800 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -42,8 +42,7 @@ services: $options: trace: true exceptions: true - calls: - - [ setSoapOptions, [ { features: SOAP_SINGLE_ELEMENT_ARRAYS} ] ] + features: !php/const SOAP_SINGLE_ELEMENT_ARRAYS tags: - { name: cleverage.soap.client } @@ -71,6 +70,34 @@ services: tags: - { name: cleverage.rest.client } + # SOAP clients (non-WSDL mode, urn:demo) on the WireMock service (stubs .docker/wiremock/mappings/soap-*.json). + # The headers set by `calls` are sent with every call (cleverage/soap-process-bundle#29); the second client + # does not throw on SOAP faults (exceptions: false, cleverage/soap-process-bundle#32). + app.cleverage_soap_process.client.wiremock: + class: CleverAge\SoapProcessBundle\Client\Client + bind: + $code: 'wiremock' + $wsdl: null + $options: + location: '%env(string:WIREMOCK_URL)%/soap' + uri: 'urn:demo' + calls: + - [ setSoapHeaders, [ [ !service { class: SoapHeader, arguments: [ 'urn:demo', 'ClientToken', 'from-the-client-definition' ] } ] ] ] + tags: + - { name: cleverage.soap.client } + + app.cleverage_soap_process.client.wiremock_no_exceptions: + class: CleverAge\SoapProcessBundle\Client\Client + bind: + $code: 'wiremock_no_exceptions' + $wsdl: null + $options: + location: '%env(string:WIREMOCK_URL)%/soap' + uri: 'urn:demo' + exceptions: false + tags: + - { name: cleverage.soap.client } + # REST client on an unresolvable host (.invalid TLD): every request fails with a transport error. # Used by demo.rest.transport_error (cleverage/rest-process-bundle#36). app.cleverage_rest_process.client.unreachable: diff --git a/docs/wiremock.md b/docs/wiremock.md index e349bd6..c836d98 100644 --- a/docs/wiremock.md +++ b/docs/wiremock.md @@ -33,6 +33,18 @@ cases (status codes, transport errors...) can be reproduced at will. Run them with `bin/console cleverage:process:execute ` (`make bash` first). +### Mocking a SOAP service + +WireMock also mocks SOAP services: the `soap-*.json` stubs answer SOAP envelopes on `/soap`, matched on the +`SOAPAction` header (`urn:demo#` in non-WSDL mode). The `wiremock` SOAP client (`config/services.yaml`) uses +the non-WSDL mode (`location` / `uri` options), and sets a `ClientToken` header with `calls: [setSoapHeaders, ...]`; +`wiremock_no_exceptions` uses the `exceptions: false` option. + +The `demo.soap.client_headers` and `demo.soap.headers_leak` processes check the SOAP headers actually sent, by +counting the matching requests with the admin API (`POST /__admin/requests/count` with a `bodyPatterns` criterion); +`demo.soap.false_result` and `demo.soap.fault_without_exceptions` use the `IsAvailable` (returns `false`) and `Fail` +(SOAP fault) mocked methods. + ### Adding a stub 1. Call the API once and look at the unmatched request: `curl http://localhost:8089/__admin/requests/unmatched`.