You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 14 Next »

Node.js is a good technology option on which to develop the interoperability layer core component for the following reasons:

  • It is very lightweight
  • It has a robust HTTP library
  • It has robust support from 3rd party libraries for reading and modifying HTTP requests
  • It is highly performant

Libraries to use

  • Express - Express is a web application framework for node.js. It provides easy mechanisms to expose web endpoints.
  • Connect - Connect provides middleware for node.js that allow HTTP requests and responses can be altered while in 'flight'. The express framework also uses connect middleware and middleware are exposed from the express framework as well.

General Overview

The Express library which includes the Connect middleware provides an easy way to modify HTTP request and responses as they are being processed. Each step that the OpenHIM needs to perform can be written as Connect middleware. Each middleware can handle a different aspect of processing that the OpenHIM need to perform such as authentication, authorization, message persistence and message routing. Developing each of these steps as connect middleware allows them to be easily reused and allows us to add new step for future versions.

More information on Connect middleware can be found here: http://www.senchalabs.org/connect/ and http://stephensugden.com/middleware_guide/

The express library gives us some convenience req and res objects that are designed to be used for web applications but they are equally useful for handling web services.

Logical Architecture

Authentication and Authorization

The are two possible combinations of authentication that the interoperability layer should provide to determine a users identity:

  • HTTP basic authentication
  • ATNAs Node Authentication (PKI)

Once identify has been established the IL core component should check if that user has the authority to access the requested service.

Authentication

User details for authentication are stored in the MongoDB database is the following format. Either a password or a certificate (in binary form) is stored in this structure depending on whether the user chooses to use PKI or HTTP basic auth to authenticate users.

user.json
{
	"userID": "Musha_OpenMRS",
	"name": "OpenMRS Mush instance",
	"roles": [ "OpenMRS_PoC", "PoC" ],
	"password": "",
	"cert": ""
}

When authentication is set to HTTP basic auth then connect middleware is setup to intercept the request as soon as it enters the HIM as shown above. This middleware will read user details out of the MongoDB store to determine if the user can be authenticated. If the user is rejected an error is returned else the request is considered authenticated and is then passed onto the authorization step.

TODO - should we use passport node module?

Authorization

The OpenHIM only performs simple authorization based on the path that is being requested. It should be able to restrict access to certain paths to users with particular roles. The channel description shown in the router section below shows that each path has one or more allowed roles or user associated with it. The authorization component will check if the authenticated user has the authority to access the current path. It authorized the request will be passed on, else, the request will be denied and a HTTP 401 message will be returned.

Persistence

Each request and response will be persisted so that it can be logged and so that error'd transaction may be re-run. This persistence occurs at two stages. Firstly, once a request is authenticated and authorised and secondly once a response has been received from the external service. All the metadata about a transaction is stored in a single document in MongoDB. The relevant sections are just updated as new information is received. The structure of this information is shown below.

In addition the ability to store orchestration steps exists in the structure. We anticipate exposing a web service to enable mediators to report requests and responses that they make to/receive from external services and have these stored alongside the actual transaction.

TODO - Should we use mongoose or plain mongodb node.js module?

transaction.json
{
	"transactionId": "123",
	"status": "Processing|Failed|Completed",
	"userId": "Musha_OpenMRS",
	"request": {
		"path": "/api/test",
		"headers": [
			{ "header1": "value1" },
			{ "header2": "value2" }
		],
		"requestParams": [
			{ "param1": "value1" },
			{ "param2": "value2" }
		],
		"body": "<HTTP body>",
		"method": "POST",
		"timestamp": "<ISO 8601>"
	},
	"response": {
		"status": 201,
		"body": "<HTTP body>",
		"headers": [
			{ "header1": "value1" },
			{ "header2": "value2" }
		],
		"timestamp": "<ISO 8601>"
	},
	"routes": [
		{
			"name": "<route name>"
			// Same structure as above
			"request": { ... },
			"response": { ... }
		}
	]
	"orchestrations": [
		{
			"name": "<orchestration name>"
			// Same structure as above
			"request": { ... },
			"response": { ... }
		}
	]
	"properties": [ // optional meta data about a transaction
		{ "prop1": "value1" },
		{ "prop2": "value2" }
	]
}

