Skip to content

Commit f29b9ee

Browse files
authored
Add support for endpoint discovery (#1239)
1 parent 9a752b9 commit f29b9ee

File tree

9 files changed

+282
-7
lines changed

9 files changed

+282
-7
lines changed

CHANGELOG.md

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

33
## NOT RELEASED
44

5+
### Added
6+
7+
- Added support for endpoint discovery
8+
59
## 0.1.0
610

711
First version

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
"php": "^7.2.5 || ^8.0",
1515
"ext-filter": "*",
1616
"ext-json": "*",
17-
"async-aws/core": "^1.9",
17+
"async-aws/core": "^1.16",
1818
"symfony/polyfill-uuid": "^1.0"
1919
},
2020
"autoload": {
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
<?php
2+
3+
namespace AsyncAws\TimestreamQuery\Input;
4+
5+
use AsyncAws\Core\Input;
6+
use AsyncAws\Core\Request;
7+
use AsyncAws\Core\Stream\StreamFactory;
8+
9+
final class DescribeEndpointsRequest extends Input
10+
{
11+
/**
12+
* @param array{
13+
* @region?: string,
14+
* } $input
15+
*/
16+
public function __construct(array $input = [])
17+
{
18+
parent::__construct($input);
19+
}
20+
21+
public static function create($input): self
22+
{
23+
return $input instanceof self ? $input : new self($input);
24+
}
25+
26+
/**
27+
* @internal
28+
*/
29+
public function request(): Request
30+
{
31+
// Prepare headers
32+
$headers = [
33+
'Content-Type' => 'application/x-amz-json-1.0',
34+
'X-Amz-Target' => 'Timestream_20181101.DescribeEndpoints',
35+
];
36+
37+
// Prepare query
38+
$query = [];
39+
40+
// Prepare URI
41+
$uriString = '/';
42+
43+
// Prepare Body
44+
$bodyPayload = $this->requestBody();
45+
$body = empty($bodyPayload) ? '{}' : json_encode($bodyPayload, 4194304);
46+
47+
// Return the Request
48+
return new Request('POST', $uriString, $query, $headers, StreamFactory::create($body));
49+
}
50+
51+
private function requestBody(): array
52+
{
53+
$payload = [];
54+
55+
return $payload;
56+
}
57+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
<?php
2+
3+
namespace AsyncAws\TimestreamQuery\Result;
4+
5+
use AsyncAws\Core\Response;
6+
use AsyncAws\Core\Result;
7+
use AsyncAws\TimestreamQuery\ValueObject\Endpoint;
8+
9+
class DescribeEndpointsResponse extends Result
10+
{
11+
/**
12+
* An `Endpoints` object is returned when a `DescribeEndpoints` request is made.
13+
*/
14+
private $endpoints;
15+
16+
/**
17+
* @return Endpoint[]
18+
*/
19+
public function getEndpoints(): array
20+
{
21+
$this->initialize();
22+
23+
return $this->endpoints;
24+
}
25+
26+
protected function populateResult(Response $response): void
27+
{
28+
$data = $response->toArray();
29+
30+
$this->endpoints = $this->populateResultEndpoints($data['Endpoints']);
31+
}
32+
33+
private function populateResultEndpoint(array $json): Endpoint
34+
{
35+
return new Endpoint([
36+
'Address' => (string) $json['Address'],
37+
'CachePeriodInMinutes' => (string) $json['CachePeriodInMinutes'],
38+
]);
39+
}
40+
41+
/**
42+
* @return Endpoint[]
43+
*/
44+
private function populateResultEndpoints(array $json): array
45+
{
46+
$items = [];
47+
foreach ($json as $item) {
48+
$items[] = $this->populateResultEndpoint($item);
49+
}
50+
51+
return $items;
52+
}
53+
}

src/TimestreamQueryClient.php

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,11 @@
1515
use AsyncAws\TimestreamQuery\Exception\ThrottlingException;
1616
use AsyncAws\TimestreamQuery\Exception\ValidationException;
1717
use AsyncAws\TimestreamQuery\Input\CancelQueryRequest;
18+
use AsyncAws\TimestreamQuery\Input\DescribeEndpointsRequest;
1819
use AsyncAws\TimestreamQuery\Input\PrepareQueryRequest;
1920
use AsyncAws\TimestreamQuery\Input\QueryRequest;
2021
use AsyncAws\TimestreamQuery\Result\CancelQueryResponse;
22+
use AsyncAws\TimestreamQuery\Result\DescribeEndpointsResponse;
2123
use AsyncAws\TimestreamQuery\Result\PrepareQueryResponse;
2224
use AsyncAws\TimestreamQuery\Result\QueryResponse;
2325

@@ -53,11 +55,38 @@ public function cancelQuery($input): CancelQueryResponse
5355
'ThrottlingException' => ThrottlingException::class,
5456
'ValidationException' => ValidationException::class,
5557
'InvalidEndpointException' => InvalidEndpointException::class,
56-
]]));
58+
], 'requiresEndpointDiscovery' => true, 'usesEndpointDiscovery' => true]));
5759

