Plugins

Starting from version 1.5, Delta introduces the ability to extend its functionality using plugins. Plugins enable developers to add new functionality to Nexus Delta without the need to modify Delta itself. Plugins can introduce various new functionalities, including new API endpoints and indexing capabilities.

Note

Plugins are still an experimental feature and Delta SDKs and dependent modules(rdf, sourcing) provide no binary compatibility guarantees.

Plugin development

Plugins used by Delta need to be packaged as a .jar file containing the plugin code with all its dependencies. Delta loads plugins from .jar files located in a directory specified by DELTA_PLUGINS environment variable.

Plugins must define exactly one class which extends PluginDef trait.

The class must define following methods:

def info: PluginDescription

this method returns instance of PluginDescription which defines the plugin name and version.

def initialize(locator: Locator): Task[Plugin]

this method can be used to initialize the plugin and returns an instance of a Plugin, which can additionally define logic to stop the plugin gracefully.

def module: ModuleDef

this method must return ModuleDef from distage library. This is the only way in which plugins can use dependencies provided by core of Delta and other plugins, as well as provide dependencies which can be used by Delta and other plugins.

Example

TestPluginRoutes.scala
sourcepackage ch.epfl.bluebrain.nexus.delta.testplugin

import akka.http.scaladsl.server.Directives._
import akka.http.scaladsl.server.Route
import ch.epfl.bluebrain.nexus.delta.sdk.model.BaseUri

class TestPluginRoutes(baseUri: BaseUri) {
  def routes: Route =
    pathPrefix("test-plugin") {
      concat(
        get {
          complete(baseUri.toString)
        }
      )
    }
}
TestPluginDef.scala
sourcepackage ch.epfl.bluebrain.nexus.delta.testplugin

import cats.effect.IO
import ch.epfl.bluebrain.nexus.delta.sdk.PriorityRoute
import ch.epfl.bluebrain.nexus.delta.sdk.model.ComponentDescription.PluginDescription
import ch.epfl.bluebrain.nexus.delta.sdk.model.{BaseUri, Name}
import ch.epfl.bluebrain.nexus.delta.sdk.plugin.{Plugin, PluginDef}
import izumi.distage.model.Locator
import izumi.distage.model.definition.ModuleDef

case class TestPluginDef() extends PluginDef {

  override def module: ModuleDef =
    new ModuleDef {
      make[TestPlugin]
      make[TestPluginRoutes].from { (baseUri: BaseUri) =>
        new TestPluginRoutes(baseUri)
      }
      many[PriorityRoute].add((routes: TestPluginRoutes) =>
        PriorityRoute(1, routes.routes, requiresStrictEntity = true)
      )
    }

  override val info: PluginDescription = PluginDescription(Name.unsafe("testplugin"), "0.1.0")

  override def initialize(locator: Locator): IO[Plugin] = IO.pure(locator.get[TestPlugin])

}

Delta SDK

The Delta SDK can be included as following dependency:

  • SDK - general Delta SDK

All the above dependencies should be used in provided scope and must not be bundled in the plugin.

libraryDependencies += "ch.epfl.bluebrain.nexus" %% "delta-sdk" % deltaVersion % Provided

Dependency injection

Delta uses distage library for dependency injection. Each plugin must define ModuleDef to create instances of its own classes. All the dependencies provided by ModuleDefs defined in Delta modules, as well as other plugins can be used here.

The plugin can also define instances of following traits/classes, which will be used in Delta:

  • PriorityRoute - allows the plugin to define Akka HTTP Route with priority. The priority is used by Delta to prioritize route evaluation
  • ScopeInitialization - allows the plugin to define hooks which will be run on organization and project creation.
  • ScopedEntityDefinition - allows to define the required information to be able to handle a custom scoped entity
  • Serializer - allows to define how to serialize and deserialize an event / a state to database
  • ResourceShift - enables Delta to retrieve the different resources in a common format for tasks like indexing or resolving operations.
  • SseEncoder - enables Delta to convert a database event to a SSE event
  • EventMetricEncoder - enables Delta to convert a database event to an event metric
  • MetadataContextValue - registers metadata context of this plugin into global metadata context
  • RemoteContextResolution - enables Delta to resolve static contexts defined by the plugin
  • ServiceDependency - allows the plugin to define dependencies which will be displayed in /version endpoint.
  • ApiMappings - allows the plugin to define default API mappings used to shorten URLs
  • ResourceToSchemaMappings - allows the plugin to define mapping from the resource type to schema, which can be used to interact with resources created by the plugin through /resources endpoints.

Class loading

In order to avoid clashes between different plugins, Delta uses custom classloader to load plugin classes, which will load classes from the plugin first, then using application classloader and other plugins after that. It is therefore recommended to not include in the plugin jar any dependencies which are also provided by SDK. Libraries can be easily excluded from dependencies in sbt:

libraryDependencies       ++= Seq(
  "ch.epfl.bluebrain.nexus" %% "my-custom-library" % 1.0.0 excludeAll (
        ExclusionRule(organization = "ch.epfl.bluebrain.nexus", name = "shared-library_2.13")
      )

Configuration

Plugins should provide their default configuration in {plugin_name}.conf file, where plugin_name is the same as the one in PluginDescription. Plugins should include their config inside plugins.{plugin_name} namespace in the config. plugins.{plugin_name}.priority configuration setting defines priority of the plugin, which is used to determine order in which routes provided by plugins are evaluated.

Adding plugins to a Delta deployment

Delta loads plugins from .jar files present in a folder specified by DELTA_PLUGINS environment variable. In order to make delta discover the plugin, the .jar file of the plugin must be added(or symlinked) to that directory. In the official Delta Docker image the plugins are loaded from /opt/docker/plugins directory.

Enabling/disabling plugins

Additionally, plugins can be enabled/disabled using plugins.{plugin_name}.enabled property. Setting this property to false, will disable the given plugin.

Existing plugins

Currently, following Delta functionality is provided by plugins:

Elasticsearch plugin is required in order to provide listings in the API, other plugins can be excluded if their functionality is not needed. All the above plugins are included in the Delta Docker image.