Router

The router allows request to be forwarded to one or more external services (these could be mediators or an actual HIE component). It describes where to forward the request and the who has access to that request. A route can be marked as primary there are more than one routes to which a request is forwarded. The primary route is the route where the response is expected to be relayed back to the service request that is making use of the OpenHIM.

A custom router will have to be developed that can route according to these rules. The router can be build using the node.js functions provides to make HTTP request and responses can be relayed using the .pipe() function.

channels.json
[
	{
		"name": "Some Registry Channel",
		"urlPattern": "test/sample/.+",
		"allow": "*",
		"deny": [ "Mallet" ],
		"routes": [
			{
				"name": "Some Registry",
				"path": "some/other/path" // this is optional if left out original path is used
				"host": "localhost",
				"port": 8080	
			}
			
		]
	},
	{
		"name": "Some Registry Channel",
		"urlPattern": "test/sample2/.+/test2",
		"allow": [ "Alice","Bob" ],
		"routes": [
			{
				"name": "Some Registry",
				"host": "localhost",
				"port": 8080,
				"primary": true
			},
			{
				"name": "Logger",
				"host": "log-host",
				"port": 4789	
			}
		]
	}
]

Restful API

The OpenHIM must also expose a restful API that enables it to be configured and to allow access to the transaction that it has logged. This restful API will drive a web application that can allow the OpenHIM to be configured and will allow allow transaction to be viewed and monitored.

The API must supply CRUD access to the following constructs:

  • transaction logs
  • transaction channels
  • users

It should also allow for the following actions:

  • single and batch re-processing of transactions
  • querying for monitoring statistics

The API is define in using RAML in the following code:

OpenHIM API - RAML
#%RAML 0.8
title: OpenHIM API
version: v0.1
/users:
  get:
    description: Retrieves a list of users
    queryParameters:
      role:
        displayName: Role
        description: The role that a user must have to be retrieved
        example: PoC_Role
        type: string
        required: false
    responses:
      200:
        body:
          application/json:
            example: |
              [
                {
                  "userID": "User1",
                  "name": "User1",
                  "roles": [ "OpenMRS_PoC", "PoC" ],
                  "password": "",
                  "cert": ""
                },
                {
                  "userID": "User2",
                  "name": "User2",
                  "roles": [ "OpenMRS_PoC", "PoC" ],
                  "password": "",
                  "cert": ""
                }
              ]
      401:
        description: No users found
  post:
    description: Creates a new user
    body:
      application/json:
        example: |
          {
            "userID": "Musha_OpenMRS",
            "name": "OpenMRS Mush instance",
            "roles": [ "OpenMRS_PoC", "PoC" ],
            "password": "",
            "cert": ""
          }
    responses:
      201:
        description: The user was created sucessfully
  /{userId}:
    get:
      description: Retrieves the details for a specific user
      responses:
        200:
          body:
            application/json:
              example: |
                {
                  "userID": "Musha_OpenMRS",
                  "name": "OpenMRS Mush instance",
                  "roles": [ "OpenMRS_PoC", "PoC" ],
                  "password": "",
                  "cert": ""
                }
    put:
      description: Updates the details for a specific user
      body:
        application/json:
          example: |
            {
              "userID": "Musha_OpenMRS",
              "name": "OpenMRS Mush instance",
              "roles": [ "OpenMRS_PoC", "PoC" ],
              "password": "",
              "cert": ""
            }
      responses:
        200:
          description: The user was successfully updated
    delete:
      description: Deletes a specific users details
      responses:
        200:
          description: The user was successfully deleted
