Exemple #1
0
 def __init__(self, dynamodb_table: str,
              table_schema: Union[Dict[str, type], Dict[str, str]]):
     super().__init__(dynamodb_table=dynamodb_table)
     self.validation = Validation(table_schema=table_schema)
     self.client_exceptions: tuple(Exception) = (
         dynamodb_exceptions.DynamoDbWrongKeyError,
         dynamodb_exceptions.ValidationWrongSchemaTypeError,
         dynamodb_exceptions.ValidationWrongKeyError,
         dynamodb_exceptions.ValidationMissingKeyError,
         dynamodb_exceptions.ValidationIncorrectKeyTypeError,
         dynamodb_exceptions.ValidationIncorrectAttributeError,
         dynamodb_exceptions.ValidationFailedAttributesUpdateError,
         dynamodb_exceptions.DynamoDbInvalidTableError,
         dynamodb_exceptions.DynamoDbWrongKeyFormatError,
     )
Exemple #2
0
 def test_validation_schema_delete_item(self):
     validation: Validation = Validation(
         table_schema=self.generate_schema_template())
     self.assertEqual(
         validation.validation_schema(validation_type="delete_item"),
         validation.dynamodb_key_schema,
     )
Exemple #3
0
 def test_validation_schema_updated_item(self):
     validation: Validation = Validation(
         table_schema=self.generate_schema_template())
     self.assertEqual(
         validation.validation_schema(validation_type="update_item"),
         validation.update_item_schema,
     )
Exemple #4
0
 def test_generate_expression_attribute_values_with_wrong_key(self):
     dynamodb_api: DynamodbApi = DynamodbApi(dynamodb_table="test_table")
     validation: Validation = Validation(
         table_schema=self.generate_schema_template())
     self.assertRaises(ValidationIncorrectAttributeError,
                       dynamodb_api.generate_expression_attribute_values,
                       new_attributes={
                           "address": "Rochet trust drive",
                           "age": "35",
                           "comic": "Batman",
                       },
                       dynamodb_validation_format_mapper={
                           "CustomerId": {
                               "dynamodb_type": "S"
                           },
                           "name": {
                               "dynamodb_type": "S"
                           },
                           "address": {
                               "dynamodb_type": "S"
                           },
                           "age": {
                               "dynamodb_type": "S"
                           },
                           "car": {
                               "dynamodb_type": "S"
                           },
                       },
                       expression_mapping=validation.expression_mapping)
Exemple #5
0
 def test_validate_item_to_readable_format(self):
     validation: Validation = Validation(
         table_schema=self.generate_schema_template())
     self.assertEqual(
         validation.validate_item_to_readable_format(
             dynamodb_item={
                 "CustomerId": {
                     "S": "1482328791"
                 },
                 "name": {
                     "S": "James Joseph"
                 },
                 "address": {
                     "S": "Jeff Bezos Candy land road"
                 },
                 "age": {
                     "S": "32"
                 },
                 "car": {
                     "S": "Black Skoda"
                 },
             }),
         {
             "CustomerId": "1482328791",
             "name": "James Joseph",
             "address": "Jeff Bezos Candy land road",
             "age": "32",
             "car": "Black Skoda",
         },
     )
Exemple #6
0
 def test_validate_attributes_not_updated(self):
     validation: Validation = Validation(
         table_schema=self.generate_schema_template())
     self.assertRaises(
         ValidationIncorrectAttributesError,
         validation.validate_attributes_updated,
         response={
             "Attributes": {
                 "age": {
                     "S": "32"
                 },
                 "car": {
                     "S": "Black Skoda"
                 }
             }
         },
         validated_new_attributes={
             "age": {
                 "S": "35"
             },
             "car": {
                 "S": "Blue BMW"
             }
         },
     )
