Skip to content

Code Examples

Using the addon_lime_automation.sdk module

As a consultant you can use the module addon_lime_automation.sdk in your solution to easily create automated flow participants connected to a LimeObject.

All you have to do is:

1. Add import of sdk to the top of your file.

import addon_lime_automation.sdk as automation_sdk

2. Create a new instance of the AutomatedFlowParticipantFactory like this:

factory = automation_sdk.AutomatedFlowParticipantFactory(application)

3. Use your factory instance to create automated flow participants. Just supply an automated flow, and the related lime object.

uow = application.unit_of_work()
new_participant, *affected_objects = factory.init_automatedflow_participant(
    automatedflow=my_automatedflow,
    related_object=my_person,
    # "set_to_ready" is default True.
    # Remove it if you want to send it directly to Lime Marketing
    set_to_ready=False,
)

4. Add the objects to your unit of work

uow.add(new_participant)
for affected_object in affected_objects:
    uow.add(affected_object)

5. Set participant to ready and commit your unit of work to send the participant to Lime Marketing.

# Or if it's already set to true in the init function above
new_participant.properties.isready.value = True
uow.commit()

Building an event handler that creates new participants

Create an event handler using lime-project See this guide if you need help.

Use the AutomatedFlowParticipantFactory to create a new automated flow participant when the company changes status from Prospect to Customer.

import addon_lime_automation.sdk as automation_sdk


def company_status_changed(worker, body, message):
    """Summarize your event handlers's functionality here"""
    logger.info("Received message: {}".format(body))
    application = worker.get_application_from_message(message)
    if (
        "buyingstatus" in body["original_values"]
        and "buyingstatus" in body["values"]
    ):
        if body["values"]["buyingstatus"] == "customer":
            logger.info(
                'Company status changed to Customer, \
                add responsible person to "New customers" automated flow'
            )

            uow = application.unit_of_work()
            new_customers_flow = 1001
            company = application.limetypes.company.get(body["values"]["id"])

            factory = automation_sdk.AutomatedFlowParticipantFactory(application)
            # property "isready" is set to True by default,
            # but can be changed using the argument "set_to_ready" below
            # if True, it will automatically be sent to Lime Marketing
            affected_objects = factory.init_automatedflow_participant(
                automatedflow=new_customers_flow,
                related_object=company,
            )
            for affected_object in affected_objects:
                uow.add(affected_object)
            uow.commit()

    message.ack()

Writing tests using automatedflowparticipant or automatedflowproperty

When writing tests where you use the AutomatedFlowParticipant or AutomatedFlowProperty limeobjects you might notice that you get an error similar to this:

.venv\lib\site-packages\mtech_addon_shared\services\configuration_service.py:54: NotFoundError

It is caused by not having a runtime config for addon-lime-automation in the context of the test, so it will not be an issue when running the code on the server. To fix this:

  • Create a json-blob of the config and manually set it with lime_data.set_data(). You can find an example config you use here. This can also be done in a fixture.
from lime_data import set_data

cfg = { ... }  # your example config
set_data(self.app, key, json.dumps(cfg), overwrite=True)

Using addon-lime-automation with multiple lime-marketing applications

By default the addon can only be configured to integrate with one lime-marketing application with the API settings made in application_config. There are some cases when the customer uses object access and multiple segments share the same Lime database but have multiple lime-marketing applications. To support this sceanrio the default configuration behavior can be overridden by providing a custom ConfigResolver. In the custom implementation of the resolver it can handle different settings depending on which automated flow participant is in the current context. IE if the participant belongs to this flow which belongs to this group use these lime-marketing api settings.

Warning

The config is subject to change. Use a fixed version of addon-lime-automation in your solution to avoid mismatch in required config for the addon and your custom config provider

Create your own config provider

Theres an abstract class to implement. get_config gets called when the application_config is needed. It takes the limeapplication as an argument and a context dict which holds the id of current 'automatedflowparticipant' if a partcipant is the context - it isnt always so you need to have a fallback config if its not set.

class ConfigProvider(ABC):

    @abstractmethod
    def get_config(self, limeapp: LimeApplication, context: dict):
        pass

Then you have to replace the default config provider with your own

import addon_lime_automation

