npm package: @hmsconnect/hmshealthapi
Description: This is the SDK for HMS OpenHealth APIs to connect and get data from HMSConnect.
Author: PatharaNor
Latest version: 0.4.3

# **HMS OpenHealth APIs**

This is the SDK for HMS OpenHealth APIs to connect and get data from HMSConnect.

## **Installation**

```bash
$ npm i @hmsconnect/hmshealthapi --save
```

## **HMS APIs Usage**

### **Using NodeJS/CLI**

**Initial/Config SDK**

```js
// Declare HMS instance object
const hmshealthapi = require('@hmsconnect/hmshealthapi');

let tokens = {
  username: '[YOUR_USER_NAME]',
  password: '[YOUR_PASSWORD]',
  client_id: '[YOUR_CLIENT_ID]'
};

// Create instance
let hms = hmshealthapi();

// Using the instance
// ...
```

### **Sandbox Configuration**

You can config specific data source via method name `SetConfig`. For main attributes of the config,
please refer to JSON object below :

```js
{
  host: '[DATA_SOURCE_IP_OR_HOSTNAME]',
  endpoints: {
    get_domain_resource : '/sandbox/[DATA_SOURCE]/[SOURCE_VERSION]',
    get_version : '/sandbox/version'
  }
}
```

Where

 - **host** - it can be IP with port number or host name
 - **endpoints.get_domain_resource** - an endpoint to request data in specific domain resource. The endpoint **MUST** add prefix with `/`. 
 - **endpoints.get_version** - current sandbox API version (may not relate with lib version)
 
For `DATA_SOURCE`, we provide 2-sources including :
 - `api` for data source name `HMS`
 - `sfhir` for data source name `SmartFHIR` 
 
`SOURCE_VERSION` is version of data source, including :
 - `HMS` has only version `v2`
 - `SmartFHIR` has only version `stu3`

Testing sandbox connection by checking version

```js
let tokens = {
  username: '[YOUR_USER_NAME]',
  password: '[YOUR_PASSWORD]',
  client_id: '[YOUR_CLIENT_ID]'
};

let hms = hmshealthapi();
hms.Initial(tokens, (error, response) => {
  
  if(error) {
    console.log('Initial error : ', error);
  } else {
    console.log('response :', response);

    // Config data source
    hms.SetConfig({
      host: '[DATA_SOURCE_IP_OR_HOSTNAME]',
      endpoints: {
        get_domain_resource : '/sandbox/[DATA_SOURCE]/[SOURCE_VERSION]',
        get_version : '/sandbox/version'
      }
    });
  }

});
```

**Searching with request URL style**

```js

hms.Initial(tokens, (error, response) => {

  if(error) {
    console.log('Initial error : ', error);
  } else {

    // ...

    // -----------------------------------------------------
    // ------------------ HMS Data source ------------------
    // -----------------------------------------------------

    // Config data source
    hms.SetConfig({
      host: '[DATA_SOURCE_IP_OR_HOSTNAME]',
      endpoints: {
        get_domain_resource : '/sandbox/api/v2',
        get_version : '/sandbox/version'
      }
    });

    try {

      const headers = {
        Authorization: `Bearer ${response.access_token ? response.access_token : ''}`
      };

      console.log('Header : ', headers)

      hms.get('practitioner', headers, 'name.givenName:contain="Rozelle 105418"&sort=-identifier.start')
      .then((response) => { console.log('Searching with request URL:', JSON.stringify(response, null, 2)); })
      .catch((err) => { console.log('Error :', err); });

    } catch(err) {
      console.log(err)
    }



    // -----------------------------------------------------
    // --------------- SmartFHIR Data source ---------------
    // -----------------------------------------------------

    // Config for SmartFHIR(vSTU3) data source
    hms.SetConfig({
      host: '[DATA_SOURCE_IP_OR_HOSTNAME]',
      endpoints: {
        get_domain_resource : '/sandbox/sfhir/stu3',
        get_version : '/sandbox/version'
      }
    });

    try {
      
      const headers = {
        Authorization: `Bearer ${response.access_token ? response.access_token : ''}`
      };

      console.log('Header : ', headers)

      hms.get('patient', headers, 'id=0debf275-d585-4897-a8eb-25726def1ed5')
      .then((response) => { console.log('Searching with request URL:', JSON.stringify(response, null, 2)); })
      .catch((err) => { console.log('Error :', err); });

    } catch(err) {
      console.log(err)
    }
  }

});
```