Exemple #7
0
 def test_expression_expression_mapper(self):
     validation: Validation = Validation(
         table_schema=self.generate_schema_template())
     validation.generate_expression_mapper()
     self.assertEqual(
         validation.expression_mapping,
         {
             "name": {
                 "expression_attribute_name": "#NA",
                 "expression_attribute_var": ":na",
             },
             "address": {
                 "expression_attribute_name": "#AD",
                 "expression_attribute_var": ":ad",
             },
             "age": {
                 "expression_attribute_name": "#A",
                 "expression_attribute_var": ":a",
             },
             "car": {
                 "expression_attribute_name": "#CA",
                 "expression_attribute_var": ":ca",
             },
             "CustomerId": {
                 "expression_attribute_name": "#C",
                 "expression_attribute_var": ":c",
             },
         },
     )
Exemple #8
0
 def test_failed_to_grab_validation_Schema(self):
     validation: Validation = Validation(
         table_schema=self.generate_schema_template())
     self.assertRaises(
         ValidationWrongSchemaTypeError,
         validation.validation_schema,
         validation_type="not a schema",
     )
Exemple #9
0
 def test_generate_key_schema(self):
     validation = Validation(table_schema=self.generate_schema_template())
     self.assertEqual(
         validation.dynamodb_key_schema.json_schema("CustomerId"),
         Schema({
             "CustomerId": And(Use(str))
         }).json_schema("CustomerId"),
     )
Exemple #10
0
 def test_expression_selection(self):
     validation: Validation = Validation(
         table_schema=self.generate_schema_template())
     self.assertEqual(
         validation.expression_selection(attribute="CustomerId"),
         {
             "expression_attribute_name": "#U",
             "expression_attribute_var": ":u"
         },
     )
Exemple #11
0
 def test_bad_format_schema(self):
     with self.assertRaises(ValidationWrongKeyError):
         Validation(
             table_schema={
                 "CustomerId": str,
                 "name": str,
                 "address": str,
                 "age": str,
                 "car": str,
             })
Exemple #12
0
 def test_validate_item_data_entegrity_delete_item(self):
     validation: Validation = Validation(
         table_schema=self.generate_schema_template())
     self.assertEqual(
         validation.validate_item_data_entegrity(
             dynamodb_schema=validation.dynamodb_key_schema,
             unvalidated_item={"CustomerId": "1482328791"},
         ),
         {"CustomerId": "1482328791"},
     )
Exemple #13
0
 def test_generate_update_schema(self):
     validation = Validation(table_schema=self.generate_schema_template())
     self.assertEqual(
         validation.update_item_schema.json_schema("CustomerId"),
         Schema({
             Optional("name"): And(Use(str)),
             Optional("address"): And(Use(str)),
             Optional("age"): And(Use(str)),
             Optional("car"): And(Use(str)),
             "CustomerId": (And(Use(str))),
         }).json_schema("CustomerId"),
     )
Exemple #14
0
 def test_generate_update_expression_with_wrong_key(self):
     dynamodb_api: DynamodbApi = DynamodbApi(dynamodb_table="test_table")
     validation: Validation = Validation(
         table_schema=self.generate_schema_template())
     self.assertRaises(ValidationIncorrectAttributeError,
                       dynamodb_api.generate_update_expression,
                       new_attributes={
                           "address": "Rochet trust drive",
                           "age": "35",
                           "comic": "Batman",
                       },
                       expression_mapping=validation.expression_mapping)
Exemple #15
0
 def test_generate_new_item_schema(self):
     validation = Validation(table_schema=self.generate_schema_template())
     self.assertEqual(
         validation.new_item_schema.json_schema("CustomerId"),
         Schema({
             "name": And(Use(str)),
             "address": And(Use(str)),
             "age": And(Use(str)),
             "car": And(Use(str)),
             "CustomerId": (And(Use(str))),
         }).json_schema("CustomerId"),
     )