addon_lime_automation.CONFIG_PROVIDER = CustomConfigProvider()

Example of a custom provider which uses the DefaulConfigProvider as default fallback and checks for the name of the flow in current context. It resolves the config to use from application_config by suffixing the name of the flow

application_name:
    config:
        addon_lime_automation:
            apiurl: https://app.bwz.se/customer_default/bedrock/api
            sweden:
                apiurl: https://app.bwz.se/customer_sweden/bedrock/api
            norway:
                apiurl: https://app.bwz.se/customer_norway/bedrock/api
    secrets:
        addon_lime_automation:
            apikey: apikey_customer_default
            sweden:
                apikey: apikey_customer_sweden
            norway:
                apikey: apikey_customer_norway
import lime_config
import addon_lime_automation
from lime_application import LimeApplication


class CustomConfigProvider(addon_lime_automation.DefaultConfigProvider):
    def __init__(self):
        super().__init__()

    def get_config(self, limeapp: LimeApplication, context: dict):

        id_participant = (context or {}).get('automatedflowparticipant', None)

        logger.info(f"Resolving custom config for particpant {id_participant}")

        cfg_default = super().get_config(limeapp, context)

        try:
            if id_participant is not None:

                participant = limeapp.limetypes.automatedflowparticipant.get(
                    id_participant)
                flow = limeapp.limetypes.automatedflow.get(
                    participant.properties.automatedflow.value)

                # some logic to find out which config to use depending on flow

                config_key = flow.properties.name.value
                logger.info(f"Resolving custom config {config_key}")

                cfg = lime_config.get_app_config(
                    limeapp,
                    f'config.addon_lime_automation.{config_key}')
                cfg_secret = lime_config.get_app_config(
                    limeapp,
                    f'secrets.addon_lime_automation.{config_key}')

                # set overriden endpoint
                if(cfg is not None and cfg_secret is not None):
                    cfg_default['bedrockendpoint']['url'] = cfg.get(
                        'apiurl', None)
                    cfg_default['bedrockendpoint']['apikey'] = cfg_secret.get(
                        'apikey', None)
        except Exception as e:
            logger.error(str(e))

        return cfg_default

# Replace the config provider with our custom provider
addon_lime_automation.CONFIG_PROVIDER = CustomConfigProvider()

Automate the participant creation process

In the addon-lime-automation there exists a number of helper funtions in the sdk module. They are implemented as part of the AutomatedFlowParticipantFactory class.

The functions are:

  • create_automatedflow_participant: Will create and commit a participant, returns the participant object.
  • init_automatedflow_participant: Will create a participant object and return it, amongs with all related objects.
  • get_automated_flow_by_decider: Will fetch and return the correct automated flow based on the lime-admin config.

More details on how the functions work are available in docstrings for each.

An example for how to create a participant depending on a leadstatus for a lead-flow:

import logging
from lime_type import LimeObject
import addon_lime_automation.sdk.decorators as automation_decorators
import addon_lime_automation.sdk as automation_sdk

logger = logging.getLogger(__name__)


@automation_decorators.automated_flow_decider()
class Lead(LimeObject):
    """Summarize the function of a Person object here"""

    def before_update(self, uow, **kwargs):
        """
        This is called on all new and updated objects. All changes
        made to the object here will be persisted.
        All other objects that are changed or created here must be
        added to the unit of work.
        """
        super().before_update(uow, **kwargs)

        create_af_participant(self, uow)


def register_limeobject_classes(register_class):
    register_class("lead", Lead)


def create_af_participant(lead: LimeObject, uow) -> LimeObject:
    leadstatus = lead.properties.leadstatus
    if not leadstatus.is_dirty() or leadstatus.value.key not in [
        "qualified",
        "wip",
    ]:
        return
    factory = automation_sdk.AutomatedFlowParticipantFactory(lead.application)
    flow = lead.properties.automatedflow.fetch()
    person = lead.properties.person.fetch()
    if not flow or not person:
        return

    # Don't forget to add duplicate check if needed.
    new_participant, *affected_objects = factory.init_automatedflow_participant(
        automatedflow=flow,
        related_object=person,
    )
    uow.add(new_participant)
    for affected_object in affected_objects:
        uow.add(affected_object)

    return new_participant