Technology Blogs by Members
Explore a vibrant mix of technical expertise, industry insights, and tech buzz in member blogs covering SAP products, technology, and events. Get in the mix!
cancel
Showing results for 
Search instead for 
Did you mean: 
vvdries
Contributor
Hi CAP-Developers and enthusiasts!

In 2020 I’ve been playing around with SAP CAP and Cloud Foundry quite a lot. In the very beginning it was a lot to process, but I really enjoyed the journey and I love the results/demos it brought along.

I believe the coolest thing about CAP and the Cloud Foundry Environment, is the freedom it offers the developer. Every extra feature or integration is just one “plugin” away. If you can think it, you can build it. Obviously always make sure it makes sense.

On top of this CAP and Cloud Foundry freedom, we can even choose our preferred IDE. Usually this will be VS Code or the SAP Business Application Studio. Both IDE’s have their own advantages and disadvantages of course.

Now in this blog I want to make use of this freedom by using WebSocket’s in an SAP CAP application. As some of you may already know, I’m a big fan of real-time applications. Which is why I wanted to investigate this possibility and share it in a very basic example to get started. In the end you will notice there are more ways and maybe better and more performant solutions to implement WebSocket’s. But like I said, this is more about how to get started with the basics.

I chose the SAP Business Application Studio as development IDE for this example app. This because it offers me all the tools and features I need in a preconfigured workspace. Which makes it easier to follow the example as well.

What are we building?


We will be building an application where an employee of a company has the possibility to add an idea for a team event (mocked via service / no UI5 coding). The user will be notified (via a "MessageToast" in UI5) of all the newly created ideas, added by his or her colleagues in real-time. This without the need to refresh the application.

Long story short => a real-time application using WebSocket’s.

Building the app


The development of such an application might sound a little hard. But in the end you will see it only requires a few commands, CDS and JavaScript files.

The power of CAP to the fullest!

I will not go over every single step to setup a CAP application or UI-module since this blog is about "how to integrate WebSocket’s in a CAP app". The setup of a CAP app will not be a secret to most of you anymore. 😊  But I will list alle the commands to prepare the project in the SAP Business Application Studio so you can follow along.

Let’s get started!

Execute the following commands to initialize the CAP-project and add a Fiori Module and Approuter:
cds init RealTimeCAP
cd RealTimeCAP
yo fiori-module

Pass the following arguments to the "Yeoman fiori-module" generator:

Execute the following command to create a database schema:
touch db/schema.cds

Add following code to it, to create an "Idea" entity:
using {managed} from '@sap/cds/common';

namespace RealTimeCAP.db;

entity Ideas : managed {
key ID : UUID;
name : String;
description : String;
}

Execute the following command to create an OData Service for your "Idea" entity:
touch srv/catalog-idea-service.cds

Add following code to it, to expose the "Idea" entity:
using {RealTimeCAP.db as db} from '../db/schema';

service CatalogIdeaService {
entity Ideas as projection on db.Ideas;
}

Execute the following command to add custom logic to your OData Service:
touch srv/catalog-idea-service.js

Add following code to it:
const WebSocketServer = require('ws').Server;
const ws = new WebSocketServer({
port: process.env.PORT || 8080
});

module.exports = async (srv) => {
srv.after('CREATE', '*', async (ideas, req) => {
for (const client of ws.clients) {
client.send(JSON.stringify(ideas));
}
});
}

This custom logic in the “catalog-idea-service.js” file starts a WebSocketServer and will send the created entity response to every connected client. This because of the “after event handler” attached to the service. It will only be triggered for a “CREATE” operation, but for every single entity because of the wildcard “*”.

Execute the following command to install the WebSocket NPM module:
npm i ws

Execute the following command to install all the other dependencies inside the "package.json" file:
npm install

Execute the following command to deploy your database module to a local SQLite database:
cds deploy --to sqlite:myDatabase.db

