Architecture
In this section we discuss the current state of the kbase-ui architecture. Note that this does not cover the development aspects of that architecture, which is presented in the development section.
- kbase-ui
- startup
- cut the mustard
- emergency escape pod
- boot sequence
- ui services
- routing and navigation
- plugin invocation
- plugin <-> ui communication
- ui services for plugins
- plugin configuration
- routes
- menu
- plugins
- boot sequence / lifecycle
- @kbase/ui-lib integration
- some hacks
- pass through clicks
- some hacks
Overview
When kbase-ui is first loaded in the browser, it initiates a startup sequence, which involves:
- ensuring the browser is compatible
- setting up failsafe error rendering
- preparing configuration
- loading ui services and plugins
- loading and rendering the root widget
Once this startup sequence is completed, the ui itself is in a responsive mode. It responds to events issued through its message bus, navigation events, and direct method calls of ui services.
The primary messages it responds to are:
- navigation
- setting the title to the header area
- logout detection
- adding and removing buttons from the header area
The navigation events currently correspond to the DOM hashchange
event, which indicates that a ui navigation has occured in the browser.
Navigation events are detected by listening for changes to the hash (aka fragment) in the browser’s current path. If the new hash value corresponds to a registered route, the associated widget is mounted (after unmounting the previous one). Otherwise, an error message is displayed.
The primary programmatic way for plugins to interact with the ui is through ui services. These are lightweight javascript modules which implement a component pattern (asynchronous start and stop methods), and can accept configuration from a plugin.
Startup
-
The root of kbase ui is a single file named index.html. This file is served to a web browser through a web server. In both development and production, this web server runs in a Docker container.
It is possible, of course, to load
index.html
from the file system, but it will not function correctly due to difference between how web browsers treat http-loaded files and file-system-loaded files. -
It first asynchronously starts the Google Tag Manager (gtag) in order to provide analytics.
-
It loads the animated KBase logo, which acts as a loading spinner.
-
It contains a
noscript
tag with appropriate content in case the browser has Javascript disabled or in the very rare case it does not support Javascript. -
The
mustard.js
script is responsible for ensuring that the browser is compatible with kbase-ui. -
The
fallback.js
script is loaded and executed. This script is responsible for displaying an error message in the case of catastrophic error in the startup process. -
The
build-info.js
script is run. This script simply makes basic build information available atwindow.global.__kbase__build__
-
If this is a production build,
moduleVfs.js
is loaded. This file contains most of the kbase-ui code, excluding large dependencies (over 100K). -
The
require-config.js
file is loaded. This file defines all of thhe modules available to kbase-ui, and is critical for providing a mapping from module name, or namespace, to file, or directory. -
The
require.js
file is loaded. This file provides the AMD system requirejs. requirejs reads the configuration established byrequire-config.js
, and launches the main entry point. -
requirejs loads the main entry point file
startup.js
. -
The
startup.js
file is the final layer before entering theh realm of AMD.- It sets up global requirejs error handling; this is where module loading errors are handled and propagated to the fallback error handler.
- It invokes the main entry point defined in
main.js
, and sets up a top level promise error handler, which is in turn propagated to the fallback error handler.
-
Picking up in
main.js
- Configures the promises library
bluebird
. - Merge the app config and deployment config into a single configuration
- create the main app object with configuration and the root dom node
- store the main app object in an anonymous (uuid) property on the window object
- load and configure the
knockout
component system - start the main app provided by
hub.js
.
- Configures the promises library
-
hub.js
is designed to be an implementation of the capabilities provided by the main app (just a moment on that), but in practice it has never been fully developed.- creates an instance of the app defined in
app.js
- providing the root node on which to render, the plugin configurations, the app configuration.
- starts the app.
- creates an instance of the app defined in
-
The
start()
method inapp.js
then proceeds:Note: If there are any major problems, such as a missing root node, or misconfiguration, it may throw an exception. (Remember from above, such exceptions propagate through the promise chain to
startup.js
.)- Loads ui services; ui services are available through the runtime without needing to load a module; they are generally provided in order to provide access to dynamic runtime features like configuration and authentication.
- Loads plugins
- Checks KBase core services; currently it just emits an error message to the console in case a service is either unavailable or the version is incompatible with the configured semver version expression.
- Starts ui services
- Mounts the root widget
-
At this point, kbase-ui’s startup process is complete.
Operation
After successful startup, we can consider kbase-ui to be in operational mode.
Almost all functionality in the ui is provided by the runtime object, ui services, widgets, and plugins.
Runtime Object
The primary job of the runtime object is to provide access to configuration, authentication, and messaging.
The runtime object is passed to the constructor for every ui service and widget. a characteristic which helps it glue the ui together as a system.
For example, the route
ui service listens for the hashchange DOM event. Upon detecting a change, it parses the browser location, attempts to lookup a route handler which matches the browser location, and then emits a navigation event through the runtime.
UI Services
The job of a ui service is to handle some domain of ui concern. They are essentially a structured way of namespacing ui functionality, and operate in a manner similar to widgets – i.e. they implement a component api pattern.
The component pattern requires a constructor, an asynchronous start method, and an async stop method. This allows for the services to be started and stopped in a regularized manner, and in parallel.
Another important feature of ui services is that they are configurable via the static ui configuration located at config/ui.yml
, the deployment configuration located at deployment/templates/config.json.templ
, as well as the install
key of any plugin’s config.yml
(where the key under install is the service id).
Since kbase-ui is presently under active development and refactoring, the array of services may change over the next few weeks and months (or disappear completely). However, here are a few key services as of now:
-
session: monitors kbase session state and issues loggedin, loggedout, and change events in response to changes in the kbase session; it also provides method which support querying session state as well as logging in and out via the auth2 service.
-
heartbeat: a trivial service which issues a runtime message every 0.1 second unless otherwise configured. This is useful for monitoring or polling services, since it does not require code wishing to implement such features from creating and managing their own timers.
-
menu: The menu service provides a database of available menu items, primarily provided by plugins. It is the primary mechanism by which a plugin can register its own widgets as navigation endpoints.
Note that a menu item will not actually appear on either the sidebar or hamburger menu unless configured in the ui’s
config/ui.yml
. -
connection: The connection service monitors the network connection to the host serving kbase-ui by periodically fetching a small file named
ping.txt
. If the connection fails, a notification is displayed; when (if) it recovers a recovery message is displayed. -
analytics: currently unused; provides integration with Google Analytics
-
coreService: unimplemented; will monitor core service availability and performance
-
data: a simple service to provide asynchronous fetch of json data sources located within the
/data
directory. -
feeds: monitors the current user’s feed and updates the sidebar nav badge with the number of unread feed items.
-
instrumentation: unimplemented; a shell service (methods which do nothing) to provide for widgets and plugins to send instrumentation data to an (unimplemented or designed) service.
notification: incomplete; meant to allow a plugin to register and send notifications to the notification widget.
route: provides an interface for plugins to register routes; and also publishes messaging endpoints for navigation to these routes.
rpc: little used wrapper around an rpc library provided by the ui; intended to be useful in that it wraps defaults, like the runtime object, making rpc calls more lightweight in usage.
schema: little used json schema integration, allowing a plugin to register a schema and validate data against it.
service: ??
type: a kbase data type registry, for mapping kbase workspace types to visualization widgets and icons.
userprofile: a simple service which maintains the current user’s profile upon request (runtime messages) or in response to login and logout events.
widget: a simple service to allow plugins to register kbase zero-dep widgets, with methods for fetching and creating widgets.
In operational mode, kbase-ui will take action in response to a set of events of different types.