<details>
<summary>Example output for HMS data source :</summary>
<p>

```bash
Searching with request URL:{
  "error": null,
  "message": "Query Success",
  "page": 1,
  "limit": 10,
  "total": 9,
  "data": [
    {
      "resourceType": "Practitioner",
      "employeeId": null,
      "careProviderId": "10.......A",
      "identifier": [
        {
          "type": "care_provider_id",
          "value": "105......A",
          "start": "2013-10-11",
          "end": null
        }
      ],
      "active": true,
      "gender": "unknown",
      "birthDate": "",
      "licenseNo": "2.......7",
      "name": [
        {
          "text": "นพ. Rozelle 10.........A",
          "prefix": "นพ.",
          "givenName": "Rozelle 10.........A",
          "middleName": null,
          "familyName": "Scala 10.........A"
        }
      ],
      "telecom": [
        {
          "type": "phone",
          "value": "0...........7"
        }
      ],
      "qualification": [
        {
          "code": "28",
          "display": "Part time"
        }
      ],
      "specialty": [
        {
          "code": "SURG",
          "display": "ศัลยศาสตร์ (SURGERY)"
        }
      ],
      "role": [
        {
          "code": null,
          "display": null
        }
      ],
      "location": [],
      "organization": {
        "codeNumber": "078",
        "codeName": "YMH",
        "name": null,
        "latitude": null,
        "longitude": null
      },
      "created": "2020-01-14T15:02:05",
      "updated": ""
    },
    {
      "resourceType": "Practitioner",
      "employeeId": null,
      "careProviderId": "10.........A",
      "identifier": [
        {
          "type": "care_provider_id",
          "value": "10.........A",
          "start": "2013-08-13",
          "end": null
        }
      ],
      "active": true,
      "gender": "unknown",
      "birthDate": "",
      "licenseNo": "ท.1.......9",
      "name": [
        {
          "text": "ทพ. Rozelle 10.........A",
          "prefix": "ทพ.",
          "givenName": "Rozelle 10.........A",
          "middleName": null,
          "familyName": "Scala 10.........A"
        }
      ],
      "telecom": [
        {
          "type": "phone",
          "value": "08.......11"
        }
      ],
      "qualification": [
        {
          "code": "2",
          "display": "แพทย์นอกเวลา (แพทย์เวร)"
        }
      ],
      "specialty": [
        {
          "code": "DEN",
          "display": "ทันตกรรม (DENTIST)"
        }
      ],
      "role": [
        {
          "code": null,
          "display": null
        }
      ],
      "location": [],
      "organization": {
        "codeNumber": "078",
        "codeName": "YMH",
        "name": null,
        "latitude": null,
        "longitude": null
      },
      "created": "2020-01-14T15:02:05",
      "updated": ""
    },
    {
      "resourceType": "Practitioner",
      "employeeId": null,
      "careProviderId": "10.........A",
      "identifier": [
        {
          "type": "care_provider_id",
          "value": "10.........A",
          "start": "2013-06-29",
          "end": null
        }
      ],
      "active": true,
      "gender": "unknown",
      "birthDate": "",
      "licenseNo": "3.......7",
      "name": [
        {
          "text": "นพ. Rozelle 10.........A",
          "prefix": "นพ.",
          "givenName": "Rozelle 10.........A",
          "middleName": null,
          "familyName": "Scala 10.........A"
        }
      ],
      "telecom": [
        {
          "type": "phone",
          "value": "08.........6"
        }
      ],
      "qualification": [
        {
          "code": "1",
          "display": "แพทย์ประจำ"
        }
      ],
      "specialty": [
        {
          "code": "ORT",
          "display": "กระดูก (ORTHOPAEDIST)"
        }
      ],
      "role": [
        {
          "code": null,
          "display": null
        }
      ],
      "location": [],
      "organization": {
        "codeNumber": "078",
        "codeName": "YMH",
        "name": null,
        "latitude": null,
        "longitude": null
      },
      "created": "2020-01-14T15:02:05",
      "updated": ""
    },
    {
      "resourceType": "Practitioner",
      "employeeId": null,
      "careProviderId": "10.........A",
      "identifier": [
        {
          "type": "care_provider_id",
          "value": "10.........A",
          "start": "2013-06-26",
          "end": null
        }
      ],
      "active": true,
      "gender": "unknown",
      "birthDate": "",
      "licenseNo": "3........9",
      "name": [
        {
          "text": "พญ. Rozelle 10.........A",
          "prefix": "พญ.",
          "givenName": "Rozelle 10.........A",
          "middleName": null,
          "familyName": null
        }
      ],
      "telecom": [
        {
          "type": "phone",
          "value": "08........0"
        }
      ],
      "qualification": [
        {
          "code": "1",
          "display": "แพทย์ประจำ"
        }
      ],
      "specialty": [
        {
          "code": "NEUM",
          "display": "อายุรกรรมประสาท (NEUROLOGIST)"
        }
      ],
      "role": [
        {
          "code": null,
          "display": null
        }
      ],
      "location": [],
      "organization": {
        "codeNumber": "078",
        "codeName": "YMH",
        "name": null,
        "latitude": null,
        "longitude": null
      },
      "created": "2020-01-14T15:02:05",
      "updated": ""
    },
    {
      "resourceType": "Practitioner",
      "employeeId": null,
      "careProviderId": "10.........A",
      "identifier": [
        {
          "type": "care_provider_id",
          "value": "10.........A",
          "start": "2013-06-21",
          "end": "2017-12-20"
        }
      ],
      "active": true,
      "gender": "unknown",
      "birthDate": "",
      "licenseNo": "4.......0",
      "name": [
        {
          "text": "พญ. Rozelle 10.........A",
          "prefix": "พญ.",
          "givenName": "Rozelle 10.........A",
          "middleName": null,
          "familyName": "Scala 10.........A"
        }
      ],
      "telecom": [
        {
          "type": "phone",
          "value": "08........4"
        }
      ],
      "qualification": [
        {
          "code": "28",
          "display": "Part time"
        }
      ],
      "specialty": [
        {
          "code": "GP ",
          "display": "เวชปฎิบัติทั่วไป  (GENERAL PRACTICE)"
        }
      ],
      "role": [
        {
          "code": null,
          "display": null
        }
      ],
      "location": [],
      "organization": {
        "codeNumber": "078",
        "codeName": "YMH",
        "name": null,
        "latitude": null,
        "longitude": null
      },
      "created": "2020-01-14T15:02:05",
      "updated": ""
    },
    {
      "resourceType": "Practitioner",
      "employeeId": null,
      "careProviderId": "10.........A",
      "identifier": [
        {
          "type": "care_provider_id",
          "value": "10.........A",
          "start": "2013-06-18",
          "end": null
        }
      ],
      "active": true,
      "gender": "unknown",
      "birthDate": "",
      "licenseNo": "3........2",
      "name": [
        {
          "text": "พญ. Rozelle 10.........A",
          "prefix": "พญ.",
          "givenName": "Rozelle 10.........A",
          "middleName": null,
          "familyName": "Scala 10.........A"
        }
      ],
      "telecom": [
        {
          "type": "phone",
          "value": "08.........6"
        }
      ],
      "qualification": [
        {
          "code": "1",
          "display": "แพทย์ประจำ"
        }
      ],
      "specialty": [
        {
          "code": "MED",
          "display": "อายุรศาสตร์ทั่วไป (INTERNAL MEDICINE)"
        }
      ],
      "role": [
        {
          "code": null,
          "display": null
        }
      ],
      "location": [],
      "organization": {
        "codeNumber": "078",
        "codeName": "YMH",
        "name": null,
        "latitude": null,
        "longitude": null
      },
      "created": "2020-01-14T15:02:05",
      "updated": ""
    },
    {
      "resourceType": "Practitioner",
      "employeeId": null,
      "careProviderId": "10.........A",
      "identifier": [
        {
          "type": "care_provider_id",
          "value": "10.........A",
          "start": "2013-05-30",
          "end": null
        }
      ],
      "active": true,
      "gender": "unknown",
      "birthDate": "",
      "licenseNo": "3........3",
      "name": [
        {
          "text": "นพ. Rozelle 10.........A",
          "prefix": "นพ.",
          "givenName": "Rozelle 10.........A",
          "middleName": null,
          "familyName": "Scala 10.........A"
        }
      ],
      "telecom": [
        {
          "type": "phone",
          "value": "0..........8"
        }
      ],
      "qualification": [
        {
          "code": "1",
          "display": "แพทย์ประจำ"
        }
      ],
      "specialty": [
        {
          "code": "NEUM",
          "display": "อายุรกรรมประสาท (NEUROLOGIST)"
        },
        {
          "code": "MED",
          "display": "อายุรศาสตร์ทั่วไป (INTERNAL MEDICINE)"
        }
      ],
      "role": [
        {
          "code": null,
          "display": null
        }
      ],
      "location": [],
      "organization": {
        "codeNumber": "078",
        "codeName": "YMH",
        "name": null,
        "latitude": null,
        "longitude": null
      },
      "created": "2020-01-14T15:02:05",
      "updated": ""
    },
    {
      "resourceType": "Practitioner",
      "employeeId": null,
      "careProviderId": "10.........A",
      "identifier": [
        {
          "type": "care_provider_id",
          "value": "10.........A",
          "start": "2013-05-07",
          "end": null
        }
      ],
      "active": true,
      "gender": "unknown",
      "birthDate": "",
      "licenseNo": "3......1",
      "name": [
        {
          "text": "พญ. Rozelle 10.........A",
          "prefix": "พญ.",
          "givenName": "Rozelle 10.........A",
          "middleName": null,
          "familyName": "Scala 10.........A"
        }
      ],
      "telecom": [
        {
          "type": "phone",
          "value": "0........5"
        }
      ],
      "qualification": [
        {
          "code": "1",
          "display": "แพทย์ประจำ"
        }
      ],
      "specialty": [
        {
          "code": "MED",
          "display": "อายุรศาสตร์ทั่วไป (INTERNAL MEDICINE)"
        },
        {
          "code": "GI",
          "display": "ทางเดินอาหาร (GASTROENTEROLOGIST)"
        }
      ],
      "role": [
        {
          "code": null,
          "display": null
        }
      ],
      "location": [],
      "organization": {
        "codeNumber": "078",
        "codeName": "YMH",
        "name": null,
        "latitude": null,
        "longitude": null
      },
      "created": "2020-01-14T15:02:05",
      "updated": ""
    },
    {
      "resourceType": "Practitioner",
      "employeeId": null,
      "careProviderId": "10.........A",
      "identifier": [
        {
          "type": "care_provider_id",
          "value": "10.........A",
          "start": "2013-03-29",
          "end": null
        }
      ],
      "active": true,
      "gender": "unknown",
      "birthDate": "",
      "licenseNo": "33.....7",
      "name": [
        {
          "text": "พญ. Rozelle 10.........A",
          "prefix": "พญ.",
          "givenName": "Rozelle 10.........A",
          "middleName": null,
          "familyName": "Scala 10.........A"
        }
      ],
      "telecom": [
        {
          "type": "phone",
          "value": "0.......998"
        }
      ],
      "qualification": [
        {
          "code": "1",
          "display": "แพทย์ประจำ"
        }
      ],
      "specialty": [
        {
          "code": "PHYR",
          "display": "เวชศาสตร์ฟื้นฟู (PHYSICAL & REHABILITATION MEDICINE)"
        },
        {
          "code": "REH",
          "display": "กายภาพ (PHYSIAPRIST)"
        }
      ],
      "role": [
        {
          "code": null,
          "display": null
        }
      ],
      "location": [],
      "organization": {
        "codeNumber": "078",
        "codeName": "YMH",
        "name": null,
        "latitude": null,
        "longitude": null
      },
      "created": "2020-01-14T15:02:05",
      "updated": ""
    }
  ]
}
```
</p>
</details>