Replace the configuration in the “xs-app.json” file inside your “realtimecap-approuter” with the following configuration:
{
"welcomeFile": "HTML5Module/index.html",
"authenticationMethod": "none",
"websockets": {
"enabled": true
},
"logout": {
"logoutEndpoint": "/do/logout"
},
"routes": [
{
"source": "^/NodeWS(.*)$",
"target": "$1",
"authenticationType": "none",
"destination": "NodeWs_api",
"csrfProtection": false
},
{
"source": "^/odataSrv/(.*)$",
"target": "/catalog/$1",
"authenticationType": "none",
"destination": "srv_api",
"csrfProtection": false
},
{
"source": "^/HTML5Module/(.*)$",
"target": "$1",
"authenticationType": "none",
"localDir": "../HTML5Module/webapp"
}
]
}

Notice that we enabled the “WebSockets” and a route for the “HTML5Module”, “OdataSrv” and “NodeWS” was added.

Execute the following command to create a "default-env.json" file to "mock" your destinations:
touch app/realtimecap-approuter/default-env.json

Add the following configuration to it:
{
"destinations": [
{
"name": "srv_api",
"url": "http://localhost:4004",
"forwardAuthToken": true
},
{
"name": "NodeWs_api",
"url": "ws://localhost:8080",
"forwardAuthToken": true
}
]
}

Like configured in the “routes” inside the “xs-app.json” file we define the URLs to these services with the appropriate ports.

Execute the following command to install the dependencies inside your "Approuter" directory:
npm install --prefix app/realtimecap-approuter/

Replace the code of your “IdeaOverview.controller.js” file inside your “HTML5Module” with the following code:
sap.ui.define([
"sap/ui/core/mvc/Controller",
"sap/ui/core/ws/WebSocket",
"sap/m/MessageToast"
],
/**
* @param {typeof sap.ui.core.mvc.Controller} Controller
*/
function (Controller, WebSocket, MessageToast) {
"use strict";

return Controller.extend("ns.HTML5Module.controller.IdeaOverview", {
onInit: function () {
var connection = new WebSocket("/NodeWS");
// connection opened
connection.attachOpen(function (oControlEvent) {
console.log(oControlEvent.getParameter("data"));
});

// server messages
connection.attachMessage(function (oControlEvent) {
var oIdea = JSON.parse(oControlEvent.getParameter("data"));
MessageToast.show(JSON.stringify(oIdea));
});

// error handling
connection.attachError(function (oControlEvent) {
console.log(oControlEvent.getParameter("data"));
});

// onConnectionClose
connection.attachClose(function (oControlEvent) {
console.log(oControlEvent.getParameter("data"));
});
}
});
});

This code creates a new WebSocket and opens the connection to the WebSocketServer. This via the "/NodeWS" path, earlier configured in the "Approuter" its "xs-app.json" file. It includes an "onmessage", "onError" and "onClose" listener as well.

Start your “Approuter” (npm run start in Approuter directory) and “Service” (cds watch in root directory). Next you create and execute the following “createIdea.http” file from your projects root-directory to create an Idea:
touch createIdea.http

Add the following request to it:
POST http://localhost:4004/catalog-idea/Ideas HTTP/1.1
Content-Type: application/json

{
"name": "BBQ",
"description": "With a lot of good food!"
}

You will see the "Idea" has been created successfully:

As you can see the “MessageToast” shows the created “Idea” data.

Duplicate the current tab so you have the app opened twice. You can create/add another idea and you will see that both apps receive the newly created "Idea".

Once received you could put them in a JSON-Model and display them in the app. On initial load you could also consume your OData-service to display all the current/earlier created ideas. Or you could add an "Idea" via the app and all the clients will be notified via the WebSockets.

Wrap up


In this blog we generated a CAP-project from scratch by executing some commands. We added a WebSocketServer and WebSocketClients to our project. Once an entity inside the CAP OData Service has been created, the result will be passed to every single connected WebSocketClient (the UI5 app). This UI5 app connects to the socket and OData Service via the Approuter, which has the "WebSocket" feature enabled in its configuration file.

I found this an easy way to setup and get started with WebSockets in CAP, and I would be happy to learn more on how you guys would implement it !

Hope this helps to get started easily and quickly with WebSockets in CAP!

Best regards,

Dries Van Vaerenbergh

 
17 Comments
Labels in this area