Based on https://github.com/hyperledger/aries-cloudagent-python/issues/1121.
The plugins.yaml
structure could be the following:
plugins:
- plugin_name: Universal Resolver
local_directory: uni-plugin
# Optional configuration by key-value
config:
key1: value1
key2: value2
- plugin_name: DIDComm Resolver
local_directory: didcom-plugin
# Optional configuration by file
config: "config/config-file.yaml"
In this section is going to be described the entire flow from running aca-py with a external plugin like the following:
aca-py start --arg-file default.yml --plugin mock_resolver
This logic is placed in aries_cloudagent/config/argparse.py
Argument caption
parser.add_argument(
"--plugin",
dest="external_plugins",
type=str,
action="append",
required=False,
metavar="<module>",
env_var="ACAPY_PLUGIN",
help=(
"Load <module> as external plugin module. Multiple "
"instances of this parameter can be specified."
),
)
if args.external_plugins:
settings["external_plugins"] = args.external_plugins
This logic is placed in aries_cloudagent/config/default_context.py
Register plugins
# Register external plugins
for plugin_path in self.settings.get("external_plugins", []):
plugin_registry.register_plugin(plugin_path)
await plugin_registry.init_context(context)
The registration and the init is managed by the class PluginRegistry
, aries_cloudagent/core/plugin_registry.py
Registration logic: The module is loaded on a class private variable.
self._plugins[module_name] = mod
Init the plugins: The module previously loaded launches the plugin setup.
async def init_context(self, context: InjectionContext):
"""Call plugin setup methods on the current context."""
for plugin in self._plugins.values():
if hasattr(plugin, "setup"):
await plugin.setup(context)
else:
await self.load_protocols(context, plugin)
inside of config/argparse.py ACA-Py uses configargparse which supports yaml file parsing. we can update external_plugins flag to parse and load a config.yaml file into settings["external_plugins"]
this would expose all loaded configurations inside the context to be used inside each module.(example)
We could also update registering to have more setup from configs, possibly here
import os
import json
# Set environment variables
os.environ['config'] = json.dumps({'key': "value"})
# Get environment variables
config = json.loads(os.environ.get('config'))
InjectionContext
setup
methodFollowing the current flow described on Logic Sequence
This is a implementation example
Having the folowing file plugin.yaml
plugins:
- plugin_name: Universal Resolver
local_directory: http_uniresolver
config:
methods:
- "sov"
- "btcr"
It is posible to deploy ACA-Py with the plugin configuration.
aca-py start --arg-file default.yml --plugin-config plugin.yaml
Add the following in aries_cloudagent/config/argparse.py
Argument caption
Add below the current implementation
parser.add_argument(
"--plugin-config",
dest="plugin_config",
type=str,
action="append",
required=False,
metavar="<module>",
env_var="ACAPY_PLUGIN_CONFIG",
help=(
"Load <module> as external plugin module config. Multiple "
"instances of this parameter can be specified."
),
)
Add below the current implementation
if args.external_plugins:
with open(args.external_plugins, 'r') as stream:
plugins_conf = yaml.safe_load(stream)
if not settings["external_plugins"]:
settings["external_plugins"] = []
settings["plugins_config"] = {}
for plugin in plugins_conf.get("plugins"):
plug_dir = plugin.get("local_directory")
plug_conf = plugin.get("config")
settings["external_plugins"].append(plug_dir)
settings["plugins_config"][plug_dir] = plug_conf
This logic is placed in aries_cloudagent/config/default_context.py
Add an argument with the plugins configuration
await plugin_registry.init_context(context, self.settings.get("plugins_config"))
The init is managed by the class PluginRegistry
, aries_cloudagent/core/plugin_registry.py
Init the plugins: The module previously loaded launches the plugin setup.
Add the argument in the method and the implementation
async def init_context(self, context: InjectionContext, plugins_config: dict = None):
"""Call plugin setup methods on the current context."""
for key, plugin in self._plugins.items():
if hasattr(plugin, "setup"):
plugin_conf = plugins_config.get(key,{})
await plugin.setup(context, **plugin_conf)
else:
await self.load_protocols(context, plugin)
This configuration could be extended adding more plugin entry points, for instance could be possible replace the local_directory to:
git-repo: "https://github.com/sicpa-dlab/acapy-resolver-universal"
or
pypi-package: plugin-test