Skip to content

Getting Started with SIHOT@360° using Node.js

Overview

In this tutorial we will be looking at the basics of the SIHOT@360° API using the popular Node.js environment.

For the demo purpose we will:

  • create a SOAP client from the provided WSDL
  • authenticate at the server
  • retrieve all in house guests using the API
  • finally we will be retrieving the in house guest list every 1820 seconds from the server to verify the securityID updates (usually only required all 3600 seconds)

What You Will Need

  • Internet access
  • Basic knowledge of Node.js
  • Access to a 360° service such as public WSDL
  • Node.js (v12.16.3++)

The Tutorial

This tutorial will only use minimal dependencies to demonstrate the API usage. Any server components like express outputs like html are omitted to focus on the API usage itself.

Step 1 – Loading Dependencies

Initialize the project and load dependencies.

C:\tutorial>npm init
This utility will walk you through creating a package.json file.
It only covers the most common items, and tries to guess sensible defaults.

See `npm help json` for definitive documentation on these fields
and exactly what they do.

Use `npm install <pkg>` afterwards to install a package and
save it as a dependency in the package.json file.

Press ^C at any time to quit.
package name: (tutorial) gettingstarted360
version: (1.0.0)
description: Getting Started with SIHOT 360° in Node.js
entry point: (index.js)
test command:
git repository:
keywords:
author:
license: (ISC)
About to write to C:\tutorial\package.json:

{
  "name": "gettingstarted360",
  "version": "1.0.0",
  "description": "Getting Started with SIHOT 360° in Node.js",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC"
}


Is this OK? (yes) y

After the application is initialized let's add the soap dependency.

npm install soap --save
npm install dotenv --save

After the installation is done the package.json should look like this:

{
  "name": "gettingstarted360",
  "version": "1.0.0",
  "description": "Getting Started with SIHOT 360┬░ in Node.js",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "dotenv": "^10.0.0",
    "soap": "^0.41.0"
  }
}

Step 2 – Initialize the client

There are two options to initialize the client. 1. Using the hosted WSDL 2. Using a local WSDL

Even while both options work the same there are differences you may want to take in consideration when developing for production.

Option 1 relies on an online resource during startup, which may not be available at the point in time. Therefore additional work would need to be done to cater for this. Option 2 only relies on the online resource when calling the service, which is easier to manage. The downside of option 2 is, that if new services will made available these will not automatically be available to your project. Since the later is easy to manage and gives better control to you over your project, option two is recommended.

1. Hosted WSDL

require('dotenv').config()
const soap = require('soap');

const wsdlurl = 'https://partner-api.sihot.com/PDOCS/API/CBS/SihotServices01?wsdl';

let client = null;

async function init() {
    try {
        console.log("init SOAP Client");
        client = await soap.createClientAsync(wsdlurl);
    }
    catch (e) {
        console.error("Initialization of SOAP Client failed.");
        console.error(e);
    }
}

2. local WSDL with redirect to the endpoint


require('dotenv').config();
const soap = require('soap');

const localwsdl = "./SihotServices01.xml";
const wsEndPoint = "https://partner-api.sihot.com/PDOCS/API/CBS/SihotServices01";

let securityID = "";
let tokenValidUntil = new Date();
let client = null;

async function init() {
    try {
        console.log("init SOAP Client");

        client = await soap.createClientAsync(localwsdl);
        //- overwrite the SOAP service endpoint address
        client.setEndpoint(wsEndPoint);
    }
    catch (e) {
        console.error("Initialization of SOAP Client failed.");
        console.error(e);
    }
}

Step 3 – Authentication / Requesting a securityID (token)

To authenticate and receiving a securityID the authenticate request is required. See also Authentication for more information.

The structure of the AuthenticateRequest can be reviewed seen Authenticate

To separate code from the configuration we use the dotenv package. Create a file called ".env" with the following entries:

SIHOT_USER={PLACEYOURUSERHERE!}
SIHOT_PASSWORD={PLACEYOURPASWORDHERE!}
SIHOT_HOTEL={PLACESIHOTPROPERTYIDHERE!}

Ensure that you replace the entries with "PLACEYOUR"... accordingly.

