Page tree
Skip to end of metadata
Go to start of metadata

This document describes the design changes to be made to OpenMRS App Framework for it to be able to support the OpenMRS reference application.
 
Requirements/Purpose

The purpose of the AppFramework module is to act as a) a source of all the apps in the system and b) a way to define news apps in the system through modules or via implementations. It thus provides simple classes/configuration through which other modules can specify apps that they want to define and an API through which these apps and their configuration can be queried. Apps can also define a set of extension points than can be used to enhance their functionality.

This module is expected to be used by User Interface modules to define the apps in the system and by the reference application to draw the apps and their extensions.
Terminology
App - An app in the context of OpenMRS is an independent functional entity that provides a certain amount of user functionality that is separately installable/upgradeable. In the context of the App Framework, an app is a representation that points to a URL which contains the entire app.

ExtensionPoint - Each app exposes certain points (denoted by simple string ids) that it can be enhanced at. Other modules/implementations can then write/specify these extensions. An extension point is what links extensions and an app together.

ContextModel - Each app provides a certain context in the form of a set of parameters which can be used by its extensions. It essentially provides the state information of the app so that it can then be used by the extension to do something useful. This state is passed to the rendering of the app as a map and the renderer can display the app accordingly.

Extension - An extension is a small piece of configuration that can be defined external to the apps so as to enhance their functionality. It can also be used to integrate different apps together by implementers via configuration in a manner that the users of the app might not have expected.
Example
On patient dashboard App, there are 2 extension points

    - show global patient actions

    - show actions for patient for active visit
On XRay Order App, there is 1 extension point

    - to quickly show patient information
On patient dashboard app, an extension is added

    - to order an X-Ray directly
On X-Ray order app, an extension is added

    - return back to current patient

Configuration

// App configuration for Patient Dashboard
// This is written by the developer of the Patient Dashboard app
[
   {
        id : "patientDashboardApp",
        description: "Patient Dashboard Application",
        extensionPoints: [
           { id: "activeVisitActions", description: "Actions for Visit" },
           { id: "globalPatientActions" }
        ],
        contextModel: [ “patientId”, “activeVisitId” ]
   }

]

// Extension configuration for Order XRAY Extension which links to Patient Dashboard app and enhances it
// This is configuration written by an implementer to enhance it.
[

  {
       “id” : “orderXrayEntension”,
       “appId” : “patientDashboardApp”,
       “extensionPoint”: “activeVisitActions”,
       “type” : “link”,
       “label”: “Order XRay”,
       “url”: “/xray/order?patientId=patientId&activeVisitId=activeVisitId”
  }

]

// App configuration for XRAY ordering app
[
   {
        id: "orderXrayApp",
        description: “XRay Application”,
        extensionPoints: [
           { id: “patientInformationLinks” }
        ],
        contextModel: [ “xrayConceptId”, “patientId” ]
   }

]

//Extension configuration for XRAY ordering app
[
  {
       “id” : “gotoPatientExtension”,
       “appId” : “orderXrayApp”,
       “extensionPoint”: “patientInformationLinks”,
       “type” : “link”,
       “label”: “Goto Patient”,
       “url”: “/patientDashboard?patientId=patientId”
  }
]
// Please note that multiple apps/extensions can be grouped together in one configuration file.

API

class AppDescriptor
{

   String id;
   String description;
   List<ExtensionPoint> extensionPoints;
   List<String> contextModel;

}

class ExtensionPoint
{
   String id;
   String description;
}

class Extension
{

   String id;
   String appId;
   String extensionPoint;
   String type;
   String label;
   String url;
}

interface AppFrameworkService
{
   List<Apps> getAllApps();
   List<Extensions> getExtensionsFor(String appId, String extensionId);

   // API to fetch all extensions for the apps and bundle that together in one big object
   // so that multiple calls don’t need to be made.
   List<Apps> getAppsIncludingExtensions();

}

There would be a user specific copy of these APIs as well, read more here.

Future enhancements
1. Rules
Extensions can specify rules which decide at runtime whether they are displayed or not. Some examples below.
<rule>context.isOpdAvailable() && patient.checkedIn == true</rule>

<rule>globalcontext.sessionLocation.name == 'Outpatient' && appcontext.activeVisitId != null</rule>

activeVisitId != null
BeanShell - http://www.beanshell.org/intro.html

  • No labels

2 Comments

  1. appcontext entries are entirely untyped? is it conventional that objects are passed by PK ID? why not UUID or the object itself? is overloading possible (i.e. a param can hold a concept ID, concept UUID, or concept source.name:concept term.code)?

    1. The key point is that we want to support both apps that are built with traditional server-side code, and also apps that are implemented as client-side HTML+JS+REST, and we want these apps to be able to more-or-less work together. Therefore the contextmodel values need to be simple values that can be represented as JSON.

      We haven't yet figured out how to type this or push people to use them in standard ways.