<hr>

**IMPORTANT** : Now HMS OpenHealth API support searching with request URL only

<details>
<summary><strong>2020-04-23 : [DEPRECATED] We plan to unused "getVersion()" after 2020-04-22</strong></summary>
<p>
The function using for get latest version of sandbox, we plan to use API instead of built-in method.
</p>
</details>

<details>
<summary><strong>2020-04-02 : [PENDING] Pending authentication method by using "license key" (Now we using OAuth2 instead)</strong></summary>
<p>

```js
let tokens = {
  // Sandbox license token
  license:'9b55b572-154e-3739-bebd-56ca7e2c5aa8',
    
  // Sandbox didn't require token below
  client_id: '',
  client_secret: ''
};

let hms = hmshealthapi();
hms.Initial(tokens, (error, response) => {
  if(error) {
    console.log('Initial : ', error);
  } else {
    console.log('Initial : ', response);

    // Your app here
  }
});
```

**Searching with request URL style**

```js

hms.Initial(tokens, (error, response) => {

  if(error) {
    console.log('Initial : ', error);
  } else {

    // ...

    const headers = {
        Authorization: `Bearer ${response.access_token ? response.access_token : ''}`
    };

    hms.get('practitioner', headers, 'name.givenName:contain="Rozelle 105418"&sort=-identifier.start')
    .then((response) => { console.log('Searching with request URL:', JSON.stringify(response, null, 2)); })
    .catch((err) => { console.log('Error :', err); });
  }

});
```
</p>
</details>