5860
return new CancelQueryResponse($response);
5961
}
6062

63+
/**
64+
* DescribeEndpoints returns a list of available endpoints to make Timestream API calls against. This API is available
65+
* through both Write and Query.
66+
*
67+
* @see https://docs.aws.amazon.com/timestream/latest/developerguide/API_DescribeEndpoints.html
68+
* @see https://docs.aws.amazon.com/aws-sdk-php/v3/api/api-query.timestream-2018-11-01.html#describeendpoints
69+
*
70+
* @param array{
71+
* @region?: string,
72+
* }|DescribeEndpointsRequest $input
73+
*
74+
* @throws InternalServerException
75+
* @throws ValidationException
76+
* @throws ThrottlingException
77+
*/
78+
public function describeEndpoints($input = []): DescribeEndpointsResponse
79+
{
80+
$input = DescribeEndpointsRequest::create($input);
81+
$response = $this->getResponse($input->request(), new RequestContext(['operation' => 'DescribeEndpoints', 'region' => $input->getRegion(), 'exceptionMapping' => [
82+
'InternalServerException' => InternalServerException::class,
83+
'ValidationException' => ValidationException::class,
84+
'ThrottlingException' => ThrottlingException::class,
85+
]]));
86+
87+
return new DescribeEndpointsResponse($response);
88+
}
89+
6190
/**
6291
* A synchronous operation that allows you to submit a query with parameters to be stored by Timestream for later
6392
* running. Timestream only supports using this operation with the `PrepareQueryRequest$ValidateOnly` set to `true`.
@@ -86,7 +115,7 @@ public function prepareQuery($input): PrepareQueryResponse
86115
'ThrottlingException' => ThrottlingException::class,
87116
'ValidationException' => ValidationException::class,
88117
'InvalidEndpointException' => InvalidEndpointException::class,
89-
]]));
118+
], 'requiresEndpointDiscovery' => true, 'usesEndpointDiscovery' => true]));
90119

91120
return new PrepareQueryResponse($response);
92121
}
@@ -127,11 +156,16 @@ public function query($input): QueryResponse
127156
'ThrottlingException' => ThrottlingException::class,
128157
'ValidationException' => ValidationException::class,
129158
'InvalidEndpointException' => InvalidEndpointException::class,
130-
]]));
159+
], 'requiresEndpointDiscovery' => true, 'usesEndpointDiscovery' => true]));
131160

132161
return new QueryResponse($response, $this, $input);
133162
}
134163

164+
protected function discoverEndpoints(?string $region): array
165+
{
166+
return $this->describeEndpoints($region ? ['@region' => $region] : [])->getEndpoints();
167+
}
168+
135169
protected function getAwsErrorFactory(): AwsErrorFactoryInterface
136170
{
137171
return new JsonRpcAwsErrorFactory();

src/ValueObject/Endpoint.php

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
<?php
2+
3+
namespace AsyncAws\TimestreamQuery\ValueObject;
4+
5+
use AsyncAws\Core\EndpointDiscovery\EndpointInterface;
6+
7+
/**
8+
* Represents an available endpoint against which to make API calls against, as well as the TTL for that endpoint.
9+
*/
10+
final class Endpoint implements EndpointInterface
11+
{
12+
/**
13+
* An endpoint address.
14+
*/
15+
private $address;
16+
17+
/**
18+
* The TTL for the endpoint, in minutes.
19+
*/
20+
private $cachePeriodInMinutes;
21+
22+
/**
23+
* @param array{
24+
* Address: string,
25+
* CachePeriodInMinutes: string,
26+
* } $input
27+
*/
28+
public function __construct(array $input)
29+
{
30+
$this->address = $input['Address'] ?? null;
31+
$this->cachePeriodInMinutes = $input['CachePeriodInMinutes'] ?? null;
32+
}
33+
34+
public static function create($input): self
35+
{
36+
return $input instanceof self ? $input : new self($input);
37+
}
38+
39+
public function getAddress(): string
40+
{
41+
return $this->address;
42+
}
43+
44+
public function getCachePeriodInMinutes(): int
45+
{
46+
return $this->cachePeriodInMinutes;
47+
}
48+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php
2+
3+
namespace AsyncAws\TimestreamQuery\Tests\Unit\Input;
4+
5+
use AsyncAws\Core\Test\TestCase;
6+
use AsyncAws\TimestreamQuery\Input\DescribeEndpointsRequest;
7+
8+
class DescribeEndpointsRequestTest extends TestCase
9+
{
10+
public function testRequest(): void
11+
{
12+
$input = new DescribeEndpointsRequest([
13+
14+
]);
15+
16+
// see https://docs.aws.amazon.com/timestream/latest/developerguide/API_Operations_Amazon_Timestream_Query.html/API_DescribeEndpoints.html
17+
$expected = '
18+
POST / HTTP/1.0
19+
Content-Type: application/x-amz-json-1.0
20+
x-amz-target: Timestream_20181101.DescribeEndpoints
21+
22+
{
23+
}
24+
';
25+
26+
self::assertRequestEqualsHttpRequest($expected, $input->request());
27+
}
28+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<?php
2+
3+
namespace AsyncAws\TimestreamQuery\Tests\Unit\Result;
4+
5+
use AsyncAws\Core\EndpointDiscovery\EndpointInterface;
6+
use AsyncAws\Core\Response;
7+
use AsyncAws\Core\Test\Http\SimpleMockedResponse;
8+
use AsyncAws\Core\Test\TestCase;
9+
use AsyncAws\TimestreamQuery\Result\DescribeEndpointsResponse;
10+
use Psr\Log\NullLogger;
11+
use Symfony\Component\HttpClient\MockHttpClient;
12+
13+
class DescribeEndpointsResponseTest extends TestCase
14+
{
15+
public function testDescribeEndpointsResponse(): void
16+
{
17+
// see https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_DescribeEndpoints.html
18+
$response = new SimpleMockedResponse('{
19+
"Endpoints": [
20+
{
21+
"Address": "www.aws.com",
22+
"CachePeriodInMinutes": 1234
23+
}
24+
]
25+
}');
26+
27+
$client = new MockHttpClient($response);
28+
$result = new DescribeEndpointsResponse(new Response($client->request('POST', 'http://localhost'), $client, new NullLogger()));
29+
30+
self::assertCount(1, $result->getEndpoints());
31+
self::assertInstanceOf(EndpointInterface::class, $result->getEndpoints()[0]);
32+
self::assertSame('www.aws.com', $result->getEndpoints()[0]->getAddress());
33+
self::assertSame(1234, $result->getEndpoints()[0]->getCachePeriodInMinutes());
34+
}
35+
}

tests/Unit/TimestreamQueryClientTest.php

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace AsyncAws\TimestreamQuery\Tests\Unit;
44

55
use AsyncAws\Core\Credentials\NullProvider;
6+
use AsyncAws\Core\Test\Http\SimpleMockedResponse;
67
use AsyncAws\Core\Test\TestCase;
78
use AsyncAws\TimestreamQuery\Input\CancelQueryRequest;
89
use AsyncAws\TimestreamQuery\Input\PrepareQueryRequest;
@@ -17,7 +18,12 @@ class TimestreamQueryClientTest extends TestCase
1718
{
1819
public function testCancelQuery(): void
1920
{
20-
$client = new TimestreamQueryClient([], new NullProvider(), new MockHttpClient());
21+
$client = new TimestreamQueryClient([], new NullProvider(), new MockHttpClient([new SimpleMockedResponse('{
22+
"Endpoints": [{
23+
"Address": "www.aws.com",
24+
"CachePeriodInMinutes": 1234
25+
}]
26+
}'), new SimpleMockedResponse('{}')]));
2127

2228
$input = new CancelQueryRequest([
2329
'QueryId' => 'qwertyuiop',
@@ -30,7 +36,12 @@ public function testCancelQuery(): void
3036

3137
public function testPrepareQuery(): void
3238
{
33-
$client = new TimestreamQueryClient([], new NullProvider(), new MockHttpClient());
39+
$client = new TimestreamQueryClient([], new NullProvider(), new MockHttpClient([new SimpleMockedResponse('{
40+
"Endpoints": [{
41+
"Address": "www.aws.com",
42+
"CachePeriodInMinutes": 1234
43+
}]
44+
}'), new SimpleMockedResponse('{}')]));
3445

3546
$input = new PrepareQueryRequest([
3647
'QueryString' => 'SELECT * FROM db.tbl ORDER BY time DESC LIMIT 10',
@@ -45,7 +56,12 @@ public function testPrepareQuery(): void
4556

4657
public function testQuery(): void
4758
{
48-
$client = new TimestreamQueryClient([], new NullProvider(), new MockHttpClient());
59+
$client = new TimestreamQueryClient([], new NullProvider(), new MockHttpClient([new SimpleMockedResponse('{
60+
"Endpoints": [{
61+
"Address": "www.aws.com",
62+
"CachePeriodInMinutes": 1234
63+
}]
64+
}'), new SimpleMockedResponse('{}')]));
4965

5066
$input = new QueryRequest([
5167
'ClientToken' => 'qwertyuiop',

0 commit comments

Comments
 (0)