import importlib from sqlalchemy import Column, ForeignKey, Enum as SQLAEnum from sqlalchemy.orm import relationship, reconstructor from sqlalchemy.dialects.postgresql import UUID from pydantic import ValidationError as PydanticValidationError from transitions import Machine from transitions.extensions.states import add_state_features, Tags from flask import current_app as app from atat.domain.csp.cloud.exceptions import ConnectionException, UnknownServerException from atat.database import db from atat.models.types import Id from atat.models.base import Base import atat.models.mixins as mixins from atat.models.mixins.state_machines import ( FSMStates, AzureStages, StageStates, _build_transitions, ) class StateMachineMisconfiguredError(Exception): def __init__(self, class_details): self.class_details = class_details @property def message(self): return self.class_details def _stage_to_classname(stage): return "".join(map(lambda word: word.capitalize(), stage.split("_"))) def _stage_state_to_stage_name(state, stage_state): return state.name.split(f"_{stage_state.name}")[0].lower() def get_stage_csp_class(stage, class_type): """ given a stage name and class_type return the class class_type is either 'payload' or 'result' """ cls_name = f"{_stage_to_classname(stage)}CSP{class_type.capitalize()}" try: return getattr( importlib.import_module("atat.domain.csp.cloud.models"), cls_name ) except AttributeError: raise StateMachineMisconfiguredError( f"could not import CSP Payload/Result class {cls_name}" ) @add_state_features(Tags) class StateMachineWithTags(Machine): pass class PortfolioStateMachine( Base, mixins.TimestampsMixin, mixins.AuditableMixin, mixins.DeletableMixin, mixins.FSMMixin, ): __tablename__ = "portfolio_state_machines" id = Id() portfolio_id = Column(UUID(as_uuid=True), ForeignKey("portfolios.id"),) portfolio = relationship("Portfolio", back_populates="state_machine") state = Column( SQLAEnum(FSMStates, native_enum=False, create_constraint=False), default=FSMStates.UNSTARTED, nullable=False, ) def __init__(self, portfolio, csp=None, **kwargs): self.portfolio = portfolio self.attach_machine() def after_state_change(self, event): db.session.add(self) db.session.commit() def __repr__(self): return f"