Ejemplo n.º 1
0
    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")
Ejemplo n.º 3
0
    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")
Ejemplo n.º 4
0
    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")
Ejemplo n.º 5
0
    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