Version: 2.10

Plugins

Introduction

Saleor arrives with some plugins already implemented by default. These plugins are located in saleor.plugins. To provide the PluginsManager with a list of enabled plugins, include the Python plugin path in the settings.PLUGINS list.

Plugins manager

The PluginsManager is located in the saleor.plugins.manager. It is a class responsible for handling all declared plugins and serving a response from them. In case of a non-declared plugin, it serves a default response.

It is possible to overwrite an PluginsManager class by implementing it on its own. Saleor will discover the manager class by taking the declared path from settings.PLUGINS_MANAGER.

Base plugin class

The BasePlugin class is located in the saleor.plugins.base_plugin. It serves as an abstract class for storing all callback methods available to plugins.

Multiple plugins are executed as a pipeline: each callback receives a previous_value parameter, the first plugin receives the default value, each consecutive plugin receives the output of the previous one.

Configuration

Saleor allows you to change the configuration of any given plugin over API.

CONFIG_STRUCTURE describes the shape of the configuration: field names, their types, and labels to use in the user interface.

DEFAULT_CONFIGURATION provides initial values for those fields.

The plugin configuration can be further validated by overwriting the validate_plugin_configuration method like in the following example:

# custom_tax/plugin.py
from django.core.exceptions import ValidationError
from saleor.plugins.base_plugin import BasePlugin, ConfigurationTypeField
class TaxApiPlugin(BasePlugin):
PLUGIN_ID = "plugin.taxapi" # plugin identifier
PLUGIN_NAME = "Tax API" # display name of plugin
PLUGIN_DESCRIPTION = "Description of the plugin."
CONFIG_STRUCTURE = {
"login": {
"type": ConfigurationTypeField.STRING,
"help_text": "Provide your login name",
"label": "Username or account",
},
"password": {
"type": ConfigurationTypeField.STRING,
"help_text": "Provide your password or API token",
"label": "Password",
}
}
DEFAULT_CONFIGURATION = [
{"name": "login", "value": None},
{"name": "password", "value": None},
]
DEFAULT_ACTIVE = False
@classmethod
def validate_plugin_configuration(cls, plugin_configuration: "PluginConfiguration"):
"""Validate if provided configuration is correct."""
missing_fields = []
configuration = plugin_configuration.configuration
configuration = {item["name"]: item["value"] for item in configuration}
if not configuration["login"]:
missing_fields.append("username or account")
if not configuration["password"]:
missing_fields.append("password or API token")
if plugin_configuration.active and missing_fields:
error_msg = (
"To enable a plugin, you need to provide values for the "
"following fields: "
)
raise ValidationError(error_msg + ", ".join(missing_fields))

Saleor will use this data to create default configuration in DB which will be served by the API.

Writing your own plugins

Make sure that each plugin inherits from the BasePlugin class and that it overwrites base methods. You can write your plugin as a class which has callable instances, like the one below:

# custom/plugin.py
from django.conf import settings
from urllib.parse import urljoin
from ..base_plugin import BasePlugin
from .tasks import api_post_request_task
class CustomPlugin(BasePlugin):
def postprocess_order_creation(self, order: "Order", previous_value: Any):
data = ...
transaction_url = urljoin(settings.CUSTOM_API_URL, "transactions/createoradjust")
api_post_request_task.delay(transaction_url, data)
note

There is no need to implement all base methods as the PluginsManager will use default values for methods that are not implemented.

Loading your plugin

To activate the new plugin, add it to the settings.PLUGINS list in your Django settings:

# settings.py
PLUGINS = ["saleor.plugins.custom.plugin.CustomPlugin", ]

Background tasks

Some plugin operations should be done asynchronously. Saleor uses Celery and will discover all Celery tasks declared in tasks.py in the plugin directories.

# custom_plugin/plugin.py
class MyPlugin(BasePlugin):
def postprocess_order_creation(self, order: "Order", previous_value: Any):
data = {}
transaction_url = urljoin(get_api_url(), "transactions/createoradjust")
api_post_request_task.delay(transaction_url, data)
# custom_plugin/tasks.py
import json
from celery import shared_task
from typing import Any, Dict
import requests
from requests.auth import HTTPBasicAuth
from django.conf import settings
@shared_task
def api_post_request(
url: str,
data: Dict[str, Any],
):
try:
username = "username"
password = "password"
auth = HTTPBasicAuth(username, password)
requests.post(url, auth=auth, data=json.dumps(data), timeout=settings.TIMEOUT)
except requests.exceptions.RequestException:
return