<details>
<summary><strong>2019-11-12 : [DEPRECATED] Searching with request body style : Find match</strong></summary>
<p>

```js
sb
.findMatch('patient', { hn:'7.........51' })
.then((response) => { console.log('search with findMatch:', response.data) })
.catch((err) => { console.log('findMatch:', err) })
```
</p>
</details>

<details>
<summary><strong>2019-12-01 : [DEPRECATED] Searching with request body style : Contains</strong></summary>
<p>

```js
sb
.contains(

  // Specific domain resource
  'encounter', 

  // In case, you remember some text on that field(hn).
  { hn:'952151' }, 

  // Search option
  { 
    page:1,                 // <----- support pagination feature
    limit:10,               // <----- limit record per page
                            //        (by default, minimum is 10)
    sort: { 
      ['period.start']:1    // <----- sort specific field with 
                            //         -  1 is "ascending"
                            //         - -1 is "descending"
    } 
  }
)
.then((response) => { console.log('search with contains :', response.data) })
.catch((err) => { console.log('findMatch:', err) })
```
</p>
</details>

### **UMD**

Our SDK support client side too, please refer to [example](./example/index.html)

## **Contribution**

At the end of each development phase of the API, you **MUST** pack the API by using `webpack` both **development** and **production**.

**IMPORTANT** : DON'T FORGET CHANGING VERSION IN `package.json` BASED ON [`MAJOR`].[`MINOR`].[`PATCH`].

```json
{
  "name": "@hmsconnect/hmshealthapi",
  "version": "1.0.4",      <---------------- HERE
  "main": "src/App.es6",
  "scripts": {
    ...
  }
  .
  .
  .
}
```

Let's pack the API via command:

```bash
# For development, hmshealthapi.js
$ npm run build:dev

# and

# For production (with minimize code), hmshealthapi.min.js
$ npm run build:prod
```

After that we will publish `HMSHealthAPIs` to `npm` server by calling our custom script to publish it:

```bash
$ sh publish.sh
```

See result :
https://www.npmjs.com/package/@hmsconnect/hmshealthapi

## **License**

MIT