def test_get_attributes_from_existing_table_custom_key_name_attribute_name( self): mock_table = mock.Mock() mock_table.get_item.return_value = { "Item": { "custom_attr": self.attributes } } self.dynamodb_resource.Table.return_value = mock_table self.partition_keygen.return_value = "test_partition_key" test_dynamodb_adapter = DynamoDbAdapter( table_name="test_table", partition_keygen=self.partition_keygen, dynamodb_resource=self.dynamodb_resource, partition_key_name="custom_key", attribute_name="custom_attr") assert test_dynamodb_adapter.get_attributes( request_envelope=self.request_envelope ) == self.attributes, ( "Get attributes from dynamodb table retrieves wrong values when " "custom partition key name and " "custom attribute name passed") self.dynamodb_resource.Table.assert_called_once_with("test_table"), ( "Existing table name passed incorrectly to dynamodb get table " "call") self.partition_keygen.assert_called_once_with(self.request_envelope), ( "Partition Keygen provided incorrect input parameters during get " "item call") mock_table.get_item.assert_called_once_with( Key={"custom_key": "test_partition_key"} ), ("Partition keygen provided incorrect key for get attributes call")
def test_get_attributes_from_existing_table(self): mock_table = mock.Mock() mock_table.get_item.return_value = { "Item": { "attributes": self.attributes } } self.dynamodb_resource.Table.return_value = mock_table self.partition_keygen.return_value = "test_partition_key" test_dynamodb_adapter = DynamoDbAdapter( table_name="test_table", partition_keygen=self.partition_keygen, dynamodb_resource=self.dynamodb_resource) assert test_dynamodb_adapter.get_attributes( request_envelope=self.request_envelope) == self.attributes, ( "Get attributes from dynamodb table retrieves wrong values") self.dynamodb_resource.Table.assert_called_once_with("test_table"), ( "Existing table name passed incorrectly to dynamodb get " "table call") self.partition_keygen.assert_called_once_with(self.request_envelope), ( "Partition Keygen provided incorrect input parameters during get " "attributes call") mock_table.get_item.assert_called_once_with( Key={"id": "test_partition_key"}, ConsistentRead=True ), ("Partition keygen provided incorrect key for get attributes call")
def test_get_attributes_fails_with_no_existing_table_create_table_default_false( self): self.dynamodb_resource.Table.side_effect = ResourceNotExistsError( "test", "test", "test") self.dynamodb_resource.create_table.return_value = "test" test_dynamodb_adapter = DynamoDbAdapter( table_name="test_table", partition_keygen=self.partition_keygen, dynamodb_resource=self.dynamodb_resource) with self.assertRaises(PersistenceException) as exc: test_dynamodb_adapter.get_attributes( request_envelope=self.request_envelope) assert "DynamoDb table test_table doesn't exist" in str( exc.exception), ( "Get attributes didn't raise Persistence Exception when no " "existing table and create table set as false") self.dynamodb_resource.create_table.assert_not_called(), ( "Create table called on dynamodb resource when create_table " "flag is set as False")
def test_get_attributes_from_existing_table_get_item_fails(self): mock_table = mock.Mock() mock_table.get_item.side_effect = Exception("test exception") self.dynamodb_resource.Table.return_value = mock_table self.partition_keygen.return_value = "test_partition_key" test_dynamodb_adapter = DynamoDbAdapter( table_name="test_table", partition_keygen=self.partition_keygen, dynamodb_resource=self.dynamodb_resource) with self.assertRaises(PersistenceException) as exc: test_dynamodb_adapter.get_attributes( request_envelope=self.request_envelope) assert "Failed to retrieve attributes from DynamoDb table" in str( exc.exception ), ("Get attributes didn't raise Persistence Exception when get item " "failed on dynamodb resource") mock_table.get_item.assert_called_once_with( Key={"id": "test_partition_key"} ), ("Partition keygen provided incorrect key for get attributes call")
def test_get_attributes_from_existing_table_get_item_returns_no_item(self): mock_table = mock.Mock() mock_table.get_item.return_value = {"attributes": self.attributes} self.dynamodb_resource.Table.return_value = mock_table self.partition_keygen.return_value = "test_partition_key" test_dynamodb_adapter = DynamoDbAdapter( table_name="test_table", partition_keygen=self.partition_keygen, dynamodb_resource=self.dynamodb_resource) assert test_dynamodb_adapter.get_attributes( request_envelope=self.request_envelope) == {}, ( "Get attributes returns incorrect response when no item is " "present in dynamodb table for provided key")
class BaseRequestHandler(AbstractRequestHandler): """ This is the Base request handler the other handlers will inherit from. It contains basic functions such as the saving and access data from and to DynamoDB, using the DynamoDB persistence adaptor. This data can be both user data and analytics data """ SLOTS = [] # Filled in every handler def __init__(self): super().__init__() self.slot_values = {} # Initializing an empty dict, to read slots later for slot_key in self.SLOTS: self.slot_values[slot_key] = { "synonym": None, "resolved": None, "is_validated": False, } self.dynamodb = boto3.resource('dynamodb', region_name="eu-west-1", aws_access_key_id="", aws_secret_access_key="") # TODO add here AWS keys # Default partition keygen uses user_id in request_envelope as ID table_name = None # TODO user attributes table name self.dynamo_client = DynamoDbAdapter(table_name=table_name, partition_key_name="user_id", partition_keygen=user_id_partition_keygen, # default create_table=False, # default dynamodb_resource=self.dynamodb) @abstractmethod def can_handle(self, handler_input): """ Base method, not implemented here """ raise NotImplementedError def handle(self, handler_input): """ Super handler, called by every intent that wants to save request data """ BaseRequestInterceptor().process(handler_input=handler_input, request_handler=self) def get_attributes(self, handler_input): """ Gets user attributes from DynamoDB """ user_attr = self.dynamo_client.get_attributes(request_envelope=handler_input.request_envelope) if len(user_attr) == 0: # If the user didn't exist, we create a default set of attributes with the key 'first_use' to signal it default_attr = self.set_default_attributes(handler_input) default_attr['first_use'] = True return default_attr return user_attr def set_default_attributes(self, handler_input): """ Sets default user attributes to DynamoDB """ attr = { 'custom_attr': 'TODO', } self.dynamo_client.save_attributes(request_envelope=handler_input.request_envelope, attributes=attr) return attr def set_attributes(self, handler_input, attr): """ Overwrite user attributes to DynamoDB """ user_attr = self.dynamo_client.get_attributes(request_envelope=handler_input.request_envelope) for changed_attr in attr: user_attr[changed_attr] = attr[changed_attr] self.dynamo_client.save_attributes(request_envelope=handler_input.request_envelope, attributes=user_attr) # --------- Other methods def get_slot_values(self, filled_slots): """ Return slot values with additional info, to understand if the slots were filled """ if DEBUG: logger.info("Filled slots: {}".format(filled_slots).replace("\n", "\r")) if filled_slots is None: return {} for key, slot_item in six.iteritems(filled_slots): name = slot_item.name try: status_code = slot_item.resolutions.resolutions_per_authority[0].status.code if status_code == StatusCode.ER_SUCCESS_MATCH: self.slot_values[name] = { "synonym": slot_item.value, "resolved": slot_item.resolutions.resolutions_per_authority[0].values[0].value.__dict__, "is_validated": True, } elif status_code == StatusCode.ER_SUCCESS_NO_MATCH: self.slot_values[name] = { "synonym": slot_item.value, "resolved": slot_item.value, "is_validated": False, } else: pass except (AttributeError, ValueError, KeyError, IndexError, TypeError) as e: # for BUILT-IN intents, there are no resolutions, but the value is specified if slot_item.value is not None and slot_item.value != 'NONE': self.slot_values[name] = { "synonym": slot_item.value, "resolved": slot_item.value, "is_validated": True, } else: if DEBUG: logger.info("SLOT {} UNRESOLVED".format(name)) self.slot_values[name] = { "synonym": slot_item.value, "resolved": slot_item.value, "is_validated": False, } return self.slot_values