EWD.js & VistA: A technical deep-dive into the VistA Demo Application

In the previous article I explained how to install EWD.js and VistA using Christopher Edwards’ OSEHRA installer, and showed you how to start up the VistA Demonstration application in a browser.

In this article I’ll take a deep dive into this application and show you how EWD.js is used to integrate with VistA.

EWD.js Configuration

First, let’s take a look at how EWD.js has been installed and configured in your Ubuntu Virtual Machine.

The back-end Node.js modules can be found in:

/home/osehra/node/node_modules/

EWD.js is running as a service, started by the file:

/home/osehra/node/ewdStart.js

If you look at this file, you’ll see that EWD.js is listening on port 8080 and SSL has been enabled.

You should also see that the Web Server Root Path is defined as

/home/osehra/www

and you’ll therefore find all the pre-defined EWD.js applications under the path:

/home/osehra/www/ewd/

So the browser-side source code for the VistA Demonstration application can be found in the directory:

/home/osehra/www/ewd/VistADemo

and you’ll find its back-end logic in the application module file:

/home/osehra/node/node_modules/VistADemo.js

Stopping and Starting the EWD.js Service

The EWD.js service is automatically started by the OSEHRA installer, but if you want to stop it for any reason:

sudo service osehravista-ewdjs stop

To restart EWD.js:

sudo service osehravista-ewdjs start

VistADemo: A Responsive Bootstrap 3 Application

If you look the main container page for the VistADemo application, ie:

/home/osehra/www/ewd/VistADemo/index.html

you’ll find that it uses the Boostrap 3 JavaScript framework.  It has been designed to be what’s known as a responsive application, meaning that its layout changes according to the width of device on which it’s running.  It will change and optimise its appearance automatically, in such a way that it will work and behave nicely on browsers in desktop devices (including ChromeBooks and ChromeBoxes), tablets and phones.  Try it and see!

You’ll also discover that the VistADemo application also uses a new feature of EWD.js: its User Interface (UI) is broken down into fragments: ie small files that contain markup that is injected into the container page whenever needed.  This approach makes the definition and maintenance of a large complex UI much easier than if it was all defined in a single file.  For example, take a look at the files:

/home/osehra/www/ewd/VistADemo/about.html
/home/osehra/www/ewd/VistADemo/login.html

VistADemo’s Browser-side Logic

The browser-side behaviour of the VistADemo application is defined in a single JavaScript file:

/home/osehra/www/ewd/VistADemo/app.js

If you consult the EWD.js Reference Guide, you’ll soon understand what it’s doing and why it does it, especially if you work through the tutorial in Appendix 3.

VistADemo’s Back-end Logic

The aspect of the VistADemo application’s logic that we’re really interested in is its integration with VistA.  This takes place in its back-end Node.js module file:

/home/osehra/node/node_modules/VistADemo.js

We’ll now look at how it implements the three key steps that are demonstrated in the application:

  • logging into VistA using an Access Code and Verify Code
  • searching for a patient by name or name prefix
  • retrieving a patient’s information

Along the way, we’ll also discover how state is maintained and security is managed by EWD.js.

Logging Into VistA

You’ll find the login form specified here:

/home/osehra/www/ewd/VistADemo/login.html

Notice the Login Button:

  <a href="#" id="loginBtn" style="display: none" class="btn btn-primary">Login</a>

The browser-side handler for this button is actually automatically built-in to EWD.js – provided by a special Bootstrap support library file:

/home/osehra/www/ewdLite/ewdBootstrap3.js

You shouldn’t edit or modify this file.

Now look in the browser-side JavaScript file:

/home/osehra/www/ewd/VistADemo/app.js

At line 7, you’ll see the line:

  login: true,

This is what tells EWD.js to use the built-in form handler which, in turn, expects the login button as we’ve specified it.

When that button is clicked, the form contents (ie the Access Code and Verify Code as entered by the user) are sent as a WebSocket message to the back-end where they are to be verified and used to log in to VistA. So let’s see how that is done.

Look inside the file:

/home/osehra/node/node_modules/VistADemo.js

