Repository URL to install this package:
| 
      
     
      
        
        
        Version: 
        
         
  
        
    
          
          0.4.184  ▾
        
         
  
      
        
      
  
      
  
     | 
    
    lib-py-b2b
  
    /
        change.py
    | 
|---|
from datetime import datetime
from enum import Enum
from typing import Any, Optional
from boto3.dynamodb.conditions import Attr, Or
from dateutil.parser import isoparse
from py_aws_util.logging import log_data
from lib_b2b.persistent import Persistable
from lib_b2b.policy import Policy
from .base import BaseClass
import abc
import logging
from os import environ
logger = logging.getLogger(__name__)
class ChangeRecord(BaseClass, Persistable):
    def __init__(self, before: Any, after: Any, description: str, when: datetime, who: str = 'the System'):
        self.before = before
        self.after = after
        self.description = description
        self.when = when
        self.who = who
    @staticmethod
    def from_dict(data: dict):
        return ChangeRecord(before=data['before'], after=data['after'], description=data['description'],
                            when=isoparse(data['when']), who=data['who'])
    def as_dict(self) -> dict:
        return {
            'before': self.before,
            'after': self.after,
            'description': self.description,
            'when': self.when.isoformat(),
            'who': self.who
        }
    def __str__(self):
        return f"{self.description} by {self.who} on {self.when.strftime('%b %d, %Y at %H:%M:%S')}"
class ChangeRequest:
    def __init__(self, on_behalf_of: str, policy: Policy = None):
        self.policy = policy
        self.changeable_object = None
        self.on_behalf_of = on_behalf_of
    @abc.abstractmethod
    def permitted(self, changeable_object: 'Changeable') -> bool:
        raise NotImplementedError
    @abc.abstractmethod
    def will_change(self, changeable_object: 'Changeable') -> bool:
        """
        Compares the existing data to the change request to determine if there has been a change
        :return: True/False
        :rtype: bool
        """
        raise NotImplementedError
    @abc.abstractmethod
    def apply(self, changeable_object: 'Changeable'):
        raise NotImplementedError
class Changeable:
    @abc.abstractmethod
    def record(self, change_record: ChangeRecord):
        raise NotImplementedError
    @abc.abstractmethod
    def supported_change_request_types(self) -> [ChangeRequest]:
        raise NotImplementedError
    @property
    @abc.abstractmethod
    def changeable_identity(self):
        raise NotImplementedError
    def change(self, changes: [ChangeRequest]) -> 'Changeable':
        for change in changes:
            logger.info(f"Processing change of type: {type(change).__name__}.",
                        extra=log_data(change_type=type(change).__name__, changeable_id=str(self.changeable_identity)))
            change.apply(self)
            logger.info(f"Change of type: {type(change).__name__} applied.",
                        extra=log_data(change_type=type(change).__name__, changeable_id=str(self.changeable_identity)))
            return self
class ChangeDataProvider(metaclass=abc.ABCMeta):
    @abc.abstractmethod
    def version(self) -> Optional[str]:
        """
        returns the version of the change data.  In most cases this is an iso formatted date.
        If is is an ISO formatted date, it should be UTC to ensure consistency.
        :return: String representation of the version.  If None is returned, we will override the
                        data value ignoring the version.
        :rtype: str
        """
    @abc.abstractmethod
    def data(self) -> dict:
        """
        The request data
        :return: dictionary of the change request data
        :rtype: dict
        """
        raise NotImplementedError