Skip to content

[Proposal]: Extension System

Lorenzo Natali edited this page Jul 20, 2020 · 6 revisions

Overview

The goal of this improvement is to allow MapStore runtime extensibility using uploadable extensions.

Proposed By

  • Lorenzo Natali
  • Mauro Bartolomeoli

Assigned to Release

The proposal is for 2020.02.00 (first version integrated with GeOrchestra).

State

  • Under Discussion
  • In Progress
  • Completed
  • Rejected
  • Deferred

Motivation

The improvement is proposed to support the integration with GeOrchestra and to make MapStore more extensible without the need to created custom projects.

Proposal

We have already in development the context editor that allow to create some mapviewer contexts with a subset of the existing plugins. With this extension we want to allow:

  • Administrators to upload extensions (an extension is a plugin that can be imported and used at runtime, while actual plugins need to be included in the MapStore build)
  • Activation and usage of the uploaded extensions from the context editor, so that they can be added to a map context

An uploadable Extension package will include the compiled plugin code, some metadata, localization files and any other asset needed by the plugin to work.

In order to provide this kind of extension system to MapStore we have to provide at least the following functionalities:

  • Support to load javascript bundles not compiled with MapStore
  • Support on the frontend and backend to handle extensions install and uninstall
  • Support for enabling infrastructure functionalities needed by an extension (e.g. translations, reducers, epics, etc.)
  • Support in context editor to browse static plugins together uploaded extensions

Support for loading javascript bundles not compiled in MapStore

There is a draft PR about this here

The work done allows to:

  • create a self contained plugin, with all the needed code components (e.g. the ReactJS components and Redux actions / reducers, the additional epics), the runtime configuration (e.g. supported containers)
  • build the plugin using a script to create a javascript bundle that can be loaded / included at runtime

Still to be done:

  • test importing libraries not included in package.json
  • implement support for additional assets (e.g. translation files)

Have a look at the lazyplugins example to understand how to use this new functionalities.

Backend support

For this support we need:

  • Support for extension upload and installation

Extension installation support

Format of the extension package

The proposed extension package is a zip file containing the following data:

  • index.json: a json containing the metadata of the plugin
  • translations a folder with the additional i18n json files, following the same naming convention in mapstore. data.<en_EN>.json
  • index.js: The javascript bundle to load
my-extension.zip
|── index.js
├── index.json
└── translations
    └── data.en_EN.json
index.json

The `index.json file should contain all the information about the extension:

  • An id that identifies the extension
  • A version to show in UI. Semantic versioning is suggested.
  • title and description to display in UI, mnemonic hints for the administrator
  • plugins the list of plugins that it adds to the application, with all the data useful for the context manager. Format of the JSON object for plugins is suggested here
{
    "id": "a_unique_extension_identifier",
    "version": "1.0.0", 
    "title": "the title of the description", 
    "description": "a description of the extension", 
    "plugins": [{ 
         "name": "MYPlugin",
         "title": "extensions.a_unique_extension_identifier.title",
         "description": "",
         "defaultConfig": {},
         "...": "..."
    }] 
}
index.json plugins JSON Format

Most of the information for the plugins in the extensions are needed to context-editor, that allows to add the plugins to the context. Therefore we suggest to keep the same format, with same properties.

Here I report the same information available here for plugins descriptor required to context-editor :


The configuration has this shape:
{
 "plugins": [
    { 
        "name": "Map",
        "mandatory": true, // <-- mandatory should not be shown in editor OR not movable and direcly added to the right list. 
    }, { 
        "name": "Notifications",
        "mandatory": true, // <-- mandatory should not be shown in editor OR not movable and direcly added to the right list. 
              "hidden": true, // some plugins are only support, so maybe showing them in the UI is superfluous. 
    }, {
        "name": "TOC",
       "symbol": "layers",  
        "title": "plugins.TOC.title",
        "description": "plugins.TOC.description",
        "defaultConfig": {},
        "children": ["TOCItemSettings", "FeatureEditor"]
    }, {
        "name": "FeatureEditor",
        "defaultConfig":  {}
    }, {
        "name": "TOCItemSettings",
        "...": "..."
    }, {
        "name": "MyPlugin" // <-- this is typically an extension
    }, { 
       "name": "Footer", 
       "children": ["MousePosition", "CRSSelector",  "ScaleBox"]
    }, {
       "name": "Widgets", 
      "children": ["WidgetsBuilderPlugin", "WidgetsTrayPlugin"],
      "dependencies": ["WidgetsBuilderPlugin"], // some plugins may be mandatory only if parent is added.
    }, {
      "name": "WidgetsTrayPlugin"
    },  {
      "name": "WidgetsBuilderPlugin",
      "hidden": true // <-- This is a child. In this case it will be added automatically, 
                     // without showing if the parent is added
}]
}

Properties of plugin entry: Base Properties:

  • name: {string} the name (ID) of the plugin
  • title: {string} the title string OR messageId (from localization file)
  • description: {string}: the description string OR messageId (from localization file)
  • symbol: {string}`: icon (or image) symbol for the plugin
  • defaultConfig {object}: optional object containing the default configuration to pass to the context-creator.
  • mandatory {boolean}: if true, the plugin must be added to the map, so not possible to remove (but can be customized)
  • hidden {boolean}: if true, the plugin should not be shown in UI. If mandatory, is added without showing.
  • children {string[]}: list of the plugins names (ID) that should be shown as children in the UI
  • dependencies: The difference between mandatory and dependencies is the "if the parent is present" condition.). Plugins that can not be disabled (or if are hidden, added by default) and are added ONLY if the parent plugin is added. (e.g. containers like toolbar, omnibar, footer or DrawerMenu, and other dependencies like Widgets that must contain WidgetsBuilder and so on)

This JSON should have also options to match with these changes to work with extensions.


Installation workflow

TBD: directory to save data (externalized). workflow and extension status (installed, activated...)

Admin UI

TBD UI and interactions to upload extensions, back-end services to support install, activate, deactivate, (uninstall)

Support to load extensions

Translation Files

TBD

New Plugins

The proposal consists to modify the load system of MapStore adding a setup.json loading system, containing the definitions of existing plugins, including the extensions. These information for standard plugins will be stored in a static file called setup.json. If not present MapStore will work as usual.

In case of no extensions, the JSON file will be loaded directly. In case of extension present, the back-end will fake the setup.json response reading the file and appending the extensions metadata to the list of plugins.

Extension system

This way the extension system is completely transparent to the final user. plugins in setup.json. From it MapStore can get the definition of the plugins defined in extensions.

They can be loaded on demand (lazy) or not.

TBD please @mbarto define the load mechanism

Support in context editor to browse existing plugins together with plugins from extensions

The setup.json should provide all the information to configure the editor. As said, standard plugins that can be loaded from context-editor will be defined in the static version of the file.

Plugins that are not defined in the static setup.json will not be available in the list. Plugins for other pages (like home page...) can not be listed or marked as hidden for the moment.

Final Notes

  • In case of missing extension system or back-end MapStore should work as usual
  • In case of missing plugins definitions, MapStore will simply ignore them on context load (as it usually does now).