SkyPortal Extensions

It is common for specific instances of SkyPortal to employ “extensions”, which use added components and APIs not available in the baseline app, usually because of particular forms of data access and distribution. Some examples include Fritz or SkyPortal-GRANDMA.

For the APIs, they will have a folder: extensions/skyportal/skyportal/handlers/api For redux, they will have a folder: extensions/skyportal/static/js/ducks For the components, they will have a folder: extensions/skyportal/static/js/components

When deploying, files are copied into each of these SkyPortal folders from your repository.

For APIs in particular, a dedicated needs to be created. To deploy, it will take a form similar to this, where in addition to the baseline application loaded with

from skyportal.app_server import make_app

Custom handlers are added as follows (taking Fritz as an example):

from skyportal.handlers.api.alert import (
from skyportal.handlers.api.archive import (
from skyportal.handlers.api.kowalski_filter import KowalskiFilterHandler
from skyportal.handlers.api.tns_info import TNSInfoHandler

fritz_handlers = [
    # Fritz-specific API endpoints
    # Alerts
    (r"/api/alerts(/.+)?", AlertHandler),
    (r"/api/alerts_aux(/.+)?", AlertAuxHandler),
    (r"/api/alerts_cutouts(/.+)?", AlertCutoutHandler),
    (r"/api/alerts_triplets(/.+)?", AlertTripletsHandler),
    # Archive
    (r"/api/archive", ArchiveHandler),
    (r"/api/archive/catalogs", ArchiveCatalogsHandler),
    (r"/api/archive/cross_match", CrossMatchHandler),
    # Alert Stream filter versioning via K:
    (r"/api/filters/([0-9]+)?/v", KowalskiFilterHandler),
    (r"/api/tns_info/(.*)", TNSInfoHandler),

and then those handlers are added to the baseline app:

    app = make_app(cfg, baselayer_handlers, baselayer_settings, process, env)
    app.add_handlers(r".*", fritz_handlers)  # match any host

This is known as the “app factory”, which is a function used to create the Tornado application. This is often needed to add additional routes, or do certain setup procedures before the application is run.

To add accessible routes to the web interface, one configures the config.yaml as follows.

      - path: "/alerts"
        component: Alerts
      - path: "/alerts/ztf/:id"
        component: ZTFAlert
      - path: "/archive"
        component: Archive

where each path in the browser is tied to a specific React.js component as rendered.

For certain Javascript components we also have “Plugins”, through which functionality can be added to extend those pages. These are currently available on the Source (SourcePlugins.jsx), Candidates (CandidatePlugins.jsx), Filters (FilterPlugins.jsx), and About (AboutPlugins.jsx) pages.

Settings for extensions can be specified in the config.yaml. Taking fritz as an example again, here is a segment that configures kowalski:

    protocol: https
    port: 443
    token: YOUR_TOKEN_HERE

Your extension can read the configuration as follows:

  from import load_env
  env, cfg = load_env()

and then access specific entries with calls like:

  protocol = cfg["app.kowalski.protocol"]