Task 11 – Using implicit parameters with a bound function (July Developer Challenge – “Reverse APIs”

This is the final task in the July Developer Challenge – “Reverse APIs”. Well done for making it this far! 🎉

Background

In the tasks in this challenge so far you’ve encountered, amongst other things, actions and functions. But all of those actions and functions you’ve had to define and write implementations for … have been unbound, i.e. not bound to a specific instance of an entity. The Capire section Custom Actions and Functions is relevant here. Think of these types of actions and functions as being at the service level (vaguely like static methods in OO).

With that section there’s a subsection Bound Actions and Functions which describe actions and functions that can be defined to be in the context of an entity (to continue the vague OO analogy, these are like instance methods).

In this task you’ll define a bound function, and (will be encouraged to) use destructuring to determine the implicit binding parameter (this binding parameter can also be modelled explicitly but we won’t be doing that as it’s far less common).

Defining a bound function

Check the Capire docs to learn how to define a bound function, in CDL. It involves declaring an actions block as a continuation of the entity definition to which you want your function bound. Note that despite the block having the name actions, it is for containing definitions of both bound actions and functions.

Implementing a bound function

Implementing a bound action or function is pretty much the same as implementing an unbound one. The only difference really is that you need to get the value of the binding parameter, i.e. the key that the infrastructure gives your handler to point to the specific instance of the entity in the context of which the bound action or function is being called.

Here’s the difference, using the Northbreeze service, served via the OData adapter, at the default path. First, let’s remind ourselves of how an unbound function is called, which is like this – note the absence of any entity name or key:

/odata/v4/northbreeze/unboundFunction()

Now, a bound function is called like this – note the function name follows the path of a specific entity:

/odata/v4/northbreeze/Products(42)/boundFunction()

or (using the OData V4 key-as-segment approach) like this:

/odata/v4/northbreeze/Products/42/boundFunction()

The value 42 is the value of the binding parameter that you need to implement such a bound function. Where is that? It’s available in the incoming request, specifically in the params property.

In order to retrieve the value, you’ll need to grab it from the request object. How you do that is of course up to you, but I’d encourage you to try it in the “normal” way of a dotted path notation from req, for example:

const ID = req.params[0].ProductID

but also using a destructuring assignment in the actual function signature, i.e.

async (<SOME-DESTRUCTURING-ASSIGNMENT-HERE>) => { ... }

Remember also that you’ll probably want to re-check the details of the srv.on request, as you’ll want to include the Products entity name in that incantation in the optional entity? position:

function srv.on (event, entity?, handler: (
  req  : cds.Request,
  next : function
))

The requirements

Here are the specific requirements for this task.

Define a bound function called stockValue on the Products entity in your Northbreeze service. This should return an integer value being the value of the bound product’s stock, which should be calculated as the product’s UnitPrice multiplied by its UnitsInStock. For example, for product Chai:

{
  "@odata.context": "$metadata#Products/$entity",
  "ProductID": 1,
  "ProductName": "Chai",
  "QuantityPerUnit": "10 boxes x 20 bags",
  "UnitPrice": 18,
  "Category_CategoryID": 1,
  "Supplier_SupplierID": 1,
  "UnitsInStock": 39,
  "UnitsOnOrder": 0,
  "ReorderLevel": 10,
  "Discontinued": false
}

the stock value is 702 (18 x 39).

Submitting to the TESTER

Now you’re ready to submit your CANDIDATE service, with this new API endpoint, to the TESTER!

The payload

The task identifier you need to supply in the payload of your submission is: northbreeze-stockValue.

You’ll have already done this sort of thing previously so just head back there for the more detailed instructions if you need them, or to the the section titled “The Tester service, and making a test request” in the main challenge blog post.

You’ll need to submit a JSON payload like this:

{
  "communityid": "<your-community-id>",
  "serviceurl": "<the-URL-of-your-service>",
  "task": "northbreeze-stockValue"
}

And, just as with the previous (and all further tasks):

  • the value for the communityid property should be your ID on this SAP Community platform (e.g. mine is “qmacro”)

  • the value for the serviceurl property should be the absolute URL (i.e. including the scheme), of your CANDIDATE service which contains the API endpoint (see ℹ️ A note on URLs and services).

That’s it!

Logging of test results

Remember that you can check on your progress, and the progress of your fellow participants – all requests are logged and are available in an entity set served by the TESTER service. The entity set URL is https://developer-challenge-2024-07.cfapps.eu10.hana.ondemand.com/tester/Testlog and being an OData V4 entity set, all the normal OData system query options are available to you for digging into that information.

If you have any questions or comments, leave them below!

Scroll to Top