Go to line 254 where you’ll find the handler for the login form. Let’s step through the logic. First it checks whether or not the user entered any values for the Access and Verify Codes:

    'EWD.form.login': function(params, ewd) {
      if (params.username === '') return 'You must enter an Access Code';
      if (params.password === '') return 'You must enter a Verify Code';

Note that username and password are the parameters holding the Access Code and Verify Code respectively. Check back to the login.html fragment file and you’ll see why – notice the id values:

      <div class="form-group">
        <label for="username">Access Code: </label>
        <input type="text" class="form-control focusedInput" id="username" placeholder="Enter your Access Code" />
      </div>
      <div class="form-group">
        <label for="password">Verify Code:  </label>
        <input type="password" class="form-control" id="password" placeholder="Enter your Verify Code" />
      </div>

Notice that if either field is empty, all the back-end logic did was to return an error message. Try running the application and see what happens when you submit the form with one or other of the values empty: what you should see is a widget known as a toastr pop down in the top right corner of the browser containing the appropriate error message. All that behaviour happens automatically, courtesy of EWD.js, simply by virtue of the programmer returning an error string!

If both the Access and Verify Codes are non-empty strings, the back-end logic proceeds to this line:

      var results = VistALogin(params.username, params.password, ewd);

The reason why this piece of the logic has been separated out into a separate function will be explained in my next blog article, but for now we need to jump back up to line 36 in the VistADemo.js file where you’ll find the VistALogin function:

var VistALogin = function(accessCode, verifyCode, ewd) {
  var authP = new ewd.mumps.GlobalNode('%zewdTemp', [process.pid]);
  authP._delete();
  authP._setDocument({
    inputs:{
      password: verifyCode,
      username: accessCode
    }
  });
  var result = ewd.mumps.function('login^ZZCPCR00', '');
  if (result === '') {
    var document = authP._getDocument();
    return {
      error: false,
      outputs: document.outputs
    };
  }
  else{
    return {error: result};
  }
};

The key line to notice in this function is this one:

  var result = ewd.mumps.function('login^ZZCPCR00', '');

What this is doing is executing a Mumps function: login^ZZCPCR00.

You’ll find the source for this Mumps routine file here:

  /home/osehra/p/ZZCPCR00.m

I have Chris Casey to thank for this file: it’s the one he used for the NHS VistA demonstration application and he has made it available in Open Source.

Those of you who are familiar with VistA’s Mumps logic will recognise most of the logic in his login() function (go to line 62 of the routine file).   The interesting bit in the context of this article is the way he’s implemented the function’s integration with the EWD.js.  At line 68, he picks up the inputs from a temporary Mumps Global:

 m inputs=^%zewdTemp($j,"inputs")

To see where this was populated, go back to the VistALogin() JavaScript function where, at its start, you’ll see this:

  var authP = new ewd.mumps.GlobalNode('%zewdTemp', [process.pid]);
  authP._delete();
  authP._setDocument({
    inputs:{
      password: verifyCode,
      username: accessCode
    }
  });

This is EWD.js JavaScript/Node.js logic for pushing the Access and Verify codes into that temporary Mumps Global: note the use of the _setDocument() function.

Now go back to Chris Casey’s Mumps login() function where, at the bottom, after a successful login, he merges a number of values into that same temporary Global:

 s results("DT")=DT
 s results("DUZ")=personDuz
 s results("username")=personName
 s results("displayName")=displayPersonName
 s results("greeting")=$g(user(7))
 m ^%zewdTemp($j,"outputs")=results

When his function quits, it returns control back to the VistALogin() JavaScript function where the _getDocument() method is used to extract those values from the temporary Global and create a JavaScript object (see line 46):

  if (result === '') {
    var document = authP._getDocument();

This object is then returned from the VistALogin() function when it completes. Go back down to line 268 of VistADemo.js where you’ll see the following lines:

        ewd.session.$('username')._value = params.username;
        ewd.session.$('userDUZ')._value = results.outputs.DUZ;
        ewd.session.$('displayName')._value = results.outputs.displayName;

What’s happening here is those values that were extracted from Chris’s temporary global are now being saved into the user’s EWD Session: data storage that is specific to this user and that persists for the duration of the user’s activity (If you look at the top of the browser-side app.js file, you’ll see that this timeout is set to an hour of inactivity).  In particular you’ll see that the user’s DUZ value is being persisted in the EWD Session.  That,of course, is the key to VistA data and API access.

Note: access to a specific user’s EWD Session is determined automatically and securely by EWD.js via a randomised token string that is attached to every WebSocket message that is sent from the user’s browser.  The programmer just needs to access it via the object ewd.session that EWD.js makes available automatically.

Now that the user has successfully logged in with a valid Access and Verify Code, all that remains is to send a signal back to the user’s browser via a WebSocket message:

        ewd.session.setAuthenticated();
        ewd.sendWebSocketMsg({
          type: 'loggedIn',
          message: {
            ok: true,
            name: ewd.session.$('displayName')._value
          }
        });
        return '';

Notice that line:

        ewd.session.setAuthenticated();

This sets a flag against the user’s session denoting that the user has now been successfully authenticated. We’ll see how that is checked in the subsequent actions.

EWD.js will now remove the pop-up form from the browser and allows the user to start using the application’s main UI.  This happens as a result of the browser-side handler for the loggedIn message, defined in /home/osehra/www/ewd/VistADemo/app.js at line 103:

 
    loggedIn: function(messageObj) {
      toastr.options.target = 'body';
      $('#main_Container').show();
      $('#mainPageTitle').text('Welcome to VistA, ' + messageObj.message.name);
      $('#patientSelectionPanel').modal('show');
      EWD.bootstrap3.enableSelect2();
    },

That’s it: the user has logged into VistA via their Access/Verify Codes. The DUZ value for this user is now sitting securely in his/her EWD session.

Now let’s see how the application proceeds.

Selecting a Patient By Name Prefix

Whenever the user enters a character into the Patient Selection combo box, a message is sent to the back-end containing the prefix entered so far.  Because this is something you’ll want to frequently do, just like the login mechanism, the handler and logic for handling this is built into EWD.js.  All you have to do is provide the back-end logic for handling the prefix by specifying a handler function for a special reserved message type of patientQuery.

Look at line 289 in /home/osehra/node/node_modules/VistADemo.js:

 
    patientQuery: function(params, ewd) {
      if (ewd.session.isAuthenticated) {

        // ********************************
        var results = getPatientsByName(params.prefix, 40, ewd);
        // ********************************

        ewd.session.$('names')._delete();
        ewd.session.$('names')._setDocument(results.namesById);
        ewd.sendWebSocketMsg({
          type: 'patientMatches',
          message: results.results
        });
      }
    },

Notice the first line:

 
      if (ewd.session.isAuthenticated) {

This checks that the user has properly logged in. It’s really important to apply this check: it prevents any possibility of a malicious user attempting to invoke this handler function by figuring out how to send a WebSocket message via the browser’s JavaScript console without having properly logged in.

Next, it invokes the separate function getPatientsByName() which you’ll find at line 58. The key part of this function is:

 
var getPatientsByName = function(prefix, max, ewd) {
  var patientIndex = new ewd.mumps.GlobalNode("DPT", ["B"]);
  var results = [];
  var namesById = {};
  var i = 0;
  patientIndex._forPrefix(prefix.toUpperCase(), function(name, node) {

What’s happening here is that it’s essentially iterating through the ^DPT(“B”) Mumps Global, returning all names that start with the prefix specified in the combo box. Unlike the login() function, we’re not using a Mumps function: we’re able to perform all the necessary logic in Node.js using JavaScript.  Strictly-speaking, we should probably check the value of DUZ stored in the user’s EWD Session to confirm that they are actually allowed to perform this action and perhaps the range of patients that they can see and access.  To get the user’s DUZ, the programmer would simply do this:

 
  var DUZ = ewd.session.$('userDUZ')._value;

On completion, control is then returned back to the patientQuery message handler function (see line 296) which finishes by doing the following:

 
        ewd.session.$('names')._delete();
        ewd.session.$('names')._setDocument(results.namesById);
        ewd.sendWebSocketMsg({
          type: 'patientMatches',
          message: results.results
        });

What it’s doing is caching an index of names by patient Id into the EWD session for later use and then returning an array of matching names in a WebSocket message that uses the special reserved type of patientMatches.

EWD.js recognises this message type and automatically handles it within the browser without any further action by the developer: the result is that the matching names automatically appear in the dropdown panel under the patient selection ComboBox.

Retrieving Patient Details

The final step in this demonstration example occurs when the user selects a patient from the Selection ComboBox dropdown panel. Once again, what happens when you select a patient from this panel happens automatically: a WebSocket message with a reserved type of patientSelected is sent to the back-end. This message carries with it the Id of the selected patient.

So take a look at line 305 in /home/osehra/node/node_modules/VistADemo.js for the handler for this message:

 
    patientSelected: function(params, ewd) {
      if (ewd.session.isAuthenticated) {
        // record at back end for future validation of actions
        ewd.session.$('patientIdSelected')._value = params.patientId;

        // ***********************************
        var results = getPatientSummaryDetails(params.patientId, ewd)
        // ***********************************

        return results;
      }
    }

Once again we do an isAuthenticated check to prevent unathorised access, and then control is passed to a separate function named getPatientSummaryDetails(). You’ll find this at line 81 of VistADemo.js:

 
var getPatientSummaryDetails = function(patientId, ewd) {
  var patient= new ewd.mumps.GlobalNode("DPT", [patientId,'0']);
  var patientRec0 = patient._value;
  var patientObj = patientRec0.split('^');
  return {
    EIN: patientId,
    name: patientObj[0],
    sex: patientObj[1],
    DOB: nodeVista.convertFtoStringDate(patientObj[2]),
    SSN: patientObj[8]
  };
};

Hopefully most of this is now self-explanatory: it’s extracting the patient data from the Mumps Global ^DPT(patientId, 0) and packaging it up into a JavaScript object that is returned to the browser as a WebSocket message that also has the type patientSelected.

Note the use of a handy date format conversion utility function that once again comes courtesy of Chris Casey:

nodeVista.convertFtoStringDate()

You’ll find this in the file /home/osehra/node/node_modules/nodeVista.js

On receipt of the response message, the browser-side handler for the patientSelected message is fired. See line 112 of /home/osehra/www/ewd/VistADemo/app.js:

    patientSelected: function(messageObj) {
      $('#patientSelectionPanel').modal('hide');
      EWD.application.patient = messageObj.message;
      if ($('#patientPanel').length > 0) $('#patientPanel').remove();
      EWD.getFragment('patientPanel.html', 'mainPageContent');
    }

What this does is pretty interesting. First it hides the patient selection ComboBox panel and then retrieves a fragment file named patientPanel.html. This is injected into the mainPageContent div tag. You’ll have probably realised by that these fragment files are just static content. In order to dynamically populate them, EWD.js fires a special event after they’ve been injected and rendered: onFragment. You can add an onFragment handler for each specific fragment that is injected. So if you look at line 74 of app.js you’ll see the handler for patientPanel.html:

    'patientPanel.html': function(messageObj) {
      EWD.application.activeMenu = 'patientMenu-vitals';
      var patient = EWD.application.patient;
      var sex = 'Male';
      if (patient.sex === 'F') sex = 'Female'
      var title = patient.name + ' ;  DOB   ' + patient.DOB + ' (' + sex + ')';
      $('#patientPanelTitle').text(title);
      $('.patientMenu').click(function(e) {
        e.stopPropagation();
        var id = e.currentTarget.id;
        $('#' + EWD.application.activeMenu).toggleClass("active", false);
        $('#' + id).toggleClass("active", true);
        EWD.application.activeMenu = id;
        var option = id.split('patientMenu-')[1];
        if (option === 'vitals') {
          $('#patientDataPanel').text('Patient Vitals Go Here');
        }
      });
    },

I’ll let the reader figure out what this does. In summary it’s populating the various div tags within the injected panel with the patient details values that were retrieved from VistA. It also creates a menu panel that is armed with click handlers to allow further data to be retrieved. At present that’s as far as the application will go until such times as more patient data is added to the VistA instance created by the OSEHRA installer.

Conclusion

That completes our deep dive into the VistA Demonstration application.  It has shown how EWD.js very simply and easily integrates with VistA, by a combination of:

  • integrating with Mumps functions using temporary Mumps Globals to transfer inputs and outputs in JSON format to and from Node.js
  • direct access to Mumps Globals from within Node.js

Those of you who are experts in VistA development using Mumps code can hopefully understand that what is now needed is a library of Mumps wrapper functions and JavaScript interfaces around VistA / FileMan APIs.  I’m hoping that as a result of releasing and documenting this example, the scene is now set for the VistA community to begin developing this library.

As you’ll see in the next article, by doing so the development community will also automatically provide what’s required for exposing VistA’s functionality as REST services.  Sounds too good to be true?  Move on to my next posting in this series!

Advertisements

2 comments

  1. Thanks for this post. I am following your 5 post series to start my Vista journey.

    I have been able to setup Vista and EWD.js over a virtualised ubuntu using vagrand. I wish to follow this post to learn the basics of demo application. I was able to see the demo application in my browser however I can not find the “/home/osehra/node/node_modules/” directory or any other directory/file mentioned in the post. Could you please help ?

  2. The best place to ask for help is the EWD Google Group – there’s already a lot of information that you can search there that may also help. Here’s the link:

    https://groups.google.com/forum/#!forum/enterprise-web-developer-community

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: