Skip to content

Commit

Permalink
enhancement: add stringarray and operationContextParam support (#2982)
Browse files Browse the repository at this point in the history
  • Loading branch information
stobrien89 committed Aug 26, 2024
1 parent 702b995 commit 1c518ab
Show file tree
Hide file tree
Showing 15 changed files with 572 additions and 51 deletions.
7 changes: 7 additions & 0 deletions .changes/nextrelease/endpointv2-updates.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[
{
"type": "enhancement",
"category": "EndpointV2",
"description": "Adds support for `StringArray` endpoint parameters and `operationContextParams`"
}
]
16 changes: 16 additions & 0 deletions src/Api/Operation.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ class Operation extends AbstractModel
private $errors;
private $staticContextParams = [];
private $contextParams;
private $operationContextParams = [];

public function __construct(array $definition, ShapeMap $shapeMap)
{
Expand All @@ -28,6 +29,10 @@ public function __construct(array $definition, ShapeMap $shapeMap)
$this->staticContextParams = $definition['staticContextParams'];
}

if (isset($definition['operationContextParams'])) {
$this->operationContextParams = $definition['operationContextParams'];
}

parent::__construct($definition, $shapeMap);
$this->contextParams = $this->setContextParams();
}
Expand Down Expand Up @@ -124,6 +129,17 @@ public function getContextParams()
return $this->contextParams;
}

/**
* Gets definition of modeled dynamic values used
* for endpoint resolution
*
* @return array
*/
public function getOperationContextParams(): array
{
return $this->operationContextParams;
}

