Technology Blogs by SAP
Learn how to extend and personalize SAP applications. Follow the SAP technology blog for insights into SAP BTP, ABAP, SAP Analytics Cloud, SAP HANA, and more.
Showing results for 
Search instead for 
Did you mean: 
Developer Advocate
Developer Advocate
This is a searchable description of the content of a live stream recording, specifically "Ep.49 – Defining our first Business Rules project" in the "Hands-on SAP dev with qmacro" series. There are links directly to specific highlights in the video recording. For links to annotations of other episodes, please see the "Catch the replays" section of the series blog post.

This episode was streamed live on Fri 03 Jan 2020 and is approximately 65 minutes in length. The stream recording is available on YouTube.

Brief synopsis: Now we have the admin “Manage Projects” UI (and understand how the magic works) it’s time to create our first Business Rules project.

00:00:20 The first live stream of 2020, and the first live stream on YouTube rather than Twitter. And the FIFTIETH so far. Exciting!

00:03:00 I've been tweaking my YouTube channel to fill it out with hopefully useful information - check it out at and of course please don't forget to SUBSCRIBE, and you can set reminders for upcoming live streams too.

00:05:35 Tshirt giveaway! In a previous live stream jbm1991 mentioned that his UI5 tshirt was getting pretty old, and that prompted me to dig out some tshirts that I can give away. I have the following (all in large):

  • A custom ">The Future Is Terminal_" tshirt (that I got printed via Spreadshirt)

  • A semi-vintage UI5 tshirt with the Phoenix logo

  • An SAP Cloud Application Programming Model tshirt with the logo on it too

  • An SAP CodeJam Wroclaw tshirt from when I was over there giving a CodeJam

I'll draw the winner of the first tshirt on the next live stream - if you would like to be considered in that draw, please add a comment saying so, to the recording of this Ep.49live stream on my channel.

00:09:45 My son Joseph and I have been working on a Raspberry Pi based cluster over the holiday period, which I showed to the camera. All powered via power-over-ethernet (PoE) via additional PoE HATs. Right now I'm controlling them remotely via Ansible. The intention is to build a Kubernetes cluster with them (one primary and three secondary nodes). We learned everything we needed to know from Jeff Geerling and his Pi Dramble project.

As an aside, I learned that "Bramble" is the name given to a cluster of Raspberry Pis (and I guess "Dramble" = "Drupal" + "Bramble"), and in Douglas Adams's honour, I decided to set the hostnames of my our Raspberry Pis to "brambleweeny1", "brambleweeny2", "brambleweeny3" and "brambleweeny4", in reference to the "Bambleweeny 57 Sub-Meson Brain" in Hitch Hiker's Guide To The Galaxy.:

"The Infinite Improbability Drive is a wonderful new method of crossing interstellar distances in a few seconds, without all that tedious mucking about in hyperspace. The principle of generating small amounts of finite probability by simply hooking the logic circuits of a Bambleweeny Fifty-Seven Sub-Meson Brain to an atomic vector plotter suspended in a strong Brownian Motion producer - say a nice hot cup of tea - were, of course, well understood."

00:12:00 Note that the "upcoming" section of the canonical Hands-on SAP dev with qmacro blog post, whichyou can always get here (this link including the "upcoming" reference):, now has a "Live Stream Link" which will take you directly to the upcoming live stream video link on YouTube, for which you can set a reminder. If you want to find out how to set reminders, have a look at this 30 second video I recorded yesterday: Subscribing and setting reminders for upcoming HandsOnSAPDev live stream episodes.

00:15:05 Having a quick look, via the annotation blog post for Ep.48, where we left off with the Business Rules service on SAP Cloud Platform Cloud Foundry environment.

00:20:10 Opening up my SAP Cloud Platform cockpit, and navigating to the apps and services running already in my CF trial subaccount, specifically in the DEV space. There's a single app, which is the Business Rules Editor app, and three service instances upon which the app relies (the Business Rules service itself, a UAA service instance and an instance of the HTML5 repo service supplying the app artifacts themselves).

00:25:00 Starting to put together our first Business Rules project, which will be what we have seen in previous episodes, i.e. a very simple "Flight Discount" project that gives discounts for given airlines.

00:25:30 I'd noticed by accident that there is a reference to the Business Rules service on SAP Cloud Platform ...over on the Cloud Foundry website! Unfortunately the link there takes you to a "not found" page in the SAP Help Portal. A partial success, I guess 😕 (I've informed the team).

00:26:00 We start to build out the simple project by creating and activating the artifacts, over the next 20 mins or so. Here's a quick summary of the artifacts we created, in some sort of structure -- that makes sense to me, anyway! (Note that we forgot to associate the ruleset with the ruleservice, so I've just done that now.)
            Project: airlinediscounts
+-------->> Dataobject: flightinfo
| |
| +-- Attribute: carrier (String)
| +---->> Dataobject: discountinfo <<-------------------+
| | | |
| | +-- Attribute: discount (Number) |
| | |
| | +->> Rule: airlinediscount |
| | | | |
| | | +-- Result: discountinfo ----------------+
| | |
| | | Ruleset: discountruleset
| | | |
| | +----------- +-- Rule: airlinediscount
| | |
| | +-- Rule Service: airlinediscount
| |
| | Ruleservice: discountruleservice
| | |
| +----------------- +-- Vocabulary (Result): discountinfo
| |
+--------------------- +-- Vocabulary (Input): flightinfo

00:44:50 Now we have this simple Business Rules project set up, we go over to the API Business Hub to check out the Business Rules service API collection, which includes a three-way combination of offerings, based on environment (CF and Neo), target (designtime / rule authoring and runtime / rule execution) and version (the runtime API set has a deprecated version 1 for both Neo and CF environments).

00:51:05 Having reviewed the different areas of the Rule Authoring API for Cloud Foundry, such as Export/Import, Projects, Data Objects and so on, we pick a simple Projects based endpoint to get basic information for projects:
GET /v1/projects

00:51:30 We look at my "EU10" environment configuration to understand how things work from an authentication perspective, noting that unlike in the Neo environment where we had been able to use HTTP Basic Authentication, we must embrace OAuth 2.0. So first of all thank goodness for the API Business Hub's "environments" feature, which does a lot of magic for us. But of course while we like magic, we want to know what's going on, and that's what we endeavour to find out next. We note first that there are two sections to the environment definition:

  1. the endpoint definition (the root URL of what we'll be using in the actual API calls). My endpoint is

  2. the OAuth 2.0 authentication detail, consisting of the Client Id, Secret, and Token URL.

All three parts of the authentication details are obtained from the service key data that we created in a previous episode, which we briefly review at this point too, to match up what we have there with what we have in this environment definition.

What the environment magic does when we want to make a call is first obtain an access token with this endpoint information, and then that access token, one retrieved, is used in the actual API calls for bearer token based authentication. This is described briefly but very nicely by archana.shukla in Step VII "Test Business Rules" of her blog post Quick Start Guide to SAP Cloud Platform Business Rules Service in Cloud Foundry.

00:54:00 With that explanation out of the way, we hit the API endpoint /v1/projects, in other words the URL, using the magic of the API Business Hub and get a response, which is a list containing a single project, like this, and that project data is what we're looking for. Nice!

"Id": "94ad349cbeec476ea259354805b35520",
"Name": "airlinediscounts",
"Description": [
"Language": "en",
"Text": "airlinediscounts"
"Language": "",
"Text": "airlinediscounts"
"ChangedOn": "2020-01-03T08:42:50.824Z",
"ChangedBy": "",
"Annotations": [],
"Version": {
"Id": "000000000000000000",
"Revision": "000000000000000000",
"Description": "Initial",
"ChangedOn": "2020-01-03T08:27:17.096Z",
"ChangedBy": "",
"ParentId": "000000000000000000"

00:59:50 With time pressing, we take one more step, to understand even better what is going on under the hood, in the magic that's supplied by the API Business Hub. And that of course is terminal based, because as we all know, "the future is terminal", right? 🙂

Using curl and .netrc we set up an HTTP request to ask for an access token, which is the first step in being able to make an actual API call, as described just above.

The call ended up looking like this, split into separate lines for better readability:
curl \
--verbose \
-n \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=password&response_type=token&$PASSWORD" \ > token.json

Going down the list of switches, we have:

  • --verbose: show more info!

  • -n: use .netrc

  • -X: use the following HTTP method

  • -H: include this header in the request

  • -d: send this data as payload

(Note that my password is not shown - I'd deliberately stored it in an environment variable $PASSWORD)

We captured the response payload into the file token.json to have a look at it, first in Vim, and then using the wonderful jq:
-> jq < token.json
"access_token": "eyJhbGciOiJSUzI1NiIsImprdSI6Imh0dHBzOi8vaTM0NzQ5MXRyaWFsLmF1dGhlbnRpY2F0aW9uLmV1MTAuaGFuYS5vbmRlbWFuZC5jb20vdG9rZW5fa2V5cyIsImtpZCI6ImtleS1pZC0xIiwidHlwIjoiSldUIn0.eyJqdGkiOiI0NGU4MDllNWRmNDk0NjAyYTYwZmIyOTNkMjBkNGZiMSIsImV4dF9hdHRyIjp7ImVuaGFuY2VyIjoiWFNVQUEiLCJ6ZG4iOiJpMzQ3NDkxdHJpYWwiLCJzZXJ2aWNlaW5zdGFuY2VpZCI6ImE0ZDJmMTU4LTRlMzgtNGY5Yi05OTc5LTkzNTExYzE2ZWQ4MSJ9LCJ4cy5zeXN0ZW0uYXR0cmlidXRlcyI6eyJ4cy5yb2xlY29sbGVjdGlvbnMiOlsiYnVzaW5lc3MtcnVsZXMiXX0sImdpdmVuX25hbWUiOiJESiIsInhzLnVzZXIuYXR0cmlidXRlcyI6e30sImZhbWlseV9uYW1lIjoiQWRhbXMiLCJzdWIiOiI4NTY5NzU0ZS0yYTZjLTQzZWYtYTdiYS01ZjIxMDM5ZjZlYzUiLCJzY29wZSI6WyJicG1ydWxlcmVwb3NpdG9yeSF0MjQ2Ni5SdWxlUmVwb3NpdG9yeS5TdXBlclVzZXIiLCJicG1ydWxlY29tcGlsZXIhdDI0NjYuUnVsZUNvbXBVyLkphdmFDb21wnBtcnVsZWNvbXBpbGVyIXQyNDY2LlJ1bGVDb21waWxlci5TcWxDb21waWxlIiwib3BlbmlkIiwiYnBtcnVsZXNxbGNvbXBpbGVyIXQyNDY2LlJ1bGVTcWxDb21waWxlci5TcWxDb21waWxlIiwiYnBtcnVsZXJ1bnRpbWUhdDI0NjYuUnVsZVJ1bnRpbWUuU3VwZXJVc2VyIl0sImNsaWVudF9pZCI6InNiLWNsb25lLWE0ZDJmMTU4LTRlMzgtNGY5Yi05OTc5LTkzNTExYzE2ZWQ4MSFiMzAwNjh8YnBtcnVsZWJyb2tlciFiMjQ2NiIsImNpZCI6InNiLWNsb25lLWE0ZDJmMTU4LTRlMzgtNGY5Yi05OTc5LTkzNTExYzE2ZWQ4MSFiMzAwNjh8YnBtcnVsZWJyb2tlciFiMjQ2NiIsImF6cCI6InNiLWNsb25lLWE0ZDJmMTU4LTRlMzgtNGY5Yi05OTc5LTkzNTExYzE2ZWQ4MSFiMzAwNjh8YnBtcnVsZWJyb2tlciFiMjQ2NiIsImdyYW50X3R5cGUiOiJwYXNzd29yZCIsInVzZXJfaWQiOiI4NTY5NzU0ZS0yYTZjLTQzZWYtYTdiYS01ZjIxMDM5ZjZlYzUiLCJvcmlnaW4iOiJsZGFwIiwidXNlcl9uYW1lIjoiZGouYWRhbXNAc2FwLmNvbSIsImVtYWlsIjoiZGouYWRhbXNAc2FwLmNvbSIsImF1dGhfdGltZSI6MTU3ODA0MjI2NSwicmV2X3NpZyI6IjEzYTUzNjAzIiwiaWF0IjoxNTc4MDQyMjY2LCJleHAiOjE1NzgwODU0NjYsImlzcyI6Imh0dHA6Ly9pMzQ3NDkxdHJpYWwubG9jYWxob3N0OjgwODAvdWFhL29hdXRoL3Rva2VuIiwiemlkIjoiYzk4M2VkOWYtNDEwZi00MDdjLTkzZjYtODE2NjA0ZTExMjhjIiwiYXVkIjpbImJwbXJ1bGVjb21waWxlciF0MjQ2Ni5SdWxlQ29tcGlsZXIiLCJvcGVuaWQiLCJicG1ydWxlc3FsY29tcGlsZXIhdDI0NjYuUnVsZVNxbENvbXBpbGVyIiwic2ItY2xvbmUtYTRkMmYxNTgtNGUzOC00ZjliLTk5NzktOTM1MTFjMTZlZDgxIWIzMDA2OHxicG1ydWxlYnJva2VyIWIyNDY2IiwiYnBtcnVsZXJlcG9zaXRvcnkhdDI0NjYuUnVsZVJlcG9zaXRvcnkiLCJicG1ydWxlcnVudGltZSF0MjQ2Ni5SdWxlUnVudGltZSJdfQ.HcZTis_E9Nrn28VbW1Js8_yMRGgFwOfywcsXnj-LluP8I7SKa3n9z7OwdiJxYxrYhBl6tGc9HFCDbfyD3xJ9bfiEHxlGmUR7K7LNyMWjEQExn5joEOrqqzL9oQ9mI3SVQd93taKqgTaSzaoTwlz_g2horzrZOU8D1fuS-69F2aRoGptB228-wG9TnaVV-RFS8XJYjyMoEjCgTyhZyVxuC1h16inF4NvmtT_Q_QA1UGt3g6CyhV_k-wSrc2m1nsmfW2UGlUZ4V4zTxuWJIuEILaqBk1zXX_pO3wUlwE02jTMazI0AdsC1uPa3F6SqoMNwFgx2gx5paGRAraeLeoZJ_VWWwy2Mw8mpvuvHIYaVVZ6rIu-dta8JRtQWcjt_bxTU_SvWm1dwIfOCHORpU99iROLnlhJKGWVhOnQZqpxSMTIYw668Ms1OTpd3Z2DB9xm5nV1TLvJwpcD3yTUnxhpcy6azMoMpEXwquobs2g4u9uHJ5QCDPanFwZWwg75fa7Wq4ahHHEGFaAztNxm4gxAFkt7coHJrk9xLsYvu4gC2akYBLO4k7wy385DgmhWYrFDQgjog-R79SHiHTd0upxqQIM-7gTWtno2Uf5Ve682DNrQz-8AhI5WEGdplj2_YH-tWHdjUUp1Mc7Bmn3yFS-3dxRTB6xtMT56NhuuuAn7S0qo",
"token_type": "bearer",
"id_token": "eyJhbGciOiJSUzI1NiIsImprdSI6Imh0dHBzOi8vaTM0NzQ5MXRyaWFsLmF1dGhlbnRpY2F0aW9uLmV1MTAuaGFuYS5vbmRlbWFuZC5jb20vdG9rZW5fa2V5cyIsImtpZCI6ImtleS1pZC0xIiwidHlwIjoiSldUIn0.eyJzdWIiOiI4NTY5NzU0ZS0yYTZjLTQzZWYtYTdiYS01ZjIxMDM5ZjZlYzUiLCJhdWQiOlsic2ItY2xvbmUtYTRkMmYxNTgtNGUzOC00ZjliLTk5NzktOTM1MTFjMTZlZDgxIWIzMDA2OHxicG1ydWxlYnJva2VyIWIyNDY2Il0sImlzcyI6Imh0dHA6Ly9pMzQ3NDkxdHJpYWwubG9jYWxob3N0OjgwODAvdWFhL29hdXRoL3Rva2VuIiwiZXhwIjoxNTc4MDg1NDY2LCJpYXQiOjE1NzgwNDIyNjYsImFtciI6WyJleHQiLCJwd2QiXSwiYXpwIjoic2ItY2xvbmUtYTRkMmYxNTgtNGUzOC00ZjliLTk5NzktOTM1MTFjMTZlZDgxIWIzMDA2OHxicG1ydWxlYnJva2VyIWIyNDY2Iiwic2NvcGUiOlsib3BlbmlkIl0sImVtYWlsIjoiZGouYWRhbXNAc2FwLmNvbSIsInppZCI6ImM5ODNlZDlmLTQxMGYtNDA3Yy05M2Y2LTgxNjYwNGUxMTI4YyIsIm9yaWdpbiI6ImxkYXAiLRpbWUiOjE1NzgwNDIyNTcyODUsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJjbGllbnRfaWQiOiJzYi1jbG9uZS1hNGQyZjE1OC00ZTM4LTRmOWItOTk3OS05MzUxMWMxNmVkODEhYjMwMDY4fGJwbXJ1bGVicm9rZXIhYjI0NjYiLCJjaWQiOiJzYi1jbG9uZS1hNGQyZjE1OC00ZTM4LTRmOWItOTk3OS05MzUxMWMxNmVkODEhYjMwMDY4fGJwbXJ1bGVicm9rZXIhYjI0NjYiLCJncmFudF90eXBlIjoicGFzc3dvcmQiLCJ1c2VyX25hbWUiOiJkai5hZGFtc0BzYXAuY29tIiwicmV2X3NpZyI6IjEzYTUzNjAzIiwiYXV0aF90aW1lIjoxNTc4MDQyMjY1LCJ1c2VyX2lkIjoiODU2OTc1NGUtMmE2Yy00M2VmLWE3YmEtNWYyMTAzOWY2ZWM1In0.XfjRKJyOWJ3p-HzQ-cr1PSSxp7fBIGXlszBA1D_E7W54anPFynVhdRmD3wuQJ_ubknj_ICH7DN2OJy1IZ5SBktTT_prveT9w47sfSlGlztiUl72SYdMloe07VB3sr63jah86jm0I3SgTyOue-v3dYRanB4CtJIojTe8KKOaboBJ2NmcsVsjQphkIR2Gmc2QxXi23fLY5VQPFgCBw8aiKAggn_5o2oK4LWATm5k688cT97wHsiwHOBJQyTtoiy21T1pOgmxTdI7eIEjFonRyiTzbg7ucbf2Q4QIprP5MZYEXeI-q0SC45DOCXXse5U2C8cfy_kOhyIlTrEECWpq4ixgpRBlwH5SDZLKa4ULrhrMPKZiQDLijD-2Veo0AZfwDkFPhbT71HqAihRo1K927cgC2fMbthzs3WUoBPDCu9sYfzQdGTGoLKfGG6Muqw_lK_z7WJCKfv8PfwOrc3Aol2LCMK9W3aZ- [ ] vo1LofWSgZqAFSnrXtjbSHz8RXGEY9My_vzOnWHZqojPU6g2BNaH6y7qEuok2FZHMGAcUKrnjTtH5u858oKAAc00k9WluXgLIJCBUzLZxIeJpsIf-zWryNXjftWbT9KIMR_jmz9kPvKzdNJAHJQgfLk5DEvuQzfwrnvl4xmHOOhJmf0hay5WaoEcw6XwlaVtFOIayWCMW_E4",
"refresh_token": "7dc0315fcae449c29b48a441137ca723-r",
"expires_in": 43199,
"scope": "bpmrulerepository!t2466.RuleRepository.SuperUser bpmrulecompiler!t2466.RuleCompiler.JavaCompile bpmrulecompiler!t2466.RuleCompiler.SqlCompile openid bpmrulesqlcompiler!t2466.RuleSqlCompiler.SqlCompile bpmruleruntime!t2466.RuleRuntime.SuperUser",
"jti": "44e809e5df494602a60fb293d20d4fb1"

(If you're thinking of trying to use these tokens, don't bother - I've doctored them and you need my password anyway :-))

For those wondering, you can just grab the access token value itself like this:
-> jq --raw-output '.access_token' < token.json

And we'll be using this raw value extraction technique in the next episode, where we specify the value as the bearer token in an API call from the command line. Stay tuned, until the next episode!