Exemple #16
0
 def test_generate_update_expression(self):
     dynamodb_api: DynamodbApi = DynamodbApi(dynamodb_table="test_table")
     validation: Validation = Validation(
         table_schema=self.generate_schema_template())
     self.assertEqual(
         dynamodb_api.generate_update_expression(
             new_attributes={
                 "address": "Rochet trust drive",
                 "age": "35"
             },
             expression_mapping=validation.expression_mapping),
         "SET #A = :a, #AG = :ag",
     )
Exemple #17
0
 def test_validate_new_attributes_exist(self):
     validation: Validation = Validation(
         table_schema=self.generate_schema_template())
     self.assertEqual(
         validation.validate_new_attributes_exist(
             item_attributes={
                 "name": "James Joseph",
                 "address": "Jeff Bezos Candy land road",
             }),
         {
             "name": "James Joseph",
             "address": "Jeff Bezos Candy land road"
         },
     )
Exemple #18
0
 def test_validate_item_data_entegrity_wrong_key(self):
     validation: Validation = Validation(
         table_schema=self.generate_schema_template())
     self.assertRaises(
         ValidationWrongKeyError,
         validation.validate_item_data_entegrity,
         dynamodb_schema=validation.update_item_schema,
         unvalidated_item={
             "CustomerId": "1482328790",
             "name": "James Joseph",
             "address": "Jeff Bezos Candy land road",
             "comics": "Batman",
         },
     )
Exemple #19
0
 def test_validate_attributes_failed_to_update(self):
     validation: Validation = Validation(
         table_schema=self.generate_schema_template())
     self.assertRaises(
         ValidationFailedAttributesUpdateError,
         validation.validate_attributes_updated,
         response={},
         validated_new_attributes={
             "age": {
                 "S": "32"
             },
             "car": {
                 "S": "Black Skoda"
             }
         },
     )
Exemple #20
0
 def test_format_schema(self):
     validation = Validation(table_schema=self.generate_schema_template())
     self.assertEqual(
         (validation.schema_template, validation.key_template),
         (
             {
                 "CustomerId": str,
                 "name": str,
                 "address": str,
                 "age": str,
                 "car": str,
             },
             {
                 "CustomerId": str
             },
         ),
     )
Exemple #21
0
 def test_validate_item_data_entegrity_update_item(self):
     validation: Validation = Validation(
         table_schema=self.generate_schema_template())
     self.assertEqual(
         validation.validate_item_data_entegrity(
             dynamodb_schema=validation.update_item_schema,
             unvalidated_item={
                 "CustomerId": "1482328791",
                 "name": "James Joseph",
                 "address": "Jeff Bezos Candy land road",
             },
         ),
         {
             "CustomerId": "1482328791",
             "name": "James Joseph",
             "address": "Jeff Bezos Candy land road",
         },
     )
Exemple #22
0
 def test_generate_format_mapper(self):
     validation = Validation(table_schema=self.generate_schema_template())
     self.assertEqual(
         validation.dynamodb_format_mapper,
         {
             "name": {
                 "dynamodb_type": "S"
             },
             "address": {
                 "dynamodb_type": "S"
             },
             "age": {
                 "dynamodb_type": "S"
             },
             "car": {
                 "dynamodb_type": "S"
             },
             "CustomerId": {
                 "dynamodb_type": "S"
             },
         },
     )
Exemple #23
0
 def test_validate_item_data_entegrity_int_to_string(self):
     validation: Validation = Validation(
         table_schema=self.generate_schema_template())
     self.assertEqual(
         validation.validate_item_data_entegrity(
             dynamodb_schema=validation.new_item_schema,
             unvalidated_item={
                 "CustomerId": 1482328791,
                 "name": "James Joseph",
                 "address": "Jeff Bezos Candy land road",
                 "age": 32,
                 "car": "Black Skoda",
             },
         ),
         {
             "CustomerId": "1482328791",
             "name": "James Joseph",
             "address": "Jeff Bezos Candy land road",
             "age": "32",
             "car": "Black Skoda",
         },
     )
