Skip to Content
Event Information

Annotated links: Episode 11 of Hands-on SAP dev with qmacro

This is a searchable description of the content of a live stream recording, specifically “Episode 11 – A CAP project for “Geonames” & funcprog style with Chris Whealy” 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, titled “A CAP project for “Geonames” & funcprog style with Chris Whealy“, was streamed live on Fri 08 Mar 2019 and is approximately one hour in length. The stream recording is available on YouTube.

Below is a brief synopsis, and links to specific highlights – use these links to jump directly to particular places of interest in the recording, based on ‘hh:mm:ss’ style timestamps.

Brief synopsis

In this episode we have developer Chris Whealy as a guest, taking us through his personal project making use of Geonames data and built using CAP with Node.js. Along the way we look at Chris’s programming style which takes a lot from the functional programming world.

00:01:30: Good morning to Chris Whealy, our special guest for this episode! The project we’re going to take a look at with Chris is available on GitHub – see the geonames_hana repository.q

00:03:20: Starting out by looking at the tweet on 5th March showing the functional “tick” that I learned from Chris, specifically:

data.results.reduce((a, x) => (_ => a)(a[x.Id] = x.Name), {})

00:05:25: Chris explains in what circumstances such a construct can be used, and why it’s useful (especially referring to the fact that some standard JavaScript array functions such as Array.prototype.push don’t return the array as a result, which is, on the whole, less than helpful, as we can’t chain functions together).

00:08:20: Reminding ourselves that the Array.prototype.reduce function can turn an array into any shape, either another array, a map (an object) or even a scalar.

00:09:40: While map and reduce are relatively well known, functional programming is a lot more than just those two functions. Also, map is just a special case of reduce.

00:11:10: In other languages, reduce is known as fold (and there’s a fold left and a fold right, by the way).

00:12:00: Chris starts to share his screen to introduce the project and start taking us through some of the codebase.

00:13:02: The data is taken from the website geonames.org which is open and crowdsourced, updated daily.

00:15:58: Chris takes us through a few of the REST API calls to see some of the data that’s stored and served by the service, including modifying the requests with values in query parameters in the URL.

00:21:35: Switching to the GitHub project to start to take a look at the code behind the service. We notice straight away the now-familiar db and srv directories therein, this being a CAP project.

00:22:20: Chris points out that he built the project using the SAP Web IDE, mostly because he specifically wanted to use HANA for the persistence layer immediately.

00:24:00: Taking a first look at some of the annotations, and also specifically at the index.cds and the role that plays in a CAP project. If found by the CDS compiler, that index.cds file, and only that index.cds file will be opened and its contents (including references to other files) compiled. Otherwise any and all files with a cds extension will be opened and compiled. This way you can manage how the data model gets compiled.

00:26:00: Chris highlights that once the HDI container is created, he can interact with it directly, in JavaScript, using the @sap/cds module. We saw some of this in Ep.4 – Debugging CAPM and a look at the cds REPL.

00:26:40: It’s the srv/server.js script that responds to the HTTP requests that we’ve already seen when exploring the frontend (via the HTML index) and the API calls.

00:27:53: Chris explains that instead of using the normal express based HTTP server that comes set up with any new CAP project, he’s using his own HTTP server based on the lower-level http module.

00:29:05: Relaying a message from Former Member who has spotted a vue/ directory in the project, which has piqued his (and others’) interest! This is the next part of the project that Chris intends to work on.

00:30:38: Starting to look at some of the JS in the srv/server.js file, specifically the genApiHandler function, which has a certain style and has a noticeable lack of curly braces. Chris tries to write every function as a single expression, holding to the principle that a function should do one thing and one thing only.

00:32:25: Noticing that the initial lines of the genApiHandler definition is somewhat familiar to us, in that it is a function that returns a function, enabling partial application. Chris also talks about how he tries to be consistent with variable names.

00:35:05: Chris explains how he has to build something on the server side that services API calls with paths like this:

/api/v1/geonames

00:36:25: Chris describes the function as giving back a function, i.e. it is a partial function, in that the complete evaluation of the arguments doesn’t happen necessarily all at once.

00:38:05: Looking at the config that encapsulates the different possible paths in the API.

00:39:50: Essentially, the genApiHandler function is abstraction across every object in the config, and returns a function that is immediately invoked, i.e. an immediately invoked function expression (IIFE).

00:41:05: Chris explains one of the challenges with the traditional “for loop”, in that you have to keep track of how many times the loop has executed. Very low level, using things that are changing. Functional programming is in many ways all about moving from such a “how” to a “what” approach to describing what you want.

00:45:30: Looking specifically at how request handlers are created for each of the possible API URLs, with this bit of code in srv/server.js:

Object
  .keys(config.urls)
  .map(assignRequestHandler)

00:47:25: Considering that we might be in a “strange land” somewhere between programming and configuration, using higher order functions (functions that receive and / or produce functions) as solid-state building blocks, upon which basis Chris likens his creation to a functional finite state machine.

00:48:50: Noticing the use of a ternary operator (of which Ronnie is fond, in fact there’s a nested ternary operator in there too. Chris points out that one of the advantages of these ternary expressions is using the alternative, an if statement, introduces something that is not an expression (it is a statement).

00:51:50: Jumping to a different place now in srv/server.js to look at some usage of the CDS API, which starts with:

cds.connect(connectionObj)

00:52:58: We look at the contents of the connectionObj which in fact looks like this:

const connectionObject = {
  "kind": "hana",
  "model": "gen/csn.json",
  "credentials": (vcapSrv['hana'] || vcapSrv['hanatrial'])[0].credentials

and specifically focus on the value for the “credentials” property, which is using the “or” operator (||) in place of an if ... else expression. Again, a functional approach which is as easy to read as it is concise.

00:54:00: Examining the promise chain of .then calls after the CDS connection (the cds.connect function actually returns a promise); much preferable than using a callback approach.

00:55:13: Then we look briefly at the next CDS API call, which is this:

cds.run('SELECT * FROM ORG_GEONAMES_BASE_GEO_COUNTRIES').catch(console.error))

and this in turn returns a promise, and so on.

00:57:30: I note that in a couple of our previous episodes we see another promise chain in action, specifically in Ep.9 and Ep.10, in our data retrieval script.

Be the first to leave a comment
You must be Logged on to comment or reply to a post.