// build the AuthenticateRequest
const AuthenticateRequest = {
    AuthenticationInfos: {
        "user": process.env.SIHOT_USER,
        "password": process.env.SIHOT_PASSWORD,
        "hotel": process.env.SIHOT_HOTEL
    }
};

// local variables to store the securityID as well as the invalidation timestamp
let securityID = ""
let tokenValidUntil = new Date()

async function getSecurityID() {
    try {
        const AuthenticateResponse = await client.AuthenticateAsync(AuthenticateRequest);

        securityID = AuthenticateResponse[0].Authentication[0].SecurityID;
        tokenValidUntil = new Date();
        // calculate the datetime when the token will be invalidated
        tokenValidUntil.setTime( tokenValidUntil.getTime() + (parseInt(AuthenticateResponse[0].Authentication[0].DurationInSec) * 1000) );
        console.log("New security received: " + securityID);
        console.log("New security valid until: " + tokenValidUntil.toLocaleString());
    }
    catch (e) {
        console.error("Authentication Failed");
        console.error(e);
    }
}

In this step we will write a new function that will call the api to retrieve the in-house guests for a certain day.

async function getInhouseGuests(dateString) {
    // ensure the securityID is still valid
    const now = new Date();
    if (tokenValidUntil <= now) {
        console.log(tokenValidUntil + " now it was " + now);
        console.log("getting new securityID .. ");
        await getSecurityID();
    }

    const S_GUEST_IN_HOUSE_SEARCH_V001Request = {
        Authentication: {
            SecurityID: securityID
        },
        GuestInHouseSearch: {
            date: dateString
        }
    }

    const S_GUEST_IN_HOUSE_SEARCH_V001Response = await client.S_GUEST_IN_HOUSE_SEARCH_V001Async(S_GUEST_IN_HOUSE_SEARCH_V001Request);
    if (S_GUEST_IN_HOUSE_SEARCH_V001Response.length <= 0 )
    {
        console.error ("Response invalid");
    }

    if ( S_GUEST_IN_HOUSE_SEARCH_V001Response[0].Result.Success !== "true" )
    {
        console.error ("Method call failed");
        console.error (S_GUEST_IN_HOUSE_SEARCH_V001Response[0].Result.ErrorMsg);
        console.error (S_GUEST_IN_HOUSE_SEARCH_V001Response[0].Result['MSG-LIST']);
    }
    showGuests(S_GUEST_IN_HOUSE_SEARCH_V001Response);
    return S_GUEST_IN_HOUSE_SEARCH_V001Response;
}

Step 5 – Showing the result

function showGuests(S_GUEST_IN_HOUSE_SEARCH_V001Response) {
    if ( S_GUEST_IN_HOUSE_SEARCH_V001Response[0].room == undefined )
    {
        console.log ("Room list was empty");
        return;
    }

    for ( let i = 0; i < S_GUEST_IN_HOUSE_SEARCH_V001Response[0].room.length; i++ )
    {
        const cat = S_GUEST_IN_HOUSE_SEARCH_V001Response[0].room[i].category;
        const room = S_GUEST_IN_HOUSE_SEARCH_V001Response[0].room[i].roomState[0].room;
        let sharerNames = "";
        for (j = 0; j < S_GUEST_IN_HOUSE_SEARCH_V001Response[0].room[i].Person.length; j++) {
            if (sharerNames.length > 0) {
                sharerNames += ","
            }
            sharerNames += S_GUEST_IN_HOUSE_SEARCH_V001Response[0].room[i].Person[j].name1;
        }
        console.log(`Room ${room} of type ${cat} occupied by: ${sharerNames} `);
    }
}

Step 6 – Bringing all together

async function main() {
    await init();
    await getSecurityID();

    const today = new Date().toISOString().substring(0, 10);
    const response = await getInhouseGuests(today);
    setInterval ( () => {getInhouseGuests (today)}, 1820000);   
}

main();

What is next?

Download of the Source

A complete working example can be downloaded at: - GettingStarted360_onNode.zip

To install the project run:

npm install

Please ensure that you enter username, password and hotel ot the .env configuration file!

run as

node index.js