private function setContextParams()
{
$members = $this->getInput()->getMembers();
Expand Down
1 change: 0 additions & 1 deletion src/ClientSideMonitoring/ApiCallMonitoringMiddleware.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
*/
class ApiCallMonitoringMiddleware extends AbstractMonitoringMiddleware
{

/**
* Api Call Attempt event keys for each Api Call event key
*
Expand Down
33 changes: 33 additions & 0 deletions src/EndpointV2/EndpointV2Middleware.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use Aws\CommandInterface;
use Closure;
use GuzzleHttp\Promise\Promise;
use function JmesPath\search;

/**
* Handles endpoint rule evaluation and endpoint resolution.
Expand Down Expand Up @@ -137,9 +138,14 @@ private function resolveArgs(array $commandArgs, Operation $operation): array
$contextParams = $this->bindContextParams(
$commandArgs, $operation->getContextParams()
);
$operationContextParams = $this->bindOperationContextParams(
$commandArgs,
$operation->getOperationContextParams()
);

return array_merge(
$this->clientArgs,
$operationContextParams,
$contextParams,
$staticContextParams,
$endpointCommandArgs
Expand Down Expand Up @@ -231,6 +237,33 @@ private function bindContextParams(
return $scopedParams;
}

/**
* Binds context params to their corresponding values found in
* command arguments.
*
* @param array $commandArgs
* @param array $contextParams
*
* @return array
*/
private function bindOperationContextParams(
array $commandArgs,
array $operationContextParams
): array
{
$scopedParams = [];

foreach($operationContextParams as $name => $spec) {
$scopedValue = search($spec['path'], $commandArgs);

if ($scopedValue) {
$scopedParams[$name] = $scopedValue;
}
}

return $scopedParams;
}

/**
* Applies resolved auth schemes to the command object.
*
Expand Down
73 changes: 52 additions & 21 deletions src/EndpointV2/Ruleset/RulesetParameter.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace Aws\EndpointV2\Ruleset;

use Aws\Exception\UnresolvedEndpointException;
use function \Aws\is_associative;

/**
* Houses properties of an individual parameter definition.
Expand Down Expand Up @@ -30,6 +31,13 @@ class RulesetParameter
/** @var boolean */
private $deprecated;

/** @var array<string, string> */
private static $typeMap = [
'String' => 'is_string',
'Boolean' => 'is_bool',
'StringArray' => 'isStringArray'
];

public function __construct($name, array $definition)
{
$type = ucfirst($definition['type']);
Expand All @@ -38,18 +46,16 @@ public function __construct($name, array $definition)
} else {
throw new UnresolvedEndpointException(
'Unknown parameter type ' . "`{$type}`" .
'. Parameters must be of type `String` or `Boolean`.'
'. Parameters must be of type `String`, `Boolean` or `StringArray.'
);
}

$this->name = $name;
$this->builtIn = isset($definition['builtIn']) ? $definition['builtIn'] : null;
$this->default = isset($definition['default']) ? $definition['default'] : null;
$this->required = isset($definition['required']) ?
$definition['required'] : false;
$this->documentation = isset($definition['documentation']) ?
$definition['documentation'] : null;
$this->deprecated = isset($definition['deprecated']) ?
$definition['deprecated'] : false;
$this->builtIn = $definition['builtIn'] ?? null;
$this->default = $definition['default'] ?? null;
$this->required = $definition['required'] ?? false;
$this->documentation = $definition['documentation'] ?? null;
$this->deprecated = $definition['deprecated'] ?? false;
}

/**
Expand Down Expand Up @@ -116,12 +122,7 @@ public function getDeprecated()
*/
public function validateInputParam($inputParam)
{
$typeMap = [
'String' => 'is_string',
'Boolean' => 'is_bool'
];

if ($typeMap[$this->type]($inputParam) === false) {
if (!$this->isValidInput($inputParam)) {
throw new UnresolvedEndpointException(
"Input parameter `{$this->name}` is the wrong type. Must be a {$this->type}."
);
Expand All @@ -130,19 +131,49 @@ public function validateInputParam($inputParam)
if ($this->deprecated) {
$deprecated = $this->deprecated;
$deprecationString = "{$this->name} has been deprecated ";
$msg = isset($deprecated['message']) ? $deprecated['message'] : null;
$since = isset($deprecated['since']) ? $deprecated['since'] : null;
$msg = $deprecated['message'] ?? null;
$since = $deprecated['since'] ?? null;

if (!is_null($since)) $deprecationString = $deprecationString
. 'since '. $since . '. ';
if (!is_null($msg)) $deprecationString = $deprecationString . $msg;
if (!is_null($since)){
$deprecationString .= 'since ' . $since . '. ';
}
if (!is_null($msg)) {
$deprecationString .= $msg;
}

trigger_error($deprecationString, E_USER_WARNING);
}
}

private function isValidType($type)
{
return in_array($type, ['String', 'Boolean']);
return isset(self::$typeMap[$type]);
}

private function isValidInput($inputParam): bool
{
$method = self::$typeMap[$this->type];
if (is_callable($method)) {
return $method($inputParam);
} elseif (method_exists($this, $method)) {
return $this->$method($inputParam);
}

return false;
}

private function isStringArray(array $array): bool
{
if (is_associative($array)) {
return false;
}

foreach($array as $value) {
if (!is_string($value)) {
return false;
}
}

return true;
}
}
14 changes: 10 additions & 4 deletions src/EndpointV2/Ruleset/RulesetStandardLibrary.php
Original file line number Diff line number Diff line change
Expand Up @@ -61,17 +61,23 @@ public function not($value)
*/
public function getAttr($from, $path)
{
// Handles the case where "[<int|string]" is provided as the top-level path
if (preg_match('/^\[(\w+)\]$/', $path, $matches)) {
$index = is_numeric($matches[1]) ? (int) $matches[1] : $matches[1];

return $from[$index] ?? null;
}

$parts = explode('.', $path);
foreach ($parts as $part) {
$sliceIdx = strpos($part, '[');
if ($sliceIdx !== false) {
if (substr($part, -1) !== ']') {
return null;
}
$slice = intval(substr($part, $sliceIdx + 1, strlen($part) - 1));
$from = isset($from[substr($part,0, $sliceIdx)][$slice])
? $from[substr($part,0, $sliceIdx)][$slice]
: null;
$slice = (int) substr($part, $sliceIdx + 1, strlen($part) - 1);
$fromIndex = substr($part, 0, $sliceIdx);
$from = $from[$fromIndex][$slice] ?? null;
} else {
$from = $from[$part];
}
Expand Down
20 changes: 20 additions & 0 deletions src/functions.php
Original file line number Diff line number Diff line change
Expand Up @@ -603,3 +603,23 @@ function strip_fips_pseudo_regions($region)
return str_replace(['fips-', '-fips'], ['', ''], $region);
}

/**
* Checks if an array is associative
*
* @param array $array
*
* @return bool
*/
function is_associative(array $array): bool
{
if (empty($array)) {
return false;
}

if (function_exists('array_is_list')) {
return !array_is_list($array);
}

return array_keys($array) !== range(0, count($array) - 1);
}

40 changes: 40 additions & 0 deletions tests/Api/OperationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -131,4 +131,44 @@ public function testGetContextParams()
$contextParams
);
}

public function testGetOperationContextParams()
{
$definition = [
"input" => ["shape" => "ListOfObjectsOperationRequest"],
"operationContextParams" => [
"stringArrayParam" => [
"path" => "nested.listOfObjects[*].key"
]
],
"http" => [
"method" => "POST",
"requestUri" => "/"
]
];
$operation = new Operation(
$definition,
new ShapeMap([
"ListOfObjectsOperationRequest" => [
"type" => "structure",
"members" => [
"nested" => ["shape" => "Nested"]
]
],
"Nested" => [
"type" => "structure",
"members" => [
"listOfObjects" => ["shape" => "ListOfObjects"]
]
],
"ListOfObjects" => [
"type" => "list",
"member" => ["shape" => "ObjectMember"]
]
])
);

$operationContextParams = $operation->getOperationContextParams();
$this->assertSame($definition['operationContextParams'], $operationContextParams);
}
}
Loading

0 comments on commit 1c518ab

Please sign in to comment.