diff --git a/CHANGELOG.md b/CHANGELOG.md index da99cde..f5b4e54 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,9 @@ Latest * [#20](https://github.com/cleverage/soap-process-bundle/issues/20) Update quality stack: use Rector `withComposerBased()` sets (removed `SYMFONY_64` / `PHPUNIT_100` sets), declare used Symfony packages and PHPUnit range in composer.json, apply quality tools fixes * [#22](https://github.com/cleverage/soap-process-bundle/issues/22) Add missing documentations: reference pages for Client, RequestTask & RequestTransformer, cookbooks. Harmonize and fix existing documentation. +### Fixes +* [#25](https://github.com/cleverage/soap-process-bundle/issues/25) RequestTask throws an exception when the SOAP call fails, so that the error strategy applies: the process now fails with the `stop` strategy, and the error outputs receive the task input (instead of `false`) with the `skip` strategy + v3.0 ------ diff --git a/docs/cookbooks/soap_enrich_csv.md b/docs/cookbooks/soap_enrich_csv.md index dc45fd8..2b7c73d 100644 --- a/docs/cookbooks/soap_enrich_csv.md +++ b/docs/cookbooks/soap_enrich_csv.md @@ -90,7 +90,7 @@ How it works: and [wrapper](https://github.com/cleverage/process-bundle/blob/main/docs/reference/transformers/wrapper_transformer.md) transformers: `FullCountryInfo` is a document/literal method expecting a single `parameters` structure. - The [RequestTask](../reference/tasks/request_task.md) calls `FullCountryInfo` with these arguments. With - `error_strategy: skip`, a failed call is logged by the task, `false` is sent to the + `error_strategy: skip`, a failed call is logged by the task, the SOAP arguments built from the line are sent to the [LoggerTask](https://github.com/cleverage/process-bundle/blob/main/docs/reference/tasks/logger_task.md) of the `error_outputs`, and the line is not written. - The second TransformerTask extracts the result from the `stdClass` response @@ -99,8 +99,9 @@ How it works: and maps the columns to write with the [CsvWriterTask](https://github.com/cleverage/process-bundle/blob/main/docs/reference/tasks/csv_writer_task.md). -Note that the error output of the RequestTask is `false`, not the CSV line: the details of the failed call (options, -last request and response) are in the log context of the `Empty resultset for query` error logged by the task. +Note that the error output of the RequestTask is its input (the SOAP arguments), not the CSV line: the details of the +failed call (options, last request and response) are in the log context of the `Empty resultset for query` error +logged by the task. To add a single value to the line instead of replacing it, the SOAP call can also be done inside a `mapping` with the [RequestTransformer](../reference/transformers/request_transformer.md) (`soap_request`), e.g. with `keep_input: true`: diff --git a/docs/reference/tasks/request_task.md b/docs/reference/tasks/request_task.md index 4b9ce58..81ffca6 100644 --- a/docs/reference/tasks/request_task.md +++ b/docs/reference/tasks/request_task.md @@ -25,7 +25,7 @@ Possible outputs `mixed`: the result of the SOAP call, usually a `stdClass` (or an array of `stdClass`) built by `SoapClient` from the response. -`false` when the call failed (`SoapFault`), see Notes. +When the call fails, an exception is thrown and the `error_strategy` applies, see Notes. Options ------- @@ -85,12 +85,12 @@ get_order: Notes ----- -* When the call fails (the client returns `false`, see [client](../client.md)), the task: - - logs an error `Empty resultset for query`, with the options and the last request and response in the log context - - sets `false` as error output, so the `error_outputs` tasks receive `false` (not the input of the task) - - with `error_strategy: skip`, skips the `outputs` tasks - - with `error_strategy: stop`, stops the process. No exception is set on the state, so the process is **not** - marked as failed and the console command does not return an error code +* When the call fails (the client returns `false`, see [client](../client.md)), the task logs an error + `Empty resultset for query`, with the options and the last request and response in the log context, then throws a + `RuntimeException` (`Soap call '' on client '' failed`), handled by the `error_strategy`: + - with `error_strategy: skip`, the `outputs` tasks are skipped and the `error_outputs` tasks receive the input of + the task + - with `error_strategy: stop`, the process fails and the console command returns an error code * An exception thrown by the client (e.g. a `SoapFault` when the WSDL cannot be loaded, or a `MissingClientException` for an unknown `client`) is handled as usual by the `error_strategy`. * `soap_call_options` and `soap_call_headers` are set on the client each time the task is executed, including when diff --git a/src/Task/RequestTask.php b/src/Task/RequestTask.php index 428e358..17065b8 100644 --- a/src/Task/RequestTask.php +++ b/src/Task/RequestTask.php @@ -13,7 +13,6 @@ namespace CleverAge\SoapProcessBundle\Task; -use CleverAge\ProcessBundle\Configuration\TaskConfiguration; use CleverAge\ProcessBundle\Model\AbstractConfigurableTask; use CleverAge\ProcessBundle\Model\ProcessState; use CleverAge\SoapProcessBundle\Registry\ClientRegistry; @@ -64,15 +63,11 @@ public function execute(ProcessState $state): void 'last_response_headers' => $client->getLastResponseHeaders(), ]; - $state->setErrorOutput($result); - $this->logger->error('Empty resultset for query', $logContext); - if (TaskConfiguration::STRATEGY_SKIP === $state->getTaskConfiguration()->getErrorStrategy()) { - $state->setSkipped(true); - } elseif (TaskConfiguration::STRATEGY_STOP === $state->getTaskConfiguration()->getErrorStrategy()) { - $state->setStopped(true); - } + // The process manager applies the error strategy: the process fails with "stop", the error outputs + // receive the task input with "skip" + throw new \RuntimeException(\sprintf("Soap call '%s' on client '%s' failed", $options['method'], $options['client'])); } $state->setOutput($result); diff --git a/tests/Task/RequestTaskTest.php b/tests/Task/RequestTaskTest.php new file mode 100644 index 0000000..1402e84 --- /dev/null +++ b/tests/Task/RequestTaskTest.php @@ -0,0 +1,79 @@ +createState(); + $state->expects($this->once())->method('setOutput')->with($response); + + $task = $this->createTask($response); + $task->initialize($state); + $task->execute($state); + } + + public function testFailedCallThrows(): void + { + $state = $this->createState(); + $state->expects($this->never())->method('setOutput'); + $state->expects($this->never())->method('setErrorOutput'); + $state->expects($this->never())->method('setStopped'); + + $task = $this->createTask(false); + $task->initialize($state); + + $this->expectException(\RuntimeException::class); + $this->expectExceptionMessage("Soap call 'FullCountryInfo' on client 'test' failed"); + $task->execute($state); + } + + private function createTask(mixed $callResult): RequestTask + { + $client = $this->createStub(ClientInterface::class); + $client->method('getCode')->willReturn('test'); + $client->method('call')->willReturn($callResult); + + $registry = new ClientRegistry(); + $registry->addClient($client); + + return new RequestTask(new NullLogger(), $registry); + } + + private function createState(): ProcessState&MockObject + { + $state = $this->createMock(ProcessState::class); + $state->method('getContextualizedOptions')->willReturn([ + 'client' => 'test', + 'method' => 'FullCountryInfo', + ]); + $state->method('getInput')->willReturn(['sCountryISOCode' => 'FR']); + + return $state; + } +}