2015-11-27 23:25:33 +01:00
|
|
|
# Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
|
|
#
|
|
|
|
# Licensed under the Apache License, Version 2.0 (the "License"). You
|
|
|
|
# may not use this file except in compliance with the License. A copy of
|
|
|
|
# the License is located at
|
|
|
|
#
|
2021-09-22 18:34:33 +02:00
|
|
|
# https://aws.amazon.com/apache2.0/
|
2015-11-27 23:25:33 +01:00
|
|
|
#
|
|
|
|
# or in the "license" file accompanying this file. This file is
|
|
|
|
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
|
|
|
|
# ANY KIND, either express or implied. See the License for the specific
|
|
|
|
# language governing permissions and limitations under the License.
|
2022-05-26 01:13:54 +02:00
|
|
|
from botocore.model import OperationModel, ServiceModel
|
2015-11-27 23:25:33 +01:00
|
|
|
|
|
|
|
from boto3.dynamodb.conditions import Attr, Key
|
2022-05-26 01:13:54 +02:00
|
|
|
from boto3.dynamodb.transform import (
|
|
|
|
DynamoDBHighLevelResource,
|
|
|
|
ParameterTransformer,
|
|
|
|
TransformationInjector,
|
|
|
|
copy_dynamodb_params,
|
|
|
|
register_high_level_interface,
|
|
|
|
)
|
|
|
|
from boto3.resources.base import ResourceMeta, ServiceResource
|
|
|
|
from tests import mock, unittest
|
2015-11-27 23:25:33 +01:00
|
|
|
|
|
|
|
|
|
|
|
class BaseTransformationTest(unittest.TestCase):
|
|
|
|
def setUp(self):
|
|
|
|
self.target_shape = 'MyShape'
|
|
|
|
self.original_value = 'orginal'
|
|
|
|
self.transformed_value = 'transformed'
|
|
|
|
self.transformer = ParameterTransformer()
|
|
|
|
self.json_model = {}
|
|
|
|
self.nested_json_model = {}
|
|
|
|
self.setup_models()
|
|
|
|
self.build_models()
|
|
|
|
|
|
|
|
def setup_models(self):
|
|
|
|
self.json_model = {
|
|
|
|
'operations': {
|
|
|
|
'SampleOperation': {
|
|
|
|
'name': 'SampleOperation',
|
|
|
|
'input': {'shape': 'SampleOperationInputOutput'},
|
2022-05-26 01:13:54 +02:00
|
|
|
'output': {'shape': 'SampleOperationInputOutput'},
|
2015-11-27 23:25:33 +01:00
|
|
|
}
|
|
|
|
},
|
|
|
|
'shapes': {
|
|
|
|
'SampleOperationInputOutput': {
|
|
|
|
'type': 'structure',
|
2022-05-26 01:13:54 +02:00
|
|
|
'members': {},
|
2015-11-27 23:25:33 +01:00
|
|
|
},
|
2022-05-26 01:13:54 +02:00
|
|
|
'String': {'type': 'string'},
|
|
|
|
},
|
2015-11-27 23:25:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
def build_models(self):
|
|
|
|
self.service_model = ServiceModel(self.json_model)
|
|
|
|
self.operation_model = OperationModel(
|
|
|
|
self.json_model['operations']['SampleOperation'],
|
2022-05-26 01:13:54 +02:00
|
|
|
self.service_model,
|
2015-11-27 23:25:33 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
def add_input_shape(self, shape):
|
|
|
|
self.add_shape(shape)
|
|
|
|
params_shape = self.json_model['shapes']['SampleOperationInputOutput']
|
|
|
|
shape_name = list(shape.keys())[0]
|
|
|
|
params_shape['members'][shape_name] = {'shape': shape_name}
|
|
|
|
|
|
|
|
def add_shape(self, shape):
|
|
|
|
shape_name = list(shape.keys())[0]
|
|
|
|
self.json_model['shapes'][shape_name] = shape[shape_name]
|
|
|
|
|
|
|
|
|
|
|
|
class TestInputOutputTransformer(BaseTransformationTest):
|
|
|
|
def setUp(self):
|
2022-05-26 01:13:54 +02:00
|
|
|
super().setUp()
|
2015-11-27 23:25:33 +01:00
|
|
|
self.transformation = lambda params: self.transformed_value
|
|
|
|
self.add_shape({self.target_shape: {'type': 'string'}})
|
|
|
|
|
|
|
|
def test_transform_structure(self):
|
|
|
|
input_params = {
|
|
|
|
'Structure': {
|
|
|
|
'TransformMe': self.original_value,
|
|
|
|
'LeaveAlone': self.original_value,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
input_shape = {
|
|
|
|
'Structure': {
|
|
|
|
'type': 'structure',
|
|
|
|
'members': {
|
|
|
|
'TransformMe': {'shape': self.target_shape},
|
2022-05-26 01:13:54 +02:00
|
|
|
'LeaveAlone': {'shape': 'String'},
|
|
|
|
},
|
2015-11-27 23:25:33 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
self.add_input_shape(input_shape)
|
|
|
|
self.transformer.transform(
|
2022-05-26 01:13:54 +02:00
|
|
|
params=input_params,
|
|
|
|
model=self.operation_model.input_shape,
|
2015-11-27 23:25:33 +01:00
|
|
|
transformation=self.transformation,
|
2022-05-26 01:13:54 +02:00
|
|
|
target_shape=self.target_shape,
|
|
|
|
)
|
2021-10-04 19:51:32 +02:00
|
|
|
assert input_params == {
|
|
|
|
'Structure': {
|
2015-11-27 23:25:33 +01:00
|
|
|
'TransformMe': self.transformed_value,
|
2022-05-26 01:13:54 +02:00
|
|
|
'LeaveAlone': self.original_value,
|
|
|
|
}
|
|
|
|
}
|
2015-11-27 23:25:33 +01:00
|
|
|
|
|
|
|
def test_transform_map(self):
|
|
|
|
input_params = {
|
|
|
|
'TransformMe': {'foo': self.original_value},
|
2022-05-26 01:13:54 +02:00
|
|
|
'LeaveAlone': {'foo': self.original_value},
|
2015-11-27 23:25:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
targeted_input_shape = {
|
|
|
|
'TransformMe': {
|
|
|
|
'type': 'map',
|
|
|
|
'key': {'shape': 'String'},
|
2022-05-26 01:13:54 +02:00
|
|
|
'value': {'shape': self.target_shape},
|
2015-11-27 23:25:33 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
untargeted_input_shape = {
|
|
|
|
'LeaveAlone': {
|
|
|
|
'type': 'map',
|
|
|
|
'key': {'shape': 'String'},
|
2022-05-26 01:13:54 +02:00
|
|
|
'value': {'shape': 'String'},
|
2015-11-27 23:25:33 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
self.add_input_shape(targeted_input_shape)
|
|
|
|
self.add_input_shape(untargeted_input_shape)
|
|
|
|
|
|
|
|
self.transformer.transform(
|
2022-05-26 01:13:54 +02:00
|
|
|
params=input_params,
|
|
|
|
model=self.operation_model.input_shape,
|
2015-11-27 23:25:33 +01:00
|
|
|
transformation=self.transformation,
|
2022-05-26 01:13:54 +02:00
|
|
|
target_shape=self.target_shape,
|
|
|
|
)
|
2021-10-04 19:51:32 +02:00
|
|
|
assert input_params == {
|
2021-11-03 18:27:47 +01:00
|
|
|
'TransformMe': {'foo': self.transformed_value},
|
2022-05-26 01:13:54 +02:00
|
|
|
'LeaveAlone': {'foo': self.original_value},
|
2021-11-03 18:27:47 +01:00
|
|
|
}
|
2015-11-27 23:25:33 +01:00
|
|
|
|
|
|
|
def test_transform_list(self):
|
|
|
|
input_params = {
|
2022-05-26 01:13:54 +02:00
|
|
|
'TransformMe': [self.original_value, self.original_value],
|
|
|
|
'LeaveAlone': [self.original_value, self.original_value],
|
2015-11-27 23:25:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
targeted_input_shape = {
|
|
|
|
'TransformMe': {
|
|
|
|
'type': 'list',
|
2022-05-26 01:13:54 +02:00
|
|
|
'member': {'shape': self.target_shape},
|
2015-11-27 23:25:33 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
untargeted_input_shape = {
|
2022-05-26 01:13:54 +02:00
|
|
|
'LeaveAlone': {'type': 'list', 'member': {'shape': 'String'}}
|
2015-11-27 23:25:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
self.add_input_shape(targeted_input_shape)
|
|
|
|
self.add_input_shape(untargeted_input_shape)
|
|
|
|
|
|
|
|
self.transformer.transform(
|
2022-05-26 01:13:54 +02:00
|
|
|
params=input_params,
|
|
|
|
model=self.operation_model.input_shape,
|
|
|
|
transformation=self.transformation,
|
|
|
|
target_shape=self.target_shape,
|
|
|
|
)
|
2021-10-04 19:51:32 +02:00
|
|
|
assert input_params == {
|
|
|
|
'TransformMe': [self.transformed_value, self.transformed_value],
|
2022-05-26 01:13:54 +02:00
|
|
|
'LeaveAlone': [self.original_value, self.original_value],
|
|
|
|
}
|
2015-11-27 23:25:33 +01:00
|
|
|
|
|
|
|
def test_transform_nested_structure(self):
|
|
|
|
input_params = {
|
|
|
|
'WrapperStructure': {
|
|
|
|
'Structure': {
|
|
|
|
'TransformMe': self.original_value,
|
2022-05-26 01:13:54 +02:00
|
|
|
'LeaveAlone': self.original_value,
|
2015-11-27 23:25:33 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
structure_shape = {
|
|
|
|
'Structure': {
|
|
|
|
'type': 'structure',
|
|
|
|
'members': {
|
|
|
|
'TransformMe': {'shape': self.target_shape},
|
2022-05-26 01:13:54 +02:00
|
|
|
'LeaveAlone': {'shape': 'String'},
|
|
|
|
},
|
2015-11-27 23:25:33 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
input_shape = {
|
|
|
|
'WrapperStructure': {
|
|
|
|
'type': 'structure',
|
2022-05-26 01:13:54 +02:00
|
|
|
'members': {'Structure': {'shape': 'Structure'}},
|
|
|
|
}
|
2015-11-27 23:25:33 +01:00
|
|
|
}
|
|
|
|
self.add_shape(structure_shape)
|
|
|
|
self.add_input_shape(input_shape)
|
|
|
|
|
|
|
|
self.transformer.transform(
|
2022-05-26 01:13:54 +02:00
|
|
|
params=input_params,
|
|
|
|
model=self.operation_model.input_shape,
|
2015-11-27 23:25:33 +01:00
|
|
|
transformation=self.transformation,
|
2022-05-26 01:13:54 +02:00
|
|
|
target_shape=self.target_shape,
|
|
|
|
)
|
2021-10-04 19:51:32 +02:00
|
|
|
assert input_params == {
|
|
|
|
'WrapperStructure': {
|
2022-05-26 01:13:54 +02:00
|
|
|
'Structure': {
|
|
|
|
'TransformMe': self.transformed_value,
|
|
|
|
'LeaveAlone': self.original_value,
|
2015-11-27 23:25:33 +01:00
|
|
|
}
|
|
|
|
}
|
2022-05-26 01:13:54 +02:00
|
|
|
}
|
2015-11-27 23:25:33 +01:00
|
|
|
|
2022-05-26 01:13:54 +02:00
|
|
|
def test_transform_nested_map(self):
|
|
|
|
input_params = {
|
|
|
|
'TargetedWrapperMap': {'foo': {'bar': self.original_value}},
|
|
|
|
'UntargetedWrapperMap': {'foo': {'bar': self.original_value}},
|
2015-11-27 23:25:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
targeted_map_shape = {
|
|
|
|
'TransformMeMap': {
|
|
|
|
'type': 'map',
|
|
|
|
'key': {'shape': 'String'},
|
2022-05-26 01:13:54 +02:00
|
|
|
'value': {'shape': self.target_shape},
|
2015-11-27 23:25:33 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
targeted_wrapper_shape = {
|
|
|
|
'TargetedWrapperMap': {
|
|
|
|
'type': 'map',
|
|
|
|
'key': {'shape': 'Name'},
|
2022-05-26 01:13:54 +02:00
|
|
|
'value': {'shape': 'TransformMeMap'},
|
|
|
|
}
|
2015-11-27 23:25:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
self.add_shape(targeted_map_shape)
|
|
|
|
self.add_input_shape(targeted_wrapper_shape)
|
|
|
|
|
|
|
|
untargeted_map_shape = {
|
|
|
|
'LeaveAloneMap': {
|
|
|
|
'type': 'map',
|
|
|
|
'key': {'shape': 'String'},
|
2022-05-26 01:13:54 +02:00
|
|
|
'value': {'shape': 'String'},
|
2015-11-27 23:25:33 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
untargeted_wrapper_shape = {
|
|
|
|
'UntargetedWrapperMap': {
|
|
|
|
'type': 'map',
|
|
|
|
'key': {'shape': 'Name'},
|
2022-05-26 01:13:54 +02:00
|
|
|
'value': {'shape': 'LeaveAloneMap'},
|
|
|
|
}
|
2015-11-27 23:25:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
self.add_shape(untargeted_map_shape)
|
|
|
|
self.add_input_shape(untargeted_wrapper_shape)
|
|
|
|
|
|
|
|
self.transformer.transform(
|
2022-05-26 01:13:54 +02:00
|
|
|
params=input_params,
|
|
|
|
model=self.operation_model.input_shape,
|
|
|
|
transformation=self.transformation,
|
|
|
|
target_shape=self.target_shape,
|
|
|
|
)
|
2021-10-04 19:51:32 +02:00
|
|
|
assert input_params == {
|
|
|
|
'TargetedWrapperMap': {'foo': {'bar': self.transformed_value}},
|
2022-05-26 01:13:54 +02:00
|
|
|
'UntargetedWrapperMap': {'foo': {'bar': self.original_value}},
|
|
|
|
}
|
2015-11-27 23:25:33 +01:00
|
|
|
|
|
|
|
def test_transform_nested_list(self):
|
|
|
|
input_params = {
|
|
|
|
'TargetedWrapperList': [
|
|
|
|
[self.original_value, self.original_value]
|
|
|
|
],
|
|
|
|
'UntargetedWrapperList': [
|
|
|
|
[self.original_value, self.original_value]
|
2022-05-26 01:13:54 +02:00
|
|
|
],
|
2015-11-27 23:25:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
targeted_list_shape = {
|
|
|
|
'TransformMe': {
|
|
|
|
'type': 'list',
|
2022-05-26 01:13:54 +02:00
|
|
|
'member': {'shape': self.target_shape},
|
2015-11-27 23:25:33 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
targeted_wrapper_shape = {
|
|
|
|
'TargetedWrapperList': {
|
|
|
|
'type': 'list',
|
2022-05-26 01:13:54 +02:00
|
|
|
'member': {'shape': 'TransformMe'},
|
|
|
|
}
|
2015-11-27 23:25:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
self.add_shape(targeted_list_shape)
|
|
|
|
self.add_input_shape(targeted_wrapper_shape)
|
|
|
|
|
|
|
|
untargeted_list_shape = {
|
2022-05-26 01:13:54 +02:00
|
|
|
'LeaveAlone': {'type': 'list', 'member': {'shape': 'String'}}
|
2015-11-27 23:25:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
untargeted_wrapper_shape = {
|
|
|
|
'UntargetedWrapperList': {
|
|
|
|
'type': 'list',
|
2022-05-26 01:13:54 +02:00
|
|
|
'member': {'shape': 'LeaveAlone'},
|
|
|
|
}
|
2015-11-27 23:25:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
self.add_shape(untargeted_list_shape)
|
|
|
|
self.add_input_shape(untargeted_wrapper_shape)
|
|
|
|
|
|
|
|
self.transformer.transform(
|
2022-05-26 01:13:54 +02:00
|
|
|
params=input_params,
|
|
|
|
model=self.operation_model.input_shape,
|
2015-11-27 23:25:33 +01:00
|
|
|
transformation=self.transformation,
|
2022-05-26 01:13:54 +02:00
|
|
|
target_shape=self.target_shape,
|
|
|
|
)
|
2021-10-04 19:51:32 +02:00
|
|
|
assert input_params == {
|
2021-11-03 18:27:47 +01:00
|
|
|
'TargetedWrapperList': [
|
|
|
|
[self.transformed_value, self.transformed_value]
|
|
|
|
],
|
|
|
|
'UntargetedWrapperList': [
|
|
|
|
[self.original_value, self.original_value]
|
2022-05-26 01:13:54 +02:00
|
|
|
],
|
2021-11-03 18:27:47 +01:00
|
|
|
}
|
2015-11-27 23:25:33 +01:00
|
|
|
|
|
|
|
def test_transform_incorrect_type_for_structure(self):
|
2022-05-26 01:13:54 +02:00
|
|
|
input_params = {'Structure': 'foo'}
|
2015-11-27 23:25:33 +01:00
|
|
|
|
|
|
|
input_shape = {
|
|
|
|
'Structure': {
|
|
|
|
'type': 'structure',
|
|
|
|
'members': {
|
|
|
|
'TransformMe': {'shape': self.target_shape},
|
2022-05-26 01:13:54 +02:00
|
|
|
},
|
2015-11-27 23:25:33 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
self.add_input_shape(input_shape)
|
|
|
|
|
|
|
|
self.transformer.transform(
|
2022-05-26 01:13:54 +02:00
|
|
|
params=input_params,
|
|
|
|
model=self.operation_model.input_shape,
|
2015-11-27 23:25:33 +01:00
|
|
|
transformation=self.transformation,
|
2022-05-26 01:13:54 +02:00
|
|
|
target_shape=self.target_shape,
|
|
|
|
)
|
2021-10-04 19:51:32 +02:00
|
|
|
assert input_params == {'Structure': 'foo'}
|
2015-11-27 23:25:33 +01:00
|
|
|
|
|
|
|
def test_transform_incorrect_type_for_map(self):
|
2022-05-26 01:13:54 +02:00
|
|
|
input_params = {'Map': 'foo'}
|
2015-11-27 23:25:33 +01:00
|
|
|
|
|
|
|
input_shape = {
|
|
|
|
'Map': {
|
|
|
|
'type': 'map',
|
|
|
|
'key': {'shape': 'String'},
|
2022-05-26 01:13:54 +02:00
|
|
|
'value': {'shape': self.target_shape},
|
2015-11-27 23:25:33 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
self.add_input_shape(input_shape)
|
|
|
|
|
|
|
|
self.transformer.transform(
|
2022-05-26 01:13:54 +02:00
|
|
|
params=input_params,
|
|
|
|
model=self.operation_model.input_shape,
|
2015-11-27 23:25:33 +01:00
|
|
|
transformation=self.transformation,
|
2022-05-26 01:13:54 +02:00
|
|
|
target_shape=self.target_shape,
|
|
|
|
)
|
2021-10-04 19:51:32 +02:00
|
|
|
assert input_params == {'Map': 'foo'}
|
2015-11-27 23:25:33 +01:00
|
|
|
|
|
|
|
def test_transform_incorrect_type_for_list(self):
|
2022-05-26 01:13:54 +02:00
|
|
|
input_params = {'List': 'foo'}
|
2015-11-27 23:25:33 +01:00
|
|
|
|
|
|
|
input_shape = {
|
2022-05-26 01:13:54 +02:00
|
|
|
'List': {'type': 'list', 'member': {'shape': self.target_shape}}
|
2015-11-27 23:25:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
self.add_input_shape(input_shape)
|
|
|
|
|
|
|
|
self.transformer.transform(
|
2022-05-26 01:13:54 +02:00
|
|
|
params=input_params,
|
|
|
|
model=self.operation_model.input_shape,
|
|
|
|
transformation=self.transformation,
|
|
|
|
target_shape=self.target_shape,
|
|
|
|
)
|
2021-10-04 19:51:32 +02:00
|
|
|
assert input_params == {'List': 'foo'}
|
2015-11-27 23:25:33 +01:00
|
|
|
|
|
|
|
|
|
|
|
class BaseTransformAttributeValueTest(BaseTransformationTest):
|
|
|
|
def setUp(self):
|
|
|
|
self.target_shape = 'AttributeValue'
|
|
|
|
self.setup_models()
|
|
|
|
self.build_models()
|
|
|
|
self.python_value = 'mystring'
|
|
|
|
self.dynamodb_value = {'S': self.python_value}
|
|
|
|
self.injector = TransformationInjector()
|
|
|
|
self.add_shape({self.target_shape: {'type': 'string'}})
|
|
|
|
|
|
|
|
|
|
|
|
class TestTransformAttributeValueInput(BaseTransformAttributeValueTest):
|
|
|
|
def test_handler(self):
|
|
|
|
input_params = {
|
|
|
|
'Structure': {
|
|
|
|
'TransformMe': self.python_value,
|
2022-05-26 01:13:54 +02:00
|
|
|
'LeaveAlone': 'unchanged',
|
2015-11-27 23:25:33 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
input_shape = {
|
|
|
|
'Structure': {
|
|
|
|
'type': 'structure',
|
|
|
|
'members': {
|
|
|
|
'TransformMe': {'shape': self.target_shape},
|
2022-05-26 01:13:54 +02:00
|
|
|
'LeaveAlone': {'shape': 'String'},
|
|
|
|
},
|
2015-11-27 23:25:33 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
self.add_input_shape(input_shape)
|
|
|
|
|
|
|
|
self.injector.inject_attribute_value_input(
|
2022-05-26 01:13:54 +02:00
|
|
|
params=input_params, model=self.operation_model
|
|
|
|
)
|
2021-10-04 19:51:32 +02:00
|
|
|
assert input_params == {
|
|
|
|
'Structure': {
|
2015-11-27 23:25:33 +01:00
|
|
|
'TransformMe': self.dynamodb_value,
|
2022-05-26 01:13:54 +02:00
|
|
|
'LeaveAlone': 'unchanged',
|
|
|
|
}
|
|
|
|
}
|
2015-11-27 23:25:33 +01:00
|
|
|
|
|
|
|
|
|
|
|
class TestTransformAttributeValueOutput(BaseTransformAttributeValueTest):
|
|
|
|
def test_handler(self):
|
|
|
|
parsed = {
|
|
|
|
'Structure': {
|
|
|
|
'TransformMe': self.dynamodb_value,
|
2022-05-26 01:13:54 +02:00
|
|
|
'LeaveAlone': 'unchanged',
|
2015-11-27 23:25:33 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
input_shape = {
|
|
|
|
'Structure': {
|
|
|
|
'type': 'structure',
|
|
|
|
'members': {
|
|
|
|
'TransformMe': {'shape': self.target_shape},
|
2022-05-26 01:13:54 +02:00
|
|
|
'LeaveAlone': {'shape': 'String'},
|
|
|
|
},
|
2015-11-27 23:25:33 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
self.add_input_shape(input_shape)
|
|
|
|
self.injector.inject_attribute_value_output(
|
2022-05-26 01:13:54 +02:00
|
|
|
parsed=parsed, model=self.operation_model
|
|
|
|
)
|
2021-10-04 19:51:32 +02:00
|
|
|
assert parsed == {
|
|
|
|
'Structure': {
|
2015-11-27 23:25:33 +01:00
|
|
|
'TransformMe': self.python_value,
|
2022-05-26 01:13:54 +02:00
|
|
|
'LeaveAlone': 'unchanged',
|
|
|
|
}
|
|
|
|
}
|
2015-11-27 23:25:33 +01:00
|
|
|
|
2018-07-11 07:39:36 +02:00
|
|
|
def test_no_output(self):
|
2022-05-26 01:13:54 +02:00
|
|
|
service_model = ServiceModel(
|
|
|
|
{
|
|
|
|
'operations': {
|
|
|
|
'SampleOperation': {
|
|
|
|
'name': 'SampleOperation',
|
|
|
|
'input': {'shape': 'SampleOperationInputOutput'},
|
|
|
|
}
|
|
|
|
},
|
|
|
|
'shapes': {
|
|
|
|
'SampleOperationInput': {
|
|
|
|
'type': 'structure',
|
|
|
|
'members': {},
|
|
|
|
},
|
|
|
|
'String': {'type': 'string'},
|
2018-07-11 07:39:36 +02:00
|
|
|
},
|
|
|
|
}
|
2022-05-26 01:13:54 +02:00
|
|
|
)
|
2018-07-11 07:39:36 +02:00
|
|
|
operation_model = service_model.operation_model('SampleOperation')
|
|
|
|
|
|
|
|
parsed = {}
|
|
|
|
self.injector.inject_attribute_value_output(
|
2022-05-26 01:13:54 +02:00
|
|
|
parsed=parsed, model=operation_model
|
|
|
|
)
|
2021-10-04 19:51:32 +02:00
|
|
|
assert parsed == {}
|
2018-07-11 07:39:36 +02:00
|
|
|
|
|
|
|
|
2015-11-27 23:25:33 +01:00
|
|
|
class TestTransformConditionExpression(BaseTransformationTest):
|
|
|
|
def setUp(self):
|
2022-05-26 01:13:54 +02:00
|
|
|
super().setUp()
|
2015-11-27 23:25:33 +01:00
|
|
|
self.add_shape({'ConditionExpression': {'type': 'string'}})
|
|
|
|
self.add_shape({'KeyExpression': {'type': 'string'}})
|
|
|
|
|
|
|
|
shapes = self.json_model['shapes']
|
|
|
|
input_members = shapes['SampleOperationInputOutput']['members']
|
|
|
|
input_members['KeyCondition'] = {'shape': 'KeyExpression'}
|
|
|
|
input_members['AttrCondition'] = {'shape': 'ConditionExpression'}
|
|
|
|
self.injector = TransformationInjector()
|
|
|
|
self.build_models()
|
|
|
|
|
|
|
|
def test_non_condition_input(self):
|
2022-05-26 01:13:54 +02:00
|
|
|
params = {'KeyCondition': 'foo', 'AttrCondition': 'bar'}
|
2015-11-27 23:25:33 +01:00
|
|
|
self.injector.inject_condition_expressions(
|
2022-05-26 01:13:54 +02:00
|
|
|
params, self.operation_model
|
|
|
|
)
|
2021-10-04 19:51:32 +02:00
|
|
|
assert params == {'KeyCondition': 'foo', 'AttrCondition': 'bar'}
|
2015-11-27 23:25:33 +01:00
|
|
|
|
|
|
|
def test_single_attr_condition_expression(self):
|
2022-05-26 01:13:54 +02:00
|
|
|
params = {'AttrCondition': Attr('foo').eq('bar')}
|
2015-11-27 23:25:33 +01:00
|
|
|
self.injector.inject_condition_expressions(
|
2022-05-26 01:13:54 +02:00
|
|
|
params, self.operation_model
|
|
|
|
)
|
2021-10-04 19:51:32 +02:00
|
|
|
assert params == {
|
|
|
|
'AttrCondition': '#n0 = :v0',
|
|
|
|
'ExpressionAttributeNames': {'#n0': 'foo'},
|
2022-05-26 01:13:54 +02:00
|
|
|
'ExpressionAttributeValues': {':v0': 'bar'},
|
|
|
|
}
|
2015-11-27 23:25:33 +01:00
|
|
|
|
|
|
|
def test_single_key_conditon_expression(self):
|
2022-05-26 01:13:54 +02:00
|
|
|
params = {'KeyCondition': Key('foo').eq('bar')}
|
2015-11-27 23:25:33 +01:00
|
|
|
self.injector.inject_condition_expressions(
|
2022-05-26 01:13:54 +02:00
|
|
|
params, self.operation_model
|
|
|
|
)
|
2021-10-04 19:51:32 +02:00
|
|
|
assert params == {
|
|
|
|
'KeyCondition': '#n0 = :v0',
|
|
|
|
'ExpressionAttributeNames': {'#n0': 'foo'},
|
2022-05-26 01:13:54 +02:00
|
|
|
'ExpressionAttributeValues': {':v0': 'bar'},
|
|
|
|
}
|
2015-11-27 23:25:33 +01:00
|
|
|
|
|
|
|
def test_key_and_attr_conditon_expression(self):
|
|
|
|
params = {
|
|
|
|
'KeyCondition': Key('foo').eq('bar'),
|
2022-05-26 01:13:54 +02:00
|
|
|
'AttrCondition': Attr('biz').eq('baz'),
|
2015-11-27 23:25:33 +01:00
|
|
|
}
|
|
|
|
self.injector.inject_condition_expressions(
|
2022-05-26 01:13:54 +02:00
|
|
|
params, self.operation_model
|
|
|
|
)
|
2021-10-04 19:51:32 +02:00
|
|
|
assert params == {
|
|
|
|
'KeyCondition': '#n1 = :v1',
|
|
|
|
'AttrCondition': '#n0 = :v0',
|
|
|
|
'ExpressionAttributeNames': {'#n0': 'biz', '#n1': 'foo'},
|
2022-05-26 01:13:54 +02:00
|
|
|
'ExpressionAttributeValues': {':v0': 'baz', ':v1': 'bar'},
|
|
|
|
}
|
2015-11-27 23:25:33 +01:00
|
|
|
|
|
|
|
def test_key_and_attr_conditon_expression_with_placeholders(self):
|
|
|
|
params = {
|
|
|
|
'KeyCondition': Key('foo').eq('bar'),
|
|
|
|
'AttrCondition': Attr('biz').eq('baz'),
|
|
|
|
'ExpressionAttributeNames': {'#a': 'b'},
|
2022-05-26 01:13:54 +02:00
|
|
|
'ExpressionAttributeValues': {':c': 'd'},
|
2015-11-27 23:25:33 +01:00
|
|
|
}
|
|
|
|
self.injector.inject_condition_expressions(
|
2022-05-26 01:13:54 +02:00
|
|
|
params, self.operation_model
|
|
|
|
)
|
2021-10-04 19:51:32 +02:00
|
|
|
assert params == {
|
|
|
|
'KeyCondition': '#n1 = :v1',
|
|
|
|
'AttrCondition': '#n0 = :v0',
|
|
|
|
'ExpressionAttributeNames': {
|
2022-05-26 01:13:54 +02:00
|
|
|
'#n0': 'biz',
|
|
|
|
'#n1': 'foo',
|
|
|
|
'#a': 'b',
|
|
|
|
},
|
2021-10-04 19:51:32 +02:00
|
|
|
'ExpressionAttributeValues': {
|
2022-05-26 01:13:54 +02:00
|
|
|
':v0': 'baz',
|
|
|
|
':v1': 'bar',
|
|
|
|
':c': 'd',
|
|
|
|
},
|
|
|
|
}
|
2015-11-27 23:25:33 +01:00
|
|
|
|
|
|
|
|
|
|
|
class TestCopyDynamoDBParams(unittest.TestCase):
|
|
|
|
def test_copy_dynamodb_params(self):
|
|
|
|
params = {'foo': 'bar'}
|
|
|
|
new_params = copy_dynamodb_params(params)
|
2021-10-04 19:51:32 +02:00
|
|
|
assert params == new_params
|
|
|
|
assert new_params is not params
|
2015-11-27 23:25:33 +01:00
|
|
|
|
|
|
|
|
|
|
|
class TestDynamoDBHighLevelResource(unittest.TestCase):
|
|
|
|
def setUp(self):
|
|
|
|
self.events = mock.Mock()
|
|
|
|
self.client = mock.Mock()
|
|
|
|
self.client.meta.events = self.events
|
|
|
|
self.meta = ResourceMeta('dynamodb')
|
|
|
|
|
|
|
|
def test_instantiation(self):
|
|
|
|
# Instantiate the class.
|
|
|
|
dynamodb_class = type(
|
2022-05-26 01:13:54 +02:00
|
|
|
'dynamodb',
|
|
|
|
(DynamoDBHighLevelResource, ServiceResource),
|
|
|
|
{'meta': self.meta},
|
|
|
|
)
|
|
|
|
with mock.patch(
|
|
|
|
'boto3.dynamodb.transform.TransformationInjector'
|
|
|
|
) as mock_injector:
|
2015-11-27 23:25:33 +01:00
|
|
|
with mock.patch(
|
|
|
|
'boto3.dynamodb.transform.DocumentModifiedShape.'
|
2022-05-26 01:13:54 +02:00
|
|
|
'replace_documentation_for_matching_shape'
|
|
|
|
) as mock_modify_documentation_method:
|
2015-11-27 23:25:33 +01:00
|
|
|
dynamodb_class(client=self.client)
|
|
|
|
|
|
|
|
# It should have fired the following events upon instantiation.
|
|
|
|
event_call_args = self.events.register.call_args_list
|
2021-10-04 19:51:32 +02:00
|
|
|
assert event_call_args == [
|
|
|
|
mock.call(
|
2015-11-27 23:25:33 +01:00
|
|
|
'provide-client-params.dynamodb',
|
|
|
|
copy_dynamodb_params,
|
2022-05-26 01:13:54 +02:00
|
|
|
unique_id='dynamodb-create-params-copy',
|
|
|
|
),
|
2021-10-04 19:51:32 +02:00
|
|
|
mock.call(
|
2015-11-27 23:25:33 +01:00
|
|
|
'before-parameter-build.dynamodb',
|
|
|
|
mock_injector.return_value.inject_condition_expressions,
|
2022-05-26 01:13:54 +02:00
|
|
|
unique_id='dynamodb-condition-expression',
|
|
|
|
),
|
2021-10-04 19:51:32 +02:00
|
|
|
mock.call(
|
2015-11-27 23:25:33 +01:00
|
|
|
'before-parameter-build.dynamodb',
|
|
|
|
mock_injector.return_value.inject_attribute_value_input,
|
2022-05-26 01:13:54 +02:00
|
|
|
unique_id='dynamodb-attr-value-input',
|
|
|
|
),
|
2021-10-04 19:51:32 +02:00
|
|
|
mock.call(
|
2015-11-27 23:25:33 +01:00
|
|
|
'after-call.dynamodb',
|
|
|
|
mock_injector.return_value.inject_attribute_value_output,
|
2022-05-26 01:13:54 +02:00
|
|
|
unique_id='dynamodb-attr-value-output',
|
|
|
|
),
|
2021-10-04 19:51:32 +02:00
|
|
|
mock.call(
|
2015-11-27 23:25:33 +01:00
|
|
|
'docs.*.dynamodb.*.complete-section',
|
|
|
|
mock_modify_documentation_method,
|
2022-05-26 01:13:54 +02:00
|
|
|
unique_id='dynamodb-attr-value-docs',
|
|
|
|
),
|
2021-10-04 19:51:32 +02:00
|
|
|
mock.call(
|
2015-11-27 23:25:33 +01:00
|
|
|
'docs.*.dynamodb.*.complete-section',
|
|
|
|
mock_modify_documentation_method,
|
2022-05-26 01:13:54 +02:00
|
|
|
unique_id='dynamodb-key-expression-docs',
|
|
|
|
),
|
2021-10-04 19:51:32 +02:00
|
|
|
mock.call(
|
2015-11-27 23:25:33 +01:00
|
|
|
'docs.*.dynamodb.*.complete-section',
|
|
|
|
mock_modify_documentation_method,
|
2022-05-26 01:13:54 +02:00
|
|
|
unique_id='dynamodb-cond-expression-docs',
|
|
|
|
),
|
|
|
|
]
|
2015-11-27 23:25:33 +01:00
|
|
|
|
|
|
|
|
|
|
|
class TestRegisterHighLevelInterface(unittest.TestCase):
|
|
|
|
def test_register(self):
|
|
|
|
base_classes = [object]
|
|
|
|
register_high_level_interface(base_classes)
|
|
|
|
|
|
|
|
# Check that the base classes are as expected
|
2021-10-04 19:51:32 +02:00
|
|
|
assert base_classes == [DynamoDBHighLevelResource, object]
|