Custom Endpoints¶
When you install Lime CRM, a central service is the Lime CRM Webserver (sometimes referred to as the appserver). The Lime CRM Webserver handles among other things:
- Security, such as certificates and authentication via logon via a web form or via an API key.
- Serving the Lime CRM REST API
- Serving the static assets and the API used by the web client
The Lime CRM REST API can be used for a multitude of integrations, but sometimes you'll want an API that is better suited for your particular integration. Uses cases for this might include:
- Having one endpoint that updates or creates more that one object and where you want to have those operations in one transaction
- Creating an API that better matches the business events for an organization than an API where you set individual properties on objects.
- Better control over how data is serialized back to the client.
With custom endpoints you can extend the API exposed by the Lime CRM Webserver with URLs that are better suited for your particular problem domain.
The Application¶
Lime CRM is at its core a multi-tenant server, meaning that several applications can be served by the same application server. That means that an endpoint cannot make assumptions or hard code information about, for instance, what database to connect to.
To relieve you from having to keep track of information about the
current database, its current structure etc., you can use the
application member of
LimeResource <lime-webserver:lime_webserver.webserver.LimeResource>
class Todos(LimeResource):
def get(self):
todos = self.application.limetypes.todo.get_all()
Adding a custom endpoint¶
To add a custom endpoint to your package, change directory to your package directory and run the command for generating the boiler plate for a custom endpoint:
cd cool-stuff
lime-project generate endpoint
This generates the following in cool-stuff/cool_stuff/endpoints/endpoint.py
:
import lime_webserver.webserver as webserver
import logging
import webargs.fields as fields
from webargs.flaskparser import use_args
from ..endpoints import api
logger = logging.getLogger(__name__)
class LimeobjectCounter(webserver.LimeResource):
"""Summarize your resource's functionality here"""
# This describes the schema for the payload when posting a new deal
# See https://webargs.readthedocs.io/en/latest/ for more info.
args = {
"limetype": fields.String(required=True),
}
@use_args(args)
def get(self, args):
"""Get the current number of objects of the given type in the system.
"""
limetype = self.application.limetypes.get_limetype(args['limetype'])
limeobjects = limetype.get_all()
return {
'message': (
'Hello, {}! There are {} objects of type {} available'
.format(
self.application.coworker.properties.firstname.value,
limeobjects.count,
limetype.name))}
api.add_resource(LimeobjectCounter, '/count/')
The generated module defines a resource, LimeobjectCounter, that is accessible
at the URL https://hostname/myapplication/cool-stuff/count/. The
URL we want to listen to is defined by the last line
api.add_resource(LimeobjectCounter, '/count/')
.
This particular resource only listens to HTTP GET requests as it
implements a method called get
. If we want to add support for other
HTTP methods, we need to add their corresponding methods, such as
post
, put
etc.
The get method requires the caller to supply a limetype argument in the
form of query strings. The args
dict at the beginning specifies that
we require the argument limetype
, and that it should be a string.
Together with the @use_args
decorator on the get method, we ensure
that the caller get a proper error message back if arguments are missing
or malformed.
Once we're satisfied that our get method has a proper limetype argument we proceed to get all objects of that type from the database via the resource's application property.
Note
We won't actually retrieve all objects from the database. The returned list is a lazy collection that starts retrieving objects as we iterate over it.
Finally, we return a dictionary with a greeting to the logged on coworker containing information about how many objects of the specified type is available in the database. The return value will automatically be formatted as a JSON response to the caller.