Runtime Configuration¶
A runtime configuration can be edited via the Lime Admin page of the web client. Runtime configuration is described in greater detail here. Creating a runtime configuration is as easy as generating a config-module in your project. Use lime-project to scaffold it:
$ lime-project generate config-module
This command generates code in your project that makes it possible for lime-crm to automatically register and render your configuration module in the web client's Administrators Page. Browse to "Settings" in Lime Admin to see what it looks like!
The command yields the following code in limepkg-cool-package/limepkg_cool_package/config/__init__.py
:
import lime_admin.plugins
from .schema import create_schema
class RuntimeConfig(lime_admin.plugins.AdminPlugin):
"""A class to represent the plugin in Lime Administration.
An instance has access to a `lime_application.application.Application`
object via `self.application`.
"""
@property
def name(self):
"""The name of the plugin
Note:
The name is used as the key when persisting the config and
also as the key for the plugin itself.
"""
return 'limepkg_cool_package'
@property
def title(self):
"""The title of the plugin
"""
return 'Cool Package'
@property
def version(self):
"""The version of the config
Note:
Should be incremented if
the config format has changed. The version (if not None)
is appended to the name when persisting the config.
"""
return None
def get_config(self):
"""Function to retrieve a persisted config
Note:
If a config doesn't exist, then a default config should be
persisted and then returned.
If version has changed, then any existing config should be
upgraded, persisted and then returned.
There needs to be a really, really good reason for not returning
a config at all.
Returns:
`dict`
Raises:
`lime_admin.plugins.NotFoundError` if the config doesn't exist.
"""
try:
return super().get_config()
except lime_admin.plugins.NotFoundError:
return {}
def get_schema(self):
"""Function to retrieve a schema for the config.
Returns:
:class:`marshmallow.Schema`
"""
return create_schema(self.application)
def set_config(self, config):
"""Function to persist a config.
Note:
The config should be validated before it's persisted.
Args:
config (dict): The config to be persisted.
Raises:
`lime_admin.plugins.ValueError` if the config is invalid.
"""
super().set_config(config=config)
def register_config():
"""Function that is called by host when it's registering plugins.
Returns:
:class:`Plugin`
"""
return RuntimeConfig
Okay, that was a lot of code, but most of it is actually self explanatory. However, we will describe get_config
and get_schema
in separate sections below.
get_config¶
get_config
will use lime_data
to load config stored under the key limepkg_cool_package
or limepkg_cool_package.<VERSION>
. If no config exists, a NotFoundError
is raised. You can provide a default configuration in the corresponding except
block.
get_schema¶
In order to get a UI for your config, you need to provide a schema. You can add the logic for that in the generated schema.py
file. We use the
marshmallow framework, so please check their documentation for how to declare your own schema.
In addition to the generic marshmallow fields you can also use Lime specific fields:
lime-core:lime_type.fields.LimeTypeField
lime-core:lime_type.fields.LimePropertyField
lime-core:lime_filter.fields.StoredFilterField
lime-core:lime_authentication.fields.GroupField
lime-core:lime_authentication.fields.UserField
Here's an example for how to use those fields. You can go to <DOMAIN>/<APP-NAME>/webadmin/#/showcase-config
to see the resulting form.
from lime_type.fields import LimeTypeField, LimePropertyField
from lime_filter.fields import StoredFilterField
from lime_authentication.fields import GroupField, UserField
from marshmallow import Schema, fields
class LimetypeFieldsSchema(Schema):
class Meta:
ordered = True
limetype = LimeTypeField(application=self.application,
title='LimeTypeField',
description='Choose a limetype based '
'on your application')
lime_property = LimePropertyField(
application=self.application,
limetype_field='limetype',
title='LimePropertyField',
description='Choose properties from the limetype you '
'previously selected')
class LimeAuthenticationSchema(Schema):
class Meta:
ordered = True
groups = GroupField(application=self.application,
title='GroupField',
description='Choose from all groups of your '
'application')
users = UserField(application=self.application,
title='UserField',
description='Choose from all users of your '
'application')
class LimeFilterSchema(Schema):
filter = StoredFilterField(
application=self.application,
title='StoredFilterField',
description='Choose from all stored filters. You could also '
'depend the selection on a previously selected '
'limetype')
class ShowcaseSchema(Schema):
class Meta:
ordered = True
lime_type = fields.Nested(title='Limetype fields',
nested=LimetypeFieldsSchema)
lime_filter = fields.Nested(title='Filter fields',
nested=LimeFilterSchema)
lime_authentication = fields.Nested(
title='Authentication fields',
nested=LimeAuthenticationSchema)
Migrations¶
If you want to change the schema of an already released solution or package, you will have to provide a migration, so that any saved and outdated config can be adapted to the new structure.
Let's assume your schema has been released like this:
from marshmallow import Schema, fields
class CoolPackageSchema(Schema):
some_field = fields.Str(title='Cool field')
For some reason you want to update it to this:
from marshmallow import Schema, fields
class CoolPackageSubSchema(Schema):
some_field = fields.Str(title='Cool field')
class CoolPackageSchema(Schema):
group_field = fields.Nested(title='Cool category',
nested=CoolPackageSubSchema)
That means for any saved config you want to migrate {'cool_field': 'some value'} to {'group_field': {'cool_field': 'some value'}}. Therefore, add a module called migrations.py to your config package:
import lime_admin.plugins
class Migration1(lime_admin.plugins.ConfigMigration):
""" describe the schema changes
"""
@property
def version(self):
return 1 # the new version, after running the upgrade
def upgrade(self, config):
return {'group_field': config}
def downgrade(self, config):
return config.get('group_field', {})
Afterwards, add the following method to your
RuntimeConfig class and change the
version from None
to 1
:
@property
def version(self):
return 1
def get_migrations(self):
"""Get a list of migrations for the config.
Returns: :class:`list`(:class:`lime_admin.plugins.Migration`)
"""
return [Migration1()]
Now whenever get_config
will find an older version than 1, it will run the migration and return the upgraded structure.
The next time you need a migration, you should write another implementation of lime_admin.plugins.ConfigMigration
, add it to the array of migrations and increase the version to 2
.