Esempio n. 1
0
 def _update_field_in_table_if_exists(
     self, table: Table, field_value: t.Any, field_name: str
 ) -> bool:
     """
     Only write the field for object in table if the objects with matchig PK and SK already exist
     (also updates updated_at).
     Returns true if object existed and therefore update was successful otherwise false.
     """
     try:
         table.update_item(
             Key={
                 "PK": self.get_dynamodb_signal_key(
                     self.SIGNAL_SOURCE_SHORTCODE, self.signal_id
                 ),
                 "SK": self.get_sort_key(self.privacy_group_id),
             },
             # service_resource.Table.update_item's ConditionExpression params is not typed to use its own objects here...
             ConditionExpression=And(Attr("PK").exists(), Attr("SK").exists()),  # type: ignore
             ExpressionAttributeValues={
                 ":f": field_value,
                 ":u": self.updated_at.isoformat(),
             },
             ExpressionAttributeNames={
                 "#F": field_name,
                 "#U": "UpdatedAt",
             },
             UpdateExpression="SET #F = :f, #U = :u",
         )
     except ClientError as e:
         if e.response["Error"]["Code"] != "ConditionalCheckFailedException":
             raise e
         return False
     return True
Esempio n. 2
0
 def write_to_table(self, table: Table):
     """
     Write operations for this object need to be special cased (to avoid overwritting)
     Therefore we do not implement `to_dynamodb_item` however basically the body of that
     method is used in this class's impl of `write_to_table_if_not_found`
     """
     # put_item does not support UpdateExpression
     table.update_item(
         Key={
             "PK": self.get_dynamodb_content_key(self.content_id),
             "SK": self.get_dynamodb_content_type_key(),
         },
         # If ContentRef exists it needs to match or BAD THING(tm) can happen...
         ConditionExpression=Or(
             Attr("ContentRef").not_exists(),
             Attr("ContentRef").eq(self.content_ref)),  # type: ignore
         # Unfortunately while prod is happy with this on multiple lines pytest breaks...
         UpdateExpression=
         """SET ContentType = :ct, ContentRef = :cr, ContentRefType = :crt, SubmissionTimes = list_append(if_not_exists(SubmissionTimes, :empty_list), :s), CreatedAt = if_not_exists(CreatedAt, :c), UpdatedAt = :u ADD AdditionalFields :af""",
         ExpressionAttributeValues={
             ":ct": self.content_type.get_name(),
             ":cr": self.content_ref,
             ":crt": self.content_ref_type.value,
             ":af": self.additional_fields if self.additional_fields else
             {self.ADDITIONAL_FIELDS_PLACE_HOLDER},
             ":s": [s.isoformat() for s in self.submission_times],
             ":c": self.created_at.isoformat(),
             ":u": self.updated_at.isoformat(),
             ":empty_list": [],
         },
     )
Esempio n. 3
0
 def dec(self, table: Table, by=1):
     """
     Increment count. Default by 1, unless specified.
     """
     table.update_item(
         Key={"PK": self.get_pkey(), "SK": self.get_skey()},
         UpdateExpression="SET CurrentCount = if_not_exists(CurrentCount, :zero) - :by",
         ExpressionAttributeValues={":by": by, ":zero": 0},
     )
 def increment_counts(cls, table: Table, counts: t.Dict[str, int]):
     for pg in counts:
         table.update_item(
             # Couldn't find a way to do update_item in batch. Can optimize
             # if found.
             Key={
                 "PK": cls._get_pkey(),
                 "SK": cls._get_skey(pg)
             },
             UpdateExpression=
             "SET WriteCount = if_not_exists(WriteCount, :zero) + :by",
             ExpressionAttributeValues={
                 ":by": counts[pg],
                 ":zero": 0
             },
         )
    def write_to_table(self, table: Table):
        """
        Write operations for this object need to be special cased (to avoid overwritting)
        Therefore we do not implement `to_dynamodb_item`

        If you're curious it would ~look like this:
        def to_dynamodb_item(self) -> dict:
            return {
                "PK": self.get_dynamodb_content_key(self.content_id),
                "SK": self.get_dynamodb_content_type_key(self.content_type),
                "ContentRef": self.content_ref,
                "ContentRefType": self.content_ref_type,
                "AdditionalFields": self.additional_fields,
                "SubmissionTimes": [s.isoformat() for s in self.submission_times],
                "CreatedOn": self.created_at.isoformat(),
                "UpdatedAt": self.updated_at.isoformat(),
            }
        """
        # put_item does not support UpdateExpression
        table.update_item(
            Key={
                "PK": self.get_dynamodb_content_key(self.content_id),
                "SK": self.get_dynamodb_content_type_key(self.content_type),
            },
            # If ContentRef exists it needs to match or BAD THING(tm) can happen...
            ConditionExpression=Or(
                Attr("ContentRef").not_exists(),
                Attr("ContentRef").eq(self.content_ref)),  # type: ignore
            # Unfortunately while prod is happy with this on multiple lines pytest breaks...
            UpdateExpression=
            """SET ContentRef = :cr, ContentRefType = :crt, SubmissionTimes = list_append(if_not_exists(SubmissionTimes, :empty_list), :s), CreatedAt = if_not_exists(CreatedAt, :c), UpdatedAt = :u ADD AdditionalFields :af""",
            ExpressionAttributeValues={
                ":cr": self.content_ref,
                ":crt": self.content_ref_type.value,
                ":af": self.additional_fields
                if self.additional_fields else {"Placeholder"},
                ":s": [s.isoformat() for s in self.submission_times],
                ":c": self.created_at.isoformat(),
                ":u": self.updated_at.isoformat(),
                ":empty_list": [],
            },
        )
Esempio n. 6
0
def update_item(table: Table, wizard: str, update_expression: str,
                attributes: dict, return_values: Any) -> Union[Any, bool]:
    try:
        db_response = table.update_item(Key={'username': wizard},
                                        UpdateExpression=update_expression,
                                        ExpressionAttributeValues=attributes,
                                        ReturnValues=return_values)
        return db_response
    except Exception as e:
        print("Something went wrong: ", e)
        return False
Esempio n. 7
0
    def get_unique(cls, table: Table, signal_type: t.Type[SignalType],
                   signal_value: str) -> "BankedSignalEntry":
        """
        Write to the table if PK / SK does not exist. In either case (exists,
        not exists), return the current unique entry.

        This is a special use-case for BankedSignalEntry. If this is useful to
        other models, we can move it to a mixin or to dynamodb item. If trying
        to generify, note how the update_item query needs a custom update query
        based on what you are trying to write. Generifying may be harder than it
        seems.
        """
        result = table.update_item(
            Key={
                "PK": cls.get_pk(signal_type),
                "SK": cls.get_sk(signal_value),
            },
            UpdateExpression=
            "SET SignalId = if_not_exists(SignalId, :signal_id), SignalType = :signal_type, SignalValue = :signal_value",
            ExpressionAttributeValues={
                # Note we are generating a new uuid even though we don't always
                # expect it to get written. AFAIK, uuids are inexhaustible, and
                # generation performance is good enough to not worry about it.
                ":signal_id": str(uuid.uuid4()),
                ":signal_type": signal_type.get_name(),
                ":signal_value": signal_value,
            },
            ReturnValues="ALL_NEW",
        ).get("Attributes")

        assert result is not None

        return BankedSignalEntry(
            signal_type=signal_type,
            signal_value=t.cast(str, result["SignalValue"]),
            signal_id=t.cast(str, result["SignalId"]),
        )