Exemple #24
0
 def test_generate_expression_attribute_values(self):
     dynamodb_api: DynamodbApi = DynamodbApi(dynamodb_table="test_table")
     validation: Validation = Validation(
         table_schema=self.generate_schema_template())
     self.assertEqual(
         dynamodb_api.generate_expression_attribute_values(
             new_attributes={
                 "address": "Rochet trust drive",
                 "age": "35"
             },
             dynamodb_validation_format_mapper={
                 "CustomerId": {
                     "dynamodb_type": "S"
                 },
                 "name": {
                     "dynamodb_type": "S"
                 },
                 "address": {
                     "dynamodb_type": "S"
                 },
                 "age": {
                     "dynamodb_type": "S"
                 },
                 "car": {
                     "dynamodb_type": "S"
                 },
             },
             expression_mapping=validation.expression_mapping),
         {
             ":a": {
                 "S": "Rochet trust drive"
             },
             ":ag": {
                 "S": "35"
             }
         },
     )
Exemple #25
0
 def test_validate_attributes_updated(self):
     validation: Validation = Validation(
         table_schema=self.generate_schema_template())
     self.assertTrue(
         validation.validate_attributes_updated(
             response={
                 "Attributes": {
                     "age": {
                         "S": "32"
                     },
                     "car": {
                         "S": "Black Skoda"
                     }
                 }
             },
             validated_new_attributes={
                 "age": {
                     "S": "32"
                 },
                 "car": {
                     "S": "Black Skoda"
                 },
             },
         ))
