Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Building a container with a list of derived classes #762

Open
gitisz opened this issue Nov 3, 2023 · 0 comments
Open

Building a container with a list of derived classes #762

gitisz opened this issue Nov 3, 2023 · 0 comments

Comments

@gitisz
Copy link

gitisz commented Nov 3, 2023

Thank you for this clean DI framework!

I have a CLI that I am attempting to refactor, where in it I have a concept of Services. This is a list of configurable objects, but all should be derived from a common base object.

The documentation points to providers.List, which seemed promising except it requires arguments to be coded.

Here is a picture of our configuration:

        self.file = {
            "Cli": {
                "Version": "latest",
                "Resources": {
                    "Qa": {
                        "Failover": {
                            "Activities": [
                                "qa-activity-one",
                                "qa-activity-two",
                            ],
                            "Services": [
                                {
                                    "CloudWatchService": {
                                        "Name": "qa-cloudwatch-service-1"
                                    }
                                },
                                {"S3Service": {"Name": "qa-s3-service-1"}},
                            ],
                            "Configuration": {},
                        }
                    },
                    "Prod": {
                        "Failover": {
                            "Activities": [
                                "prod-activity-one",
                                "prod-activity-two",
                            ],
                            "Services": [
                                {
                                    "CloudWatchService": {
                                        "Name": "prod-cloudwatch-service-1"
                                    }
                                },
                                {"S3Service": {"Name": "prod-s3-service-1"}},
                            ],
                            "Configuration": {},
                        }
                    },
                },
            }
        }

From it, Services is an array of different service types.

class BaseService:
    def __init__(self, Name):
        self.Name = Name

    def perform(self):
        pass


class CloudWatchService(BaseService):
    def __init__(self, Name):
        self.Name = Name

    def perform(self):
        logger.debug("CloudWatchService")


class S3Service(BaseService):
    def __init__(self, Name):
        self.Name = Name

    def perform(self):
        logger.debug("S3Service")


class Failover:
    """
    Contains Activities, Services, and Configuration, while also responsible for providing EffectiveActivities.
    """

    def __init__(self, Activities, Services, Configuration):
        self.Activities = Activities
        self.Services = Services
        self.Configuration = Configuration


class Environment:
    """
    Provides for a selectable environment, such as Dev, Qa, and Prod containing the full graph of Failover configuration.
    """

    def __init__(self, Failover):
        self.Failover = Failover


class Resources:
    def __init__(self, Environment):
        self.Environment = Environment


class Cli:
    def __init__(self, Version, Resources):
        self.Version = Version
        self.Resources = Resources

When creating a container, this works, except the Services are still a dictionary and not converted to classes:

class Container(containers.DynamicContainer):
    def getFailoverProvider(environment, resources):
        activities = resources[environment].Failover.Activities
        configuration = resources[environment].Failover.Configuration
        services = providers.Factory(list, resources[environment].Failover.Services)
        failover = providers.Singleton(
            Failover,
            Activities=activities,
            Configuration=configuration,
            Services=services
        )
        return failover
    config = providers.Configuration()
    environment = providers.Selector(
        config.environment,
        Qa=providers.Singleton(
            Environment, Failover=getFailoverProvider("Qa", config.OneCli.Resources)
        ),
        Prod=providers.Singleton(
            Environment, Failover=getFailoverProvider("Prod", config.OneCli.Resources)
        ),
    )
    resources = providers.Singleton(Resources, Environment=environment)
    Cli = providers.Singleton(OneCli, config.OneCli.Version, resources)

Here is my test:

class FailoverTest(unittest.TestCase):
    def setUp(self):
        logger = logging.getLogger()
        logger.level = logging.INFO
        stream_handler = logging.StreamHandler(sys.stdout)
        logger.addHandler(stream_handler)

        self.file = {
            "Cli": {
                "Version": "latest",
                "Resources": {
                    "Qa": {
                        "Failover": {
                            "Activities": [
                                "qa-activity-one",
                                "qa-activity-two",
                            ],
                            "Services": [
                                {
                                    "CloudWatchService": {
                                        "Name": "qa-cloudwatch-service-1"
                                    }
                                },
                                {"S3Service": {"Name": "qa-s3-service-1"}},
                            ],
                            "Configuration": {},
                        }
                    },
                    "Prod": {
                        "Failover": {
                            "Activities": [
                                "prod-activity-one",
                                "prod-activity-two",
                            ],
                            "Services": [
                                {
                                    "CloudWatchService": {
                                        "Name": "prod-cloudwatch-service-1"
                                    }
                                },
                                {"S3Service": {"Name": "prod-s3-service-1"}},
                            ],
                            "Configuration": {},
                        }
                    },
                },
            }
        }

    def test_qa_failover_init(self):
        # Arrange
        container = Container()
        container.config.from_dict(self.file)
        container.config.environment.from_value("Qa")

        # Act
        logger.debug(container.config)
        cli = container.Cli()

        # Assert
        self.assertIsInstance(cli, Cli)
        self.assertTrue(oneCli.Version == "latest")

        self.assertIsInstance(cli.Resources, Resources)
        self.assertIsNotNone(cli.Resources)

        self.assertIsInstance(cli.Resources.Environment, Environment)
        self.assertIsNotNone(cli.Resources.Environment)
        self.assertIn(
            "qa-activity-one", cli.Resources.Environment.Failover.Activities
        )
        
        # I don't want this: name = cli.Resources.Environment.Failover.Services[0]['CloudWatchService']['Name']
        # I want this: name = cli.Resources.Environment.Failover.Services[0].CloudWatchService.Name

How can create list of derived objects based from the configuration loaded at runtime?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant