Skip to Content
Author's profile photo Uri Nizan

Develop a Full-Stack Application for Cloud Foundry Using SAP Web IDE

Note: Not all capabilities described in this blog post are available in SAP Web IDE Full-Stack. Some are still in development and will be available in future releases.

I’d like to help illustrate the new capabilities of the SAP Web IDE, full-stack version to build full stack applications for Cloud Foundry. Here’s how you would develop a simple book store application — the app is simple, but it illustrates many of the powerful new features of SAP Web IDE.

Main Topics

In describing how to build the bookstore app, and I’ll cover the following technical topics:

  • Use SAP Web IDE to develop the entire application.
  • Use HANA CDS views to create the data model for the application.
  • Use HDBTableData to populate the tables with mock data.
  • Use XSJS XSOData to expose the data model as an OData service.
  • Use Node.js and SQL to add a new book to the database.
  • Use Fiori Master-Detail to create the UI for the application.
  • Use the Web IDE Layout Editor to enhance the UI of the application.
  • Test and debug the application in SAP Web IDE.
  • Deploy the finished application to Cloud Foundry.

The Application

The application we’ll build is designed for the owner of a book store who needs to manage the store’s inventory. The application should look something like this:

The app should do the following:

  • List all books by author.
  • In the author details field, show the books from that author. For each book, it should show the title, ISBN, price, and number of copies available in the store.
  • Add a new book to the store’s inventory.

The user should be able to add books and update existing book details, and to update the inventory when a book is sold.

You can download the entire application source-code from GitHub.

Step 1: Create MTA Project

We’ll build the app as an MTA project, which enables full-stack development.

Go to File –> New –> Project from Template and select the Multi-Target Application Project template.

  • Project Name: BookStore.

Click Finish.

Step 2: Add a HANA Database module to the MTA project

We’ll need a database for the app, so we’ll add an SAP HANA database module to the project.

Name the module booksdb, and complete the wizard.

Add CDS to the DB module by right-clicking the src folder located under the booksdb module and select New > CDS Artifact. Name it store and click Create.

Now that you have a CDS artifact, add the Author and Book.

Now let’s add some mock-data for testing purposes. Add the following files to the src folder of your HDB module:

File name: authors.hdbtabledata

File content:

{

“format_version”: 1,

“imports”:

[ {

“target_table” : “bookstore.db::store.Author”,

“source_data” : { “data_type” : “CSV”, “file_name” : “bookstore.db::authors.csv”, “has_header” : true },

“import_settings” : { “import_columns” : [“authorId”, “authorName”, “numberOfBooks” ] },

“column_mappings” : {“authorId”: 1, “authorName” : 2, “numberOfBooks” : 3 }

}

]

}

File name: books.hdbtabledata

File content:

{

“format_version”: 1,

“imports”:

[ {

“target_table”: “bookstore.db::store.Book”,

“source_data” : { “data_type” : “CSV”, “file_name” : “bookstore.db::books.csv”, “has_header” : true },

“import_settings” : { “import_columns” : [ “bookId”, “bookName”, “authorId”,”isbn”,”price”,”priceCurrency” ] },

“column_mappings” : { “bookId” : 1, “bookName” : 2, “authorId”: 3, “isbn” : 4, “price”: 5, “priceCurrency” : 6 }

}

]

}

File name: authors.csv

File content:

authorId, authorName, numberOfBooks

1, F. Scott Fitzgerald, 3

2, George Orwell, 2

3, J.D. Salinger, 1

4, Kurt Vonnegut, 4

File name: books.csv

File content:

bookId, bookName, authorId, isbn, price,priceCurrency

110, The Great Gatsby, 1, 0743273567, 11, USD

111, Tender Is the Night, 1, 1853260975, 9, USD

112, This Side of Paradise, 1, 0486289990, 5, USD

120, 1984, 2, 0451524934, 7, USD

121, Animal Farm, 2, 812911612X, 14, USD

130, The Catcher in the Rye, 3, 0316769487, 5, USD

140, Slaughterhouse-Five, 4, 0385333846, 10, USD

141, Cat’s Cradle, 4, 038533348X, 10, USD

142, Breakfast of Champions, 4, 0385334206, 10, USD

143, The Sirens of Titan, 4, 0385333498, 9, USD

File name: authorSequence.hdbsequence

File content:

SEQUENCE “bookstore.db::authorseq”

INCREMENT BY 1

START WITH 1000

MINVALUE 1000

MAXVALUE 1999999999

File name: bookSequence.hdbsequence

File content:

SEQUENCE “bookstore.db::bookseq”

INCREMENT BY 1

START WITH 1000

MINVALUE 1000

MAXVALUE 1999999999

Save your work.

This is what the DB module should look like after you’re done:

Build the DB module.

Step 3: Add a Node.js module to the MTA project

We’ll need a node.js module to expose the data as an OData service.

Add a Node.js module to the project.

  • Module Name: booksjs.
  • Enable XSJS Support: Select this checkbox.

Complete the wizard.

Now add a new folder under the lib folder, and name it xsodata, and then create a new file in this folder as follows:

File name: service.xsodata

File content:

service {

“bookstore.db::store.Author” as “Author” navigates (“AuthorBooks” as “books”);

“bookstore.db::store.Book” as “Book” create using “xsjs:bookCreateMethod.xsjslib::createBook”;

 

“bookstore.db::BestSeller” as “BestSeller” keys generate local “objectId”;

association “AuthorBooks” principal “Author”(“authorId”) multiplicity “1” dependent “Book”(“authorId”) multiplicity “*”;

}

In order to support CRUD operations (Create, Read, Update and Delete), we need to implement an XSJS extension. We do this by creating a new file here: booksjs module > lib > xsjs.

File name: bookCreateMethod.xsjslib

File content:

“use strict”;

 

// get the entry that was sent by the client

function getEntry(afterTableName, connection) {

var query = connection.prepareStatement(“select * from \”” + afterTableName + “\””);

var queryResult = query.executeQuery();

query.close();

var record = null;

while (queryResult.next()) {

record = queryResult;

}

return record;

}

 

function findAuthorByName(authorName, connection) {

// TODO: change LIKE to equal

var query = connection.prepareStatement(“SELECT * FROM \”bookstore.db::store.Author\” WHERE \”authorName\” LIKE” + “‘%” + authorName +

“%'”);

var queryResult = query.executeQuery();

query.close();

while (queryResult.next()) {

return queryResult;

}

 

return null;

}

 

function findAuthorById(authorId, connection) {

// TODO: change LIKE to equal

var query = connection.prepareStatement(“SELECT * FROM \”bookstore.db::store.Author\” WHERE \”authorId\” =” + authorId);

var queryResult = query.executeQuery();

query.close();

while (queryResult.next()) {

return queryResult;

}

 

return null;

}

 

/**

* This function will execute a query for adding a new author to the database

*

* */

function insertNewAuthor(authorName, connection) {

var currentAuthorIdQuery = connection.prepareStatement(“SELECT \”bookstore.db::authorseq\”.NEXTVAL FROM DUMMY”);

var currentAuthorIdQueryResult = currentAuthorIdQuery.executeQuery();

 

var nextAuthorId = 0;

 

while (currentAuthorIdQueryResult.next()) {

nextAuthorId = currentAuthorIdQueryResult.getInt(1);

break;

}

 

var query = connection.prepareStatement(“insert into \”bookstore.db::store.Author\” values(?,?,?,?)”);

query.setInt(1, nextAuthorId);

query.setString(2, authorName);

query.setInt(3, 0);

query.setInt(4, 0);

query.executeUpdate();

query.close();

 

return nextAuthorId;

}

 

/**

* This function will execute a query for adding a new book to the database

* bookId property will be generated via the database sequence

* after adding a new book we need to update the author number of books column

* */

function insertNewBook(bookName, authorId,isbn,price, connection) {

 

var currentBookIdQuery = connection.prepareStatement(“SELECT \”bookstore.db::bookseq\”.NEXTVAL FROM DUMMY”);

var currentBookIdQueryResult = currentBookIdQuery.executeQuery();

 

var nextBookId = 0;

 

while (currentBookIdQueryResult.next()) {

nextBookId = currentBookIdQueryResult.getInt(1);

break;

}

 

var query = connection.prepareStatement(“INSERT INTO \”bookstore.db::store.Book\” VALUES(?,?,?,?,?,?,?)”);

 

query.setInt(1, nextBookId);

query.setInt(2, authorId);

query.setString(3, isbn);

query.setString(4, bookName);

query.setInt(5, price);

query.setString(6,”USD”);

query.setString(7,””);

query.executeUpdate();

query.close();

 

incrementNumberOfBooksForAuthorWithId(authorId, connection);

}

 

function incrementNumberOfBooksForAuthorWithId(authorId, connection) {

// 1. Get the author by id from the database

 

var authorRecord = findAuthorById(authorId, connection);

if (authorRecord) {

var numberOfBooks = authorRecord.getInt(3);

numberOfBooks = numberOfBooks + 1;

var query = connection.prepareStatement(“UPDATE \”bookstore.db::store.Author\” SET \”numberOfBooks\” = ? WHERE \”authorId\” = ?”);

query.setInt(1, numberOfBooks);

query.setInt(2, authorId);

query.executeUpdate();

query.close();

}

}

 

function createBook(param) {

 

 

var entityToCreate = getEntry(param.afterTableName, param.connection);

if (!entityToCreate) {

throw “Invalid entry”;

}

// TODO: Change to more generic solution

var isbn = entityToCreate.getString(3);

var bookName = entityToCreate.getString(4);

var price = entityToCreate.getString(5);

var authorName = entityToCreate.getString(7);

 

if (!authorName || !bookName || !isbn || !price) {

throw “Bad Request”;

}

 

var authorId = -1;

 

var authorRecord = findAuthorByName(authorName, param.connection);

if (!authorRecord) {

authorId = insertNewAuthor(authorName, param.connection);

} else {

authorId = authorRecord.getInt(1);

}

 

insertNewBook(bookName, authorId, isbn,price, param.connection);

}

Run the Node.js application.

Open the Run Console to see the progress.

Click the application URL in the run console to validate it’s running correctly.

The Node application will open a new browser tab.

This is what the node project should look like:

Step 4: Add a UI module to the MTA project

Of course, we’ll want a user interface, so let’s add a SAP Fiori Master-Detail module to the project.

Name the module booksui.

Select the Node.js service as the OData source of the UI module.

In the Template Customization step, select the Author as the Object Collection and books as the Line Item Collection.

Click Finish to generate the UI module.

Run the UI application.

Step 5: Enable adding book records

Now let’s add the ability to add a new book to the database.

Open the Master.view.xml using the SAP Web IDE Layout editor.

Add a button to the master page footer, and give it the following properties:

  • Active Icon: sap-icon://add
  • Icon: sap-icon://add
  • Icon First: True

Add an event handler to the button Press event.

onCreateNewBook: function() {

this._oCreateBookDialog = sap.ui.xmlfragment(“com.sap.demo.view.createBook”, this);

this.getView().addDependent(this._oCreateBookDialog);

var oModel = new sap.ui.model.json.JSONModel({

bookName: “”,

authorName: “”,

isbn: “”,

price: 0,

authorNamePlaceholder: “Enter the author name”,

bookNamePlaceholder: “Enter the book name”,

isbnPlaceholder: “ISBN”,

pricePlaceholder: “Book Price (Numbers only)”,

doneButtonActive: true

});

 

this._oCreateBookDialog.setModel(oModel);

this._oCreateBookDialog.open();

},

onCancelBookCreation: function(oEvent) {

this._oCreateBookDialog.close();

},

onCreateBook: function(oEvent) {

// get the JSON model

var oModel = oEvent.getSource().getModel();

 

var payload = {

“bookId”: 0,

“authorId”: 0,

“isbn” : oModel.oData.isbn,

“bookName”: oModel.oData.bookName,

“authorName”: oModel.oData.authorName,

“price” : oModel.oData.price

};

 

var self = this;

this.getView().getModel().create(“/Book”,payload,{

success: function() {

self.getView().getModel().refresh();

}

});

 

this._oCreateBookDialog.close();

}

});

Add a new fragment to the UI module by creating a new file here: storeui –> resources –> webapp –> view. Call the file createBook.fragment.xml.

