Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
------

Expand Down
7 changes: 4 additions & 3 deletions docs/cookbooks/soap_enrich_csv.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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`:
Expand Down
14 changes: 7 additions & 7 deletions docs/reference/tasks/request_task.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
-------
Expand Down Expand Up @@ -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 '<method>' on client '<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
Expand Down
11 changes: 3 additions & 8 deletions src/Task/RequestTask.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down
79 changes: 79 additions & 0 deletions tests/Task/RequestTaskTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
<?php

declare(strict_types=1);

/*
* This file is part of the CleverAge/SoapProcessBundle package.
*
* Copyright (c) Clever-Age
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace CleverAge\SoapProcessBundle\Tests\Task;

use CleverAge\ProcessBundle\Model\ProcessState;
use CleverAge\SoapProcessBundle\Client\ClientInterface;
use CleverAge\SoapProcessBundle\Registry\ClientRegistry;
use CleverAge\SoapProcessBundle\Task\RequestTask;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\UsesClass;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use Psr\Log\NullLogger;

#[CoversClass(RequestTask::class)]
#[UsesClass(ClientRegistry::class)]
class RequestTaskTest extends TestCase
{
public function testExecute(): void
{
$response = new \stdClass();
$state = $this->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;
}
}
Loading