/transactions:
  get:
    description: Retrieves a list of transactions
    queryParameters:
      fromDate:
        displayName: From Date
        description: The date to filter results from in ISO8601 format
        example: "30-12-2013T00:00:00"
        type: date
        required: false
      toDate:
        displayName: To Date
        description: The date to filter results to in ISO8601 format
        example: "30-12-2013T00:00:00"
        type: date
        required: false
      status:
        displayName: Status
        description: The processing status of the transaction
        example: Completed
        type: string
        required: false
      userId:
        displayName: User ID
        description: The user ID to filter transactions by
        example: User123
        type: string
        required: false
      uriLike:
        displayName: URI Pattern
        description: A regex pattern to filter the transaction URI by
        example: "/test/sample/.+"
        type: string
        required: false
    responses:
      200:
        body:
          application/json:
            example: |
              [
                {
                  "transactionId": "123",
                  "status": "Processing|Failed|Completed",
                  "userId": "Musha_OpenMRS",
                  "request": {
                      "path": "/api/test",
                      "headers": [
                          { "header1": "value1" },
                          { "header2": "value2" }
                      ],
                      "requestParams": [
                          { "param1": "value1" },
                          { "param2": "value2" }
                      ],
                      "body": "<HTTP body>",
                      "method": "POST",
                      "timestamp": "<ISO 8601>"
                  },
                  "response": {
                      "status": 201,
                      "body": "<HTTP body>",
                      "headers": [
                          { "header1": "value1" },
                          { "header2": "value2" }
                      ],
                      "timestamp": "<ISO 8601>"
                  },
                  "routes": [
                      {
                          "name": "<route name>"
                          // Same structure as above
                          "request": { ... },
                          "response": { ... }
                      }
                  ]
                  "orchestrations": [
                      {
                          "name": "<orchestration name>"
                          // Same structure as above
                          "request": { ... },
                          "response": { ... }
                      }
                  ]
                  "properties": [ // optional meta data about a transaction
                      { "prop1": "value1" },
                      { "prop2": "value2" }
                  ]
                }
              ]
  /{transactionId}:
    get:
      description: Retrieves the details of a specific transaction
      responses:
        200:
          body:
            application/json:
              example: |
                {
                  "transactionId": "123",
                  "status": "Processing|Failed|Completed",
                  "userId": "Musha_OpenMRS",
                  "request": {
                      "path": "/api/test",
                      "headers": [
                          { "header1": "value1" },
                          { "header2": "value2" }
                      ],
                      "requestParams": [
                          { "param1": "value1" },
                          { "param2": "value2" }
                      ],
                      "body": "<HTTP body>",
                      "method": "POST",
                      "timestamp": "<ISO 8601>"
                  },
                  "response": {
                      "status": 201,
                      "body": "<HTTP body>",
                      "headers": [
                          { "header1": "value1" },
                          { "header2": "value2" }
                      ],
                      "timestamp": "<ISO 8601>"
                  },
                  "routes": [
                      {
                          "name": "<route name>"
                          // Same structure as above
                          "request": { ... },
                          "response": { ... }
                      }
                  ]
                  "orchestrations": [
                      {
                          "name": "<orchestration name>"
                          // Same structure as above
                          "request": { ... },
                          "response": { ... }
                      }
                  ]
                  "properties": [ // optional meta data about a transaction
                      { "prop1": "value1" },
                      { "prop2": "value2" }
                  ]
                }
    put:
      description: Updates the details for a specific transaction
      body:
        application/json:
          example: |
            {
              "transactionId": "123",
              "status": "Processing|Failed|Completed",
              "userId": "Musha_OpenMRS",
              "request": {
                  "path": "/api/test",
                  "headers": [
                      { "header1": "value1" },
                      { "header2": "value2" }
                  ],
                  "requestParams": [
                      { "param1": "value1" },
                      { "param2": "value2" }
                  ],
                  "body": "<HTTP body>",
                  "method": "POST",
                  "timestamp": "<ISO 8601>"
              },
              "response": {
                  "status": 201,
                  "body": "<HTTP body>",
                  "headers": [
                      { "header1": "value1" },
                      { "header2": "value2" }
                  ],
                  "timestamp": "<ISO 8601>"
              },
              "routes": [
                  {
                      "name": "<route name>"
                      // Same structure as above
                      "request": { ... },
                      "response": { ... }
                  }
              ]
              "orchestrations": [
                  {
                      "name": "<orchestration name>"
                      // Same structure as above
                      "request": { ... },
                      "response": { ... }
                  }
              ]
              "properties": [ // optional meta data about a transaction
                  { "prop1": "value1" },
                  { "prop2": "value2" }
              ]
            }
      responses:
        200:
          description: Transaction updated successfully
    delete:
      description: Deletes a specific transaction
      responses:
        200:
          description: Transaction deleted successfully
