modular_sdk/services/tenant_service.py (175 lines of code) (raw):

from typing import Optional, Iterator, Union, List from pynamodb.pagination import ResultIterator from modular_sdk.commons import RESPONSE_BAD_REQUEST_CODE, default_instance, \ deprecated from modular_sdk.commons.constants import ALLOWED_TENANT_PARENT_MAP_KEYS from modular_sdk.commons.exception import ModularException from modular_sdk.commons.log_helper import get_logger from modular_sdk.models.parent import Parent from modular_sdk.models.pynamodb_extension.pynamodb_to_pymongo_adapter import \ Result from modular_sdk.models.tenant import Tenant _LOG = get_logger(__name__) class TenantService: @staticmethod def get(tenant_name: str, attributes_to_get: Optional[list] = None) -> Optional[Tenant]: return Tenant.get_nullable( hash_key=tenant_name, attributes_to_get=attributes_to_get ) def does_exist(self, tenant_name: str, is_active: Optional[bool] = None) -> bool: """ Use this method only if you really don't need the tenant instance. Only if you need to know whether the tenant exists :param tenant_name: :param is_active: if None, this attribute is ignored. If bool, the tenant will be checked :return: """ if isinstance(is_active, bool): item = self.get(tenant_name, attributes_to_get=[Tenant.name, Tenant.is_active]) return bool(item) and item.is_active == is_active # is_active == None return bool(self.get(tenant_name, attributes_to_get=[Tenant.name])) @staticmethod def scan_tenants(only_active=False, limit: int = None, last_evaluated_key: Union[dict, str] = None): return list(TenantService.i_scan_tenants( only_active, limit, last_evaluated_key)) @staticmethod def i_scan_tenants(only_active=False, limit: int = None, last_evaluated_key: Union[dict, str] = None): condition = None if only_active: condition &= Tenant.is_active == True return Tenant.scan(limit=limit, last_evaluated_key=last_evaluated_key, filter_condition=condition) @classmethod @deprecated('Logic is deprecated') def get_tenants_by_parent_id(cls, parent_id, only_active=True): return list(cls.i_get_tenant_by_parent_id(parent_id, only_active)) @staticmethod @deprecated('Logic is deprecated') def i_get_tenant_by_parent_id(parent_id: str, active: Optional[bool] = None, limit: Optional[int] = None, last_evaluated_key: Optional[dict] = None ) -> Iterator[Tenant]: """ TODO management parent id is not the only one parent id within a tenant. What about others? """ condition = active if active is None else (Tenant.is_active == active) if condition is not None: condition &= Tenant.management_parent_id == parent_id return Tenant.scan( filter_condition=condition, limit=limit, last_evaluated_key=last_evaluated_key ) @staticmethod def i_get_tenant_by_customer( customer_id: str, active: Optional[bool] = None, tenant_name: Optional[str] = None, limit: int = None, last_evaluated_key: Union[dict, str] = None, cloud: Optional[str] = None, attributes_to_get: Optional[list] = None, rate_limit: Optional[int] = None ) -> Union[ResultIterator, Result]: condition = active if active is None else (Tenant.is_active == active) name = default_instance(tenant_name, str) if condition is not None and name: condition &= Tenant.name == name elif name: condition = Tenant.name == name if condition is not None and cloud: condition &= Tenant.cloud == cloud elif cloud: condition = Tenant.cloud == cloud return Tenant.customer_name_index.query( hash_key=customer_id, filter_condition=condition, limit=limit, last_evaluated_key=last_evaluated_key, attributes_to_get=attributes_to_get, rate_limit=rate_limit ) @staticmethod def i_get_by_acc(acc: str, active: Optional[bool] = None, limit: int = None, last_evaluated_key: Union[dict, str] = None, attributes_to_get: List[str] = None): condition = active if active is None else (Tenant.is_active == active) return Tenant.project_index.query( hash_key=acc, filter_condition=condition, limit=limit, last_evaluated_key=last_evaluated_key, attributes_to_get=attributes_to_get ) @staticmethod def i_get_by_dntl( dntl: str, cloud: str = None, active: Optional[bool] = None, limit: int = None, last_evaluated_key: Union[dict, str] = None, attributes_to_get: List[str] = None ): fc = None if active is None else (Tenant.is_active == active) rc = None if cloud is None else (Tenant.cloud == cloud.upper()) return Tenant.dntl_c_index.query( hash_key=dntl, range_key_condition=rc, filter_condition=fc, limit=limit, last_evaluated_key=last_evaluated_key, attributes_to_get=attributes_to_get ) @staticmethod @deprecated('Use i_get_by_acc instead') def i_get_by_accN(accN: str, active: Optional[bool] = None, limit: int = None, last_evaluated_key: Union[dict, str] = None, attributes_to_get: List[str] = None): fc = None if active is None else (Tenant.is_active == active) return Tenant.project_index.query( hash_key=accN, filter_condition=fc, limit=limit, last_evaluated_key=last_evaluated_key, attributes_to_get=attributes_to_get ) @staticmethod def add_to_parent_map(tenant: Tenant, parent: Parent, type_: str) -> Tenant: if type_ not in ALLOWED_TENANT_PARENT_MAP_KEYS: _LOG.warning(f'Unsupported type \'{type_}\'. Available options: ' f'{", ".join(ALLOWED_TENANT_PARENT_MAP_KEYS)}') raise ModularException( code=RESPONSE_BAD_REQUEST_CODE, content=f'Unsupported type \'{type_}\'. Available options: ' f'{", ".join(ALLOWED_TENANT_PARENT_MAP_KEYS)}' ) if not tenant.is_active: _LOG.warning(f'Tenant \'{tenant.name}\' is not active.') raise ModularException( code=RESPONSE_BAD_REQUEST_CODE, content=f'Tenant \'{tenant.name}\' is not active.' ) if parent.is_deleted: _LOG.warning(f'Parent \'{parent.parent_id}\' is deleted.') raise ModularException( code=RESPONSE_BAD_REQUEST_CODE, content=f'Tenant \'{tenant.name}\' is deleted.' ) parent_map = tenant.parent_map.as_dict() # default "dict" if type_ in parent_map: _LOG.warning(f'Tenant \'{tenant.name}\' already has \'{type_}\' ' f'linkage type.') raise ModularException( code=RESPONSE_BAD_REQUEST_CODE, content=f'Tenant \'{tenant.name}\' already has \'{type_}\' ' f'linkage type.' ) # we can update just one attribute only in # case the map already exists in DB. Otherwise -> ValidationException parent_map[type_] = parent.parent_id tenant.update(actions=[ Tenant.parent_map.set(parent_map) ]) return tenant # no need to return @staticmethod def remove_from_parent_map(tenant: Tenant, type_) -> Tenant: if not tenant.is_active: _LOG.warning(f'Tenant \'{tenant.name}\' is not active.') return tenant tenant.update(actions=[ Tenant.parent_map[type_].remove() ]) return tenant @staticmethod def get_dto(tenant: Tenant): """Be CAREFUL: returns both active and inactive regions""" tenant_json = tenant.get_json() regions = tenant_json.get('regions') or [] tenant_json['account_id'] = tenant_json.pop('project', None) tenant_json['regions'] = [ each['maestro_name'] for each in regions if 'maestro_name' in each ] return tenant_json