Exemple #26
0
class DynamodbClient(DynamodbApi):
    def __init__(self, dynamodb_table: str,
                 table_schema: Union[Dict[str, type], Dict[str, str]]):
        super().__init__(dynamodb_table=dynamodb_table)
        self.validation = Validation(table_schema=table_schema)
        self.client_exceptions: tuple(Exception) = (
            dynamodb_exceptions.DynamoDbWrongKeyError,
            dynamodb_exceptions.ValidationWrongSchemaTypeError,
            dynamodb_exceptions.ValidationWrongKeyError,
            dynamodb_exceptions.ValidationMissingKeyError,
            dynamodb_exceptions.ValidationIncorrectKeyTypeError,
            dynamodb_exceptions.ValidationIncorrectAttributeError,
            dynamodb_exceptions.ValidationFailedAttributesUpdateError,
            dynamodb_exceptions.DynamoDbInvalidTableError,
            dynamodb_exceptions.DynamoDbWrongKeyFormatError,
        )

    def validate_data(self, validation_type: str,
                      unvalidated_data: Dict[str, str]) -> Dict[str, str]:
        """Designed to validate the data using the validation_schema in the validation module

        Args:
            validation_type (str): Choose between ['new_item', 'update_item', 'delete_item', 'read_item']
            unvalidated_data (Dict[str, str]): The attributes for the item you want to validate are correct

        Returns:
            Dict[str, str]: Validated data with the correct format
        """
        validation_schema: Schema = self.validation.validation_schema(
            validation_type=validation_type)
        return self.validation.validate_item_data_entegrity(
            dynamodb_schema=validation_schema,
            unvalidated_item=unvalidated_data)

    def create_item(self, dynamodb_item: Dict[str, str]) -> Dict[str, int]:
        """Create a new item on the table

        Args:
            dynamodb_item (Dict[str, str]): Attributes for the item, must include the key specified on your table.

        Returns:
            Dict[str, int]: Status code of the results of the action
        """
        try:
            validated_item: Dict[str, str] = self.validate_data(
                validation_type="new_item", unvalidated_data=dynamodb_item)
            formated_db_item: Dict[str, Dict[
                str, str]] = self.validation.validate_item_to_db_format(
                    validated_item)
            self.add_item(dynamodb_item=formated_db_item)
            return {
                "statusCode":
                200,
                "body":
                f"Created new item with key: {validated_item[list(self.validation.key_template.keys())[0]]}",
            }
        except self.client_exceptions as error:
            return {"statusCode": 400, "body": str(error)}

    def delete_existing_attributes(
            self, key: Dict[str, str],
            validated_attributes: Dict[str, str]) -> Dict[str, str]:
        """Deletes any attributes that are the same as the old ones, used for update_item

        Args:
            key (Dict[str, str]): The key for the existing item
            validated_attributes (Dict[str, str]): Attributes that you want to update, parse in validated attributes only

        Returns:
            Dict[str, str]: Returns attributes that are only new
        """
        existing_attributes: Dict[str, Dict[str, str]] = self.get_item(key=key)
        converted_existing_attributes: Dict[
            str, str] = self.validation.validate_item_to_readable_format(
                dynamodb_item=existing_attributes)
        return self.remove_duplicated_attributes(
            new_attributes=validated_attributes,
            old_attributes=converted_existing_attributes,
        )

    def generate_expressions(
        self,
        confirmed_new_attributes: Dict[str,
                                       str]) -> Tuple[str, Dict[str, str]]:
        """Used to generate expressions required to update an item in dynamodb

        Args:
            confirmed_new_attributes (Dict[str, str]): Only parse in the attributes after you've used delete_existing_attributes

        Returns:
            Tuple[str, Dict[str, str]]: Returns the update_expression string, attribute_names and attribute_values
        """
        update_expression: str = self.generate_update_expression(
            new_attributes=confirmed_new_attributes,
            expression_mapping=self.validation.expression_mapping,
        )
        expression_attribute_names: Dict[
            str, str] = self.generate_expression_attribute_names(
                new_attributes=confirmed_new_attributes,
                expression_mapping=self.validation.expression_mapping,
            )
        expression_attribute_values: Dict[
            str, str] = self.generate_expression_attribute_values(
                new_attributes=confirmed_new_attributes,
                dynamodb_validation_format_mapper=self.validation.
                dynamodb_format_mapper,
                expression_mapping=self.validation.expression_mapping,
            )
        return (
            update_expression,
            expression_attribute_names,
            expression_attribute_values,
        )

    def confirm_item_updated(
        self,
        update_response: Dict[str, Dict[str, str]],
        confirmed_new_attributes: Dict[str, str],
    ) -> Union[bool, Exception]:
        """Validates that the item has been updated successfully after you've pushed it through the database

        Args:
            update_response (Dict[str, Dict[str, str]]): The response from the database after you've made the change
            confirmed_new_attributes (Dict[str, str]): Parse in the attributes after using delete_existing_attributes

        Returns:
            Union[bool, Exception]: Returns if it's True or raises an exception if it fails to update
        """

        formated_attributes: Dict[str, Dict[
            str, str]] = self.validation.validate_item_to_db_format(
                dynamodb_item=confirmed_new_attributes)
        return self.validation.validate_attributes_updated(
            response=update_response,
            validated_new_attributes=formated_attributes)

    def update_item(self, dynamodb_attributes: Dict[str,
                                                    str]) -> Dict[str, int]:
        """Updates an existing item to the database

        Args:
            dynamodb_attributes (Dict[str, str]): Provide your key along with the attribute names you want to update

        Returns:
            Dict[str, int]: Returns a status code and a body telling you if it passed or failed
        """
        try:
            validated_attributes: Dict[str, str] = self.validate_data(
                validation_type="update_item",
                unvalidated_data=dynamodb_attributes)
            key: Dict[str, Dict[
                str, str]] = self.validation.validate_item_to_db_format(
                    dynamodb_item={
                        list(self.validation.key_template.keys())[0]:
                        validated_attributes.pop(
                            list(self.validation.key_template.keys())[0])
                    })
            removed_duplicated_attributes: Dict[
                str, str] = self.delete_existing_attributes(
                    key=key, validated_attributes=validated_attributes)
            validated_new_attributes: Dict[
                str, str] = self.validation.validate_new_attributes_exist(
                    item_attributes=removed_duplicated_attributes)
            (
                update_expression,
                expression_attribute_names,
                expression_attribute_values,
            ) = self.generate_expressions(
                confirmed_new_attributes=validated_new_attributes)
            update_response: Dict[str, str] = self.push_update(
                key=key,
                update_expression=update_expression,
                expression_attribute_names=expression_attribute_names,
                expression_attribute_values=expression_attribute_values,
            )
            self.confirm_item_updated(
                update_response=update_response,
                confirmed_new_attributes=validated_new_attributes,
            )
            return {
                "statusCode": 200,
                "body":
                "Item with the key provided has been updated successfully",
            }
        except self.client_exceptions as error:
            return {"statusCode": 400, "body": str(error)}

    def fetch_item(
            self, key: Dict[str,
                            str]) -> Dict[str, Union[int, Dict[str, str]]]:
        """Get an existing item from the database

        Args:
            key (Dict[str, str]): They key for the item in the database

        Returns:
            Dict[str, Union[int, Dict[str, str]]]: Returns the status code and the item or the error why it failed
        """
        try:
            validated_key: Dict[str, str] = self.validate_data(
                validation_type="read_item", unvalidated_data=key)
            formated_key: Dict[str, Dict[
                str, str]] = self.validation.validate_item_to_db_format(
                    dynamodb_item=validated_key)
            fetched_item: Dict[str, str] = self.get_item(key=formated_key)
            readable_item: Dict[
                str, str] = self.validation.validate_item_to_readable_format(
                    dynamodb_item=fetched_item)
            return {"statusCode": 200, "body": readable_item}
        except self.client_exceptions as error:
            return {"statusCode": 400, "body": str(error)}

    def fetch_items(
            self) -> Dict[str, Union[int, Union[str, List[Dict[str, str]]]]]:
        """Fetch a bulk amount of items from the database

        Returns:
            Dict[str, Union[int, Union[str, List[Dict[str, str]]]]]: Returns either a status code with a list of dictionaries or an error body
        """
        try:
            unformated_table_items: List[Dict[str,
                                              Dict[str,
                                                   str]]] = self.get_items()
            formated_table_items: List[Dict[str, str]] = [
                self.validation.validate_item_to_readable_format(table_item)
                for table_item in unformated_table_items
            ]
            return {"statusCode": 200, "body": formated_table_items}
        except self.client_exceptions as error:
            return {"statusCode": 400, "body": str(error)}

    def delete_item(self, key: Dict[str, str]) -> Dict[str, int]:
        """Delete an existing item from the database

        Args:
            key (Dict[str, str]): The key for the item in the database

        Returns:
            Dict[str, int]: Returns a status code either passing or failing.
        """
        try:
            validated_key: Dict[str, str] = self.validate_data(
                validation_type="delete_item", unvalidated_data=key)
            formated_key: Dict[str, Dict[
                str, str]] = self.validation.validate_item_to_db_format(
                    dynamodb_item=validated_key)
            self.remove_item(key=formated_key)
            return {
                "statusCode":
                200,
                "body":
                f"Item with key: {validated_key[list(self.validation.key_template.keys())[0]]} has been deleted",
            }
        except self.client_exceptions as error:
            return {"statusCode": 400, "body": str(error)}
Exemple #27
0
 def test_creating_validation_class(self):
     validation = Validation(table_schema=self.generate_schema_template())
     self.assertIsInstance(validation, Validation)
Exemple #28
0
 def test_validate_new_attributes_do_not_exist(self):
     validation: Validation = Validation(
         table_schema=self.generate_schema_template())
     with self.assertRaises(ValidationNoNewAttributesError):
         validation.validate_new_attributes_exist(item_attributes={})