Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for stringArray and operationContextParams #3301

Merged
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: 2 additions & 1 deletion botocore/endpoint_provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -577,6 +577,7 @@ class ParameterType(Enum):

string = str
boolean = bool
stringarray = tuple
jonathan343 marked this conversation as resolved.
Show resolved Hide resolved


class ParameterDefinition:
Expand All @@ -600,7 +601,7 @@ def __init__(
except AttributeError:
raise EndpointResolutionError(
msg=f"Unknown parameter type: {parameter_type}. "
"A parameter must be of type string or boolean."
"A parameter must be of type string, boolean, or stringarray."
jonathan343 marked this conversation as resolved.
Show resolved Hide resolved
)
self.documentation = documentation
self.builtin = builtIn
Expand Down
4 changes: 4 additions & 0 deletions botocore/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -624,6 +624,10 @@ def context_parameters(self):
and 'name' in shape.metadata['contextParam']
]

@CachedProperty
def operation_context_parameters(self):
return self._operation_model.get('operationContextParams', [])

@CachedProperty
def request_compression(self):
return self._operation_model.get('requestcompression')
Expand Down
17 changes: 17 additions & 0 deletions botocore/regions.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
import re
from enum import Enum

import jmespath

from botocore import UNSIGNED, xform_name
from botocore.auth import AUTH_TYPE_MAPS, HAS_CRT
from botocore.crt import CRT_SUPPORTED_AUTH_TYPES
Expand Down Expand Up @@ -578,6 +580,13 @@ def _resolve_param_from_context(
)
if dynamic is not None:
return dynamic
operation_context_params = (
self._resolve_param_as_operation_context_param(
param_name, operation_model, call_args
)
)
if operation_context_params is not None:
return operation_context_params
return self._resolve_param_as_client_context_param(param_name)

def _resolve_param_as_static_context_param(
Expand All @@ -600,6 +609,14 @@ def _resolve_param_as_client_context_param(self, param_name):
client_ctx_varname = client_ctx_params[param_name]
return self._client_context.get(client_ctx_varname)

def _resolve_param_as_operation_context_param(
SamRemis marked this conversation as resolved.
Show resolved Hide resolved
self, param_name, operation_model, call_args
):
operation_ctx_params = operation_model.operation_context_parameters
if param_name in operation_ctx_params:
path = operation_ctx_params[param_name]['path']
return jmespath.search(path, call_args)

def _resolve_param_as_builtin(self, builtin_name, builtins):
if builtin_name not in EndpointResolverBuiltins.__members__.values():
raise UnknownEndpointResolutionBuiltInName(name=builtin_name)
Expand Down
5 changes: 4 additions & 1 deletion botocore/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -1479,7 +1479,7 @@ def lru_cache_weakref(*cache_args, **cache_kwargs):
functools implementation which offers ``max_size`` and ``typed`` properties.

lru_cache is a global cache even when used on a method. The cache's
reference to ``self`` will prevent garbace collection of the object. This
reference to ``self`` will prevent garbage collection of the object. This
wrapper around functools.lru_cache replaces the reference to ``self`` with
a weak reference to not interfere with garbage collection.
"""
Expand All @@ -1491,6 +1491,9 @@ def func_with_weakref(weakref_to_self, *args, **kwargs):

@functools.wraps(func)
def inner(self, *args, **kwargs):
for kwarg_key, kwarg_value in kwargs.items():
if isinstance(kwarg_value, list):
kwargs[kwarg_key] = tuple(kwarg_value)
SamRemis marked this conversation as resolved.
Show resolved Hide resolved
return func_with_weakref(weakref.ref(self), *args, **kwargs)

inner.cache_info = func_with_weakref.cache_info
Expand Down
37 changes: 37 additions & 0 deletions tests/unit/data/endpoints/test-cases/array-index.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
{
"version": "1.0",
"testCases": [
{
"documentation": "Access an array index at index 0",
"params": {
"ResourceList": ["resource"]
},
"expect": {
"endpoint": {
"url": "https://www.resource.example.com"
}
}
},
{
"documentation": "Resolved value when array is explictly set to empty",
"params": {
"ResourceList": []
},
"expect": {
"endpoint": {
"url": "https://www.example.com"
}
}
},
{
"documentation": "Resolved value to default if array is unset",
"params": {
},
"expect": {
"endpoint": {
"url": "https://www.default1.example.com"
}
}
}
]
}
47 changes: 47 additions & 0 deletions tests/unit/data/endpoints/valid-rules/array-index.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
{
"version": "1.3",
"parameters": {
"ResourceList": {
"required": true,
"default": ["default1", "default2"],
"type": "stringArray"
}
},
"rules": [
{
"documentation": "Array is set, retrieve index 0",
"conditions": [
{
"fn": "isSet",
"argv": [
{
"ref": "ResourceList"
}
]
},
{
"fn": "getAttr",
"argv": [
{
"ref": "ResourceList"
},
"[0]"
],
"assign": "resourceid"
}
],
"endpoint": {
"url": "https://www.{resourceid}.example.com"
},
"type": "endpoint"
},
{
"documentation": "Fallback when array is unset",
"conditions": [],
"endpoint": {
"url": "https://www.example.com"
},
"type": "endpoint"
}
]
}
1 change: 1 addition & 0 deletions tests/unit/test_endpoint_provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ def endpoint_rule():

def ruleset_testcases():
filenames = [
"array-index",
"aws-region",
"default-values",
"eventbridge",
Expand Down
Loading