/channels:
  get:
    description: Retrieves the list of active channels
    queryParameters:
      uriLike:
        displayName: URI Pattern
        description: A regex pattern to filter the channel URI by
        example: "/test/sample/.+"
        type: string
        required: false
    responses:
      200:
        body:
          application/json:
            example: |
              [
                {
                    "name": "Some Registry Channel",
                    "urlPattern": "test/sample/.+",
                    "allow": "*",
                    "deny": [ "Mallet" ],
                    "routes": [
                        {
                            "name": "Some Registry",
                            "path": "some/other/path" // this is optional if left out original path is used
                            "host": "localhost",
                            "port": 8080 
                        }
                         
                    ]
                },
                {
                    "name": "Some Registry Channel",
                    "urlPattern": "test/sample2/.+/test2",
                    "allow": [ "Alice","Bob" ],
                    "routes": [
                        {
                            "name": "Some Registry",
                            "host": "localhost",
                            "port": 8080,
                            "primary": true
                        },
                        {
                            "name": "Logger",
                            "host": "log-host",
                            "port": 4789 
                        }
                    ]
                }
              ]          
  post:
    description: Creates a new channel
    body:
      application/json:
        example: |
          {
            "name": "Some Registry Channel",
            "urlPattern": "test/sample2/.+/test2",
            "allow": [ "Alice","Bob" ],
            "routes": [
              {
                  "name": "Some Registry",
                  "host": "localhost",
                  "port": 8080,
                  "primary": true
              },
              {
                  "name": "Logger",
                  "host": "log-host",
                  "port": 4789 
              }
            ]
          }
    responses:
      201:
        description: Channel successfully created
  /{channelName}:
    get:
      description: Retrieves the details for a specific channel
      responses:
        200:
          body:
            application/json:
              example: |
                {
                  "name": "Some Registry Channel",
                  "urlPattern": "test/sample2/.+/test2",
                  "allow": [ "Alice","Bob" ],
                  "routes": [
                    {
                        "name": "Some Registry",
                        "host": "localhost",
                        "port": 8080,
                        "primary": true
                    },
                    {
                        "name": "Logger",
                        "host": "log-host",
                        "port": 4789 
                    }
                  ]
                }
    put:
      description: Updates the details for a specific channel
      body:
        application/json:
          example: |
            {
              "name": "Some Registry Channel",
              "urlPattern": "test/sample2/.+/test2",
              "allow": [ "Alice","Bob" ],
              "routes": [
                {
                    "name": "Some Registry",
                    "host": "localhost",
                    "port": 8080,
                    "primary": true
                },
                {
                    "name": "Logger",
                    "host": "log-host",
                    "port": 4789 
                }
              ]
            }
      responses:
        200:
          description: The channel was successfully updated
    delete:
      description: Deletes a specific channels details
      responses:
        200:
          description: The channel was successfully deleted
/monitor:
  get:
    description: Retrieves overall monitoring details
    responses:
      200:
        body:
          application/json:
            example: |
              [
                TODO
              ]
  /{channelName}:
    get:
      description: Retrieves monitoring details for a particular channel
      responses:
        200:
          body:
            application/json:
              example: |
                {
                  TODO
                }
    

 

 

  • No labels