File name: createBook.fragment.xml

File content:

<core:FragmentDefinition xmlns=”sap.m” xmlns:core=”sap.ui.core”>

<Dialog title=”New Book”>

<beginButton>

<Button text=”Cancel” press=”onCancelBookCreation”/>

</beginButton>

<endButton>

<Button text=”Done” type=”Emphasized” press=”onCreateBook” enabled=”{/doneButtonActive}”/>

</endButton>

<content>

<VBox width=”100%” direction=”Column” displayInline=”true”>

<items>

<Input width=”100%” value=”{/bookName}” placeholder=”{/bookNamePlaceholder}”/>

<Input width=”100%” value=”{/authorName}” placeholder=”{/authorNamePlaceholder}”/>

<Input width=”100%” value=”{/isbn}” placeholder=”{/isbnPlaceholder}”/>

<Input width=”100%” value=”{/price}” placeholder=”{/pricePlaceholder}”/>

</items>

</VBox>

</content>

</Dialog>

</core:FragmentDefinition>

Run the UI application and test the Add function.

Step 6: Deploy to CF

Build the MTA project.

The result of the MTA build is an MTA archive file (mtar).

Right-click the .mtar file and deploy it to Cloud Foundry.

 

 

For more information please read this great blog post about SAP Web IDE Full-Stack.

 

If you have any questions, ask our community , check out our documentation ,or contact us.

Register here to get the latest news and updates from SAP Web IDE.

Assigned Tags

      9 Comments
      You must be Logged on to comment or reply to a post.
      Author's profile photo Alexander K
      Alexander K

      Hi, Uri .

      Very interesting blog.

      I have some question. How can I transport my SAPUI5 project from SAP HCP Web IDE to Web IDE for Hana. When I doing export and import project, importing project in Web IDE for HANA do not running and run button is disabled. I need to change something?

      Author's profile photo Wolfgang Röckelein
      Wolfgang Röckelein

      IMHO you need a xs-app.json instead of a neo-app.json .

      Author's profile photo Alexander K
      Alexander K

      Thanks for idea, Wolfgang.

      But when I replaced  neo-app.json with xs-app.json nothing changed.

      Run button remains disabled.

      Author's profile photo Claudio Mauri
      Claudio Mauri

      It would be nice to add a spring-boot based Java module in Hana 2.0 tutorials / sample like this one.  It would be interesting show how different languages can cope and work together.

      Author's profile photo Gregor Wolf
      Gregor Wolf

      Hi Uri,

      are the wizards  to add the database, Node.js and UI5 module already available on the SCP Trial Multicloud Web IDE? Or when will they come available?

      Best regards
      Gregor

      Author's profile photo Florian Pfeffer
      Florian Pfeffer

      Hi Gregor,

      I had the same question and I am a little bit disappointed that no support is there for modules (except for HTML5) modules in the multi-cloud version of SAP Web IDE on CP at the moment.

      In post  Announcing General Availability of SAP Web IDE, multi-cloud version there is a little sentence which indicates that.

      Full-stack development for SAP Cloud Platform capability is currently delivered in the SAP HANA version of SAP Web IDE (such as SAP HANA Express Edition); it will become available in the on-cloud version of SAP Web IDE in the short-term.

      Also in the comments of the same post it was confirmed that only HTML5 modules are supported at the moment. Other modules should follow in Q3.

      As said I am little bit disappointed and was confused, because of that post here. I thought, that functionality which is described in that post would be available. But as it seems it is just a teaser for that what will be (maybe) available in Q3.

      Regards,
      Florian

      Author's profile photo Uri Nizan
      Uri Nizan
      Blog Post Author

       

      Full stack development is not yet available in the Web IDE multi-cloud edition.

      We are slowly adding features to the cloud edition.

      Stay tuned, HANA and Java development are coming soon...

      Author's profile photo Alexander K
      Alexander K

      Hi, all.

      When I download the entire application source code from GitHub and try to build him i getting error.

      Can someone faced such a mistake?

      Author's profile photo Jonas Wang
      Jonas Wang

      Hi all,

       

      I am having issue with the node.js odata service. When I start the node.js module, I got the following error.

      Any thoughts on the error?