Skip to content

Commit 27c7424

Browse files
authored
Add StepFunctions StopExecution operation (#1201)
* Add StepFunctions StopExecution operation * Update changelog
1 parent 399ecde commit 27c7424

9 files changed

+308
-0
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
## NOT RELEASED
44

5+
- Added operation `stopExecution`
6+
57
## 0.1.2
68

79
### Added
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?php
2+
3+
namespace AsyncAws\StepFunctions\Exception;
4+
5+
use AsyncAws\Core\Exception\Http\ClientException;
6+
use Symfony\Contracts\HttpClient\ResponseInterface;
7+
8+
/**
9+
* The specified execution does not exist.
10+
*/
11+
final class ExecutionDoesNotExistException extends ClientException
12+
{
13+
protected function populateResult(ResponseInterface $response): void
14+
{
15+
$data = $response->toArray(false);
16+
17+
if (null !== $v = (isset($data['message']) ? (string) $data['message'] : null)) {
18+
$this->message = $v;
19+
}
20+
}
21+
}

src/Input/StopExecutionInput.php

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
<?php
2+
3+
namespace AsyncAws\StepFunctions\Input;
4+
5+
use AsyncAws\Core\Exception\InvalidArgument;
6+
use AsyncAws\Core\Input;
7+
use AsyncAws\Core\Request;
8+
use AsyncAws\Core\Stream\StreamFactory;
9+
10+
final class StopExecutionInput extends Input
11+
{
12+
/**
13+
* The Amazon Resource Name (ARN) of the execution to stop.
14+
*
15+
* @required
16+
*
17+
* @var string|null
18+
*/
19+
private $executionArn;
20+
21+
/**
22+
* The error code of the failure.
23+
*
24+
* @var string|null
25+
*/
26+
private $error;
27+
28+
/**
29+
* A more detailed explanation of the cause of the failure.
30+
*
31+
* @var string|null
32+
*/
33+
private $cause;
34+
35+
/**
36+
* @param array{
37+
* executionArn?: string,
38+
* error?: string,
39+
* cause?: string,
40+
* @region?: string,
41+
* } $input
42+
*/
43+
public function __construct(array $input = [])
44+
{
45+
$this->executionArn = $input['executionArn'] ?? null;
46+
$this->error = $input['error'] ?? null;
47+
$this->cause = $input['cause'] ?? null;
48+
parent::__construct($input);
49+
}
50+
51+
public static function create($input): self
52+
{
53+
return $input instanceof self ? $input : new self($input);
54+
}
55+
56+
public function getCause(): ?string
57+
{
58+
return $this->cause;
59+
}
60+
61+
public function getError(): ?string
62+
{
63+
return $this->error;
64+
}
65+
66+
public function getExecutionArn(): ?string
67+
{
68+
return $this->executionArn;
69+
}
70+
71+
/**
72+
* @internal
73+
*/
74+
public function request(): Request
75+
{
76+
// Prepare headers
77+
$headers = [
78+
'Content-Type' => 'application/x-amz-json-1.0',
79+
'X-Amz-Target' => 'AWSStepFunctions.StopExecution',
80+
];
81+
82+
// Prepare query
83+
$query = [];
84+
85+
// Prepare URI
86+
$uriString = '/';
87+
88+
// Prepare Body
89+
$bodyPayload = $this->requestBody();
90+
$body = empty($bodyPayload) ? '{}' : json_encode($bodyPayload, 4194304);
91+
92+
// Return the Request
93+
return new Request('POST', $uriString, $query, $headers, StreamFactory::create($body));
94+
}
95+
96+
public function setCause(?string $value): self
97+
{
98+
$this->cause = $value;
99+
100+
return $this;
101+
}
102+
103+
public function setError(?string $value): self
104+
{
105+
$this->error = $value;
106+
107+
return $this;
108+
}
109+
110+
public function setExecutionArn(?string $value): self
111+
{
112+
$this->executionArn = $value;
113+
114+
return $this;
115+
}
116+
117+
private function requestBody(): array
118+
{
119+
$payload = [];
120+
if (null === $v = $this->executionArn) {
121+
throw new InvalidArgument(sprintf('Missing parameter "executionArn" for "%s". The value cannot be null.', __CLASS__));
122+
}
123+
$payload['executionArn'] = $v;
124+
if (null !== $v = $this->error) {
125+
$payload['error'] = $v;
126+
}
127+
if (null !== $v = $this->cause) {
128+
$payload['cause'] = $v;
129+
}
130+
131+
return $payload;
132+
}
133+
}

src/Result/StopExecutionOutput.php

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php
2+
3+
namespace AsyncAws\StepFunctions\Result;
4+
5+
use AsyncAws\Core\Response;
6+
use AsyncAws\Core\Result;
7+
8+
class StopExecutionOutput extends Result
9+
{
10+
/**
11+
* The date the execution is stopped.
12+
*/
13+
private $stopDate;
14+
15+
public function getStopDate(): \DateTimeImmutable
16+
{
17+
$this->initialize();
18+
19+
return $this->stopDate;
20+
}
21+
22+
protected function populateResult(Response $response): void
23+
{
24+
$data = $response->toArray();
25+
26+
$this->stopDate = /** @var \DateTimeImmutable $d */ $d = \DateTimeImmutable::createFromFormat('U.u', sprintf('%.6F', $data['stopDate']));
27+
}
28+
}

src/StepFunctionsClient.php

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use AsyncAws\Core\Configuration;
99
use AsyncAws\Core\RequestContext;
1010
use AsyncAws\StepFunctions\Exception\ExecutionAlreadyExistsException;
11+
use AsyncAws\StepFunctions\Exception\ExecutionDoesNotExistException;
1112
use AsyncAws\StepFunctions\Exception\ExecutionLimitExceededException;
1213
use AsyncAws\StepFunctions\Exception\InvalidArnException;
1314
use AsyncAws\StepFunctions\Exception\InvalidExecutionInputException;
@@ -22,10 +23,12 @@
2223
use AsyncAws\StepFunctions\Input\SendTaskHeartbeatInput;
2324
use AsyncAws\StepFunctions\Input\SendTaskSuccessInput;
2425
use AsyncAws\StepFunctions\Input\StartExecutionInput;
26+
use AsyncAws\StepFunctions\Input\StopExecutionInput;
2527
use AsyncAws\StepFunctions\Result\SendTaskFailureOutput;
2628
use AsyncAws\StepFunctions\Result\SendTaskHeartbeatOutput;
2729
use AsyncAws\StepFunctions\Result\SendTaskSuccessOutput;
2830
use AsyncAws\StepFunctions\Result\StartExecutionOutput;
31+
use AsyncAws\StepFunctions\Result\StopExecutionOutput;
2932

3033
class StepFunctionsClient extends AbstractApi
3134
{
@@ -165,6 +168,33 @@ public function startExecution($input): StartExecutionOutput
165168
return new StartExecutionOutput($response);
166169
}
167170

171+
/**
172+
* Stops an execution.
173+
*
174+
* @see https://docs.aws.amazon.com/step-functions/latest/apireference/API_StopExecution.html
175+
* @see https://docs.aws.amazon.com/aws-sdk-php/v3/api/api-states-2016-11-23.html#stopexecution
176+
*
177+
* @param array{
178+
* executionArn: string,
179+
* error?: string,
180+
* cause?: string,
181+
* @region?: string,
182+
* }|StopExecutionInput $input
183+
*
184+
* @throws ExecutionDoesNotExistException
185+
* @throws InvalidArnException
186+
*/
187+
public function stopExecution($input): StopExecutionOutput
188+
{
189+
$input = StopExecutionInput::create($input);
190+
$response = $this->getResponse($input->request(), new RequestContext(['operation' => 'StopExecution', 'region' => $input->getRegion(), 'exceptionMapping' => [
191+
'ExecutionDoesNotExist' => ExecutionDoesNotExistException::class,
192+
'InvalidArn' => InvalidArnException::class,
193+
]]));
194+
195+
return new StopExecutionOutput($response);
196+
}
197+
168198
protected function getAwsErrorFactory(): AwsErrorFactoryInterface
169199
{
170200
return new JsonRpcAwsErrorFactory();

tests/Integration/StepFunctionsClientTest.php

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use AsyncAws\StepFunctions\Input\SendTaskHeartbeatInput;
99
use AsyncAws\StepFunctions\Input\SendTaskSuccessInput;
1010
use AsyncAws\StepFunctions\Input\StartExecutionInput;
11+
use AsyncAws\StepFunctions\Input\StopExecutionInput;
1112
use AsyncAws\StepFunctions\StepFunctionsClient;
1213

1314
class StepFunctionsClientTest extends TestCase
@@ -74,6 +75,22 @@ public function testStartExecution(): void
7475
$result->resolve();
7576
}
7677

78+
public function testStopExecution(): void
79+
{
80+
self::markTestIncomplete('Cannot test StartExecution without the ability to create machines available.');
81+
82+
$client = $this->getClient();
83+
84+
$input = new StopExecutionInput([
85+
'executionArn' => 'change me',
86+
'error' => 'change me',
87+
'cause' => 'change me',
88+
]);
89+
$result = $client->stopExecution($input);
90+
91+
$result->resolve();
92+
}
93+
7794
private function getClient(): StepFunctionsClient
7895
{
7996
self::fail('Not implemented');
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<?php
2+
3+
namespace AsyncAws\StepFunctions\Tests\Unit\Input;
4+
5+
use AsyncAws\Core\Test\TestCase;
6+
use AsyncAws\StepFunctions\Input\StopExecutionInput;
7+
8+
class StopExecutionInputTest extends TestCase
9+
{
10+
public function testRequest(): void
11+
{
12+
$input = new StopExecutionInput([
13+
'executionArn' => 'arn:foo',
14+
'error' => 'some error',
15+
'cause' => 'some cause',
16+
]);
17+
18+
// see https://docs.aws.amazon.com/step-functions/latest/apireference/API_StopExecution.html
19+
$expected = '
20+
POST / HTTP/1.0
21+
Content-Type: application/x-amz-json-1.0
22+
X-Amz-Target: AWSStepFunctions.StopExecution
23+
24+
{
25+
"executionArn": "arn:foo",
26+
"error": "some error",
27+
"cause": "some cause"
28+
}
29+
';
30+
31+
self::assertRequestEqualsHttpRequest($expected, $input->request());
32+
}
33+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?php
2+
3+
namespace AsyncAws\StepFunctions\Tests\Unit\Result;
4+
5+
use AsyncAws\Core\Response;
6+
use AsyncAws\Core\Test\Http\SimpleMockedResponse;
7+
use AsyncAws\Core\Test\TestCase;
8+
use AsyncAws\StepFunctions\Result\StopExecutionOutput;
9+
use Psr\Log\NullLogger;
10+
use Symfony\Component\HttpClient\MockHttpClient;
11+
12+
class StopExecutionOutputTest extends TestCase
13+
{
14+
public function testStopExecutionOutput(): void
15+
{
16+
// see https://docs.aws.amazon.com/stepfunctions/latest/APIReference/API_StopExecution.html
17+
$response = new SimpleMockedResponse('{
18+
"executionArn": "arn:foo:bar",
19+
"stopDate": "1234567.123456"
20+
}');
21+
22+
$client = new MockHttpClient($response);
23+
$result = new StopExecutionOutput(new Response($client->request('POST', 'http://localhost'), $client, new NullLogger()));
24+
25+
self::assertSame(1234567, $result->getStopDate()->getTimestamp());
26+
}
27+
}

tests/Unit/StepFunctionsClientTest.php

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,12 @@
88
use AsyncAws\StepFunctions\Input\SendTaskHeartbeatInput;
99
use AsyncAws\StepFunctions\Input\SendTaskSuccessInput;
1010
use AsyncAws\StepFunctions\Input\StartExecutionInput;
11+
use AsyncAws\StepFunctions\Input\StopExecutionInput;
1112
use AsyncAws\StepFunctions\Result\SendTaskFailureOutput;
1213
use AsyncAws\StepFunctions\Result\SendTaskHeartbeatOutput;
1314
use AsyncAws\StepFunctions\Result\SendTaskSuccessOutput;
1415
use AsyncAws\StepFunctions\Result\StartExecutionOutput;
16+
use AsyncAws\StepFunctions\Result\StopExecutionOutput;
1517
use AsyncAws\StepFunctions\StepFunctionsClient;
1618
use Symfony\Component\HttpClient\MockHttpClient;
1719

@@ -72,4 +74,19 @@ public function testStartExecution(): void
7274
self::assertInstanceOf(StartExecutionOutput::class, $result);
7375
self::assertFalse($result->info()['resolved']);
7476
}
77+
78+
public function testStopExecution(): void
79+
{
80+
$client = new StepFunctionsClient([], new NullProvider(), new MockHttpClient());
81+
82+
$input = new StopExecutionInput([
83+
'executionArn' => 'arn:foo',
84+
'cause' => 'Some cause',
85+
'error' => 'Some error',
86+
]);
87+
$result = $client->stopExecution($input);
88+
89+
self::assertInstanceOf(StopExecutionOutput::class, $result);
90+
self::assertFalse($result->info()['resolved']);
91+
}
7592
}

0 commit comments

Comments
 (0)