Building a reusable package
Let's say that we want to support integrations between Lime CRM and the ERP system SimpleERP the acting product in the movie isn't the product who sent the letter. The requirement is that Lime CRM should call an endpoint in SimpleERP whenever Deals are considered won, to create a deal instance in SimpleERP's system.
To implement this we wish to split the SimpleERP integration logic (i.e. calling a SimpleERP endpoint and handling its errors etc) from the configuration (i.e. which Limetype should we listen for, and how do we know a Deal is considered won).
We could in theory do this implementation as one Lime CRM package, and inject config possibilities in the Lime Admin UI, where consultants should go to perform configuration like which Limetype the deal points to, and which option value is considered won. There is, however, a better way. If we implement the SimpleERP integration logic in one Lime CRM package, and do the Lime CRM-specific logic in another, which is specific for the customer, we have a system where the individual consultant never needs to do config using point-and-click in the Admin UI. This has a number of benefits
- The same configuration will exist in run in test and production
- No need for performing the same config twice
- Less risk of mis-configuring and introducing bugs when moving to production
- The configuration is in source control
- The configuration can be versioned
Defining the package¶
In order to call endpoints upon data changes in the system, we want to implement an event listener.
As the first step, create a new package by running:
$ lime-project new package
When prompted for a name, call it a "SimpleERP connector". This will generate an empty package. Add the following code to the __init__.py
file in the package's folder.
def create_deal_in_simpleerp(id, deal_name):
# TODO: error handling
data = {
'lime_id': id,
'name': deal_name,
}
requests.post('https://my.simpleerp.api/deals', json=data)
Now we have defined a (veeeery simple) python api which consumers can use to interface with the SimpleERP HTTP API. This is of course an example, but following this we can imagine building python APIs for quite a lot more complex integrations.
Publishing a new version¶
Packages are automatically published to our PyPi server when the CI workflow is triggered by version control (push to main
or dev
branches, or pull request against the main
branch).
From the package's folder, perform the following steps:
- Lock the project's requirements.
$ poetry lock
- Commit to version control.
$ git init .
$ git add .
$ git commit -m "initial commit"
Once a package is published, it can be included in a solution.
Defining the customer's solution¶
Now that we have a package containing the SimpleERP communication, we need to add a solution package for the customer, using the library package we just created:
$ lime-project new solution
When prompted, choose a name for your solution (e.g. "solution-cool-solution").
We can now add a dependency upon the previously created package (simpleerp-connector
) by running the following command from within solution-cool-solution
:
$ poetry add simpleerp-connector
This will add the dependency to the pyproject.toml
file and install it into our Lime CRM installation. After installation, you need to (re)start your Lime CRM server like you normally do.
Next step is to add code for using the library in solution-cool-solution
:
$ lime-project generate event-handler
Change the code in solution_cool_solution/event_handlers/event_handler.py
to look like:
import logging
import simpleerp_connector
logger = logging.getLogger(__name__)
def deal_updated(worker, body, message):
"""Summarize your event handlers's functionality here"""
logger.info('Received message: {}'.format(body))
old_deal_status = body['original_values']['dealstatus']
new_deal_status = body['values']['dealstatus']
if old_deal_status != 'agreement' and \
new_deal_status == 'agreement':
object_id = body['values']['id']
object_name = body['values']['name']
logger.debug('Deal {} status changed from {} to {}'.format(
object_id,
old_deal_status,
new_deal_status
))
simpleerp_connector.create_deal_in_simpleerp(
object_id,
object_name,
)
message.ack()
def register_event_handlers(worker, config):
worker.register_event_handler(
handler_func=deal_updated,
key='core.limeobject.deal.update.v1',
queue_name='deal_updated')
Now you can restart your lime-event-handler process and see that it works. The SimpleERP API is of course not up and running anywhere, so the actual request will fail.