2024 Jul 30 7:18 AM
This is the final task in the July Developer Challenge - "Reverse APIs". Well done for making it this far! 🎉
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).
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 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 ))
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).
Now you're ready to submit your CANDIDATE service, with this new API endpoint, to the TESTER!
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!
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!
2024 Jul 30 8:14 AM
2024 Jul 30 10:28 AM
Ok Done ( in the sense that the TESTER service says pass ).
2024 Jul 30 3:25 PM
Thanks DJ, was a fun late night playing catchup. Great learning and now have the awesome ngrok in my toolbox.
2024 Jul 30 6:12 PM
My submission for task 11:
OData call:
2024 Jul 30 9:03 PM
2024 Jul 31 1:38 AM
2024 Jul 31 9:15 AM
2024 Jul 31 10:02 AM
Finally got it working with the destructuring assignment, the MDN documentation helped 🙂
Here's my submission -
Thank you.
2024 Jul 31 10:07 AM
.on (..., async({ params: [ { ProductID } ] }) => (...)
aren't you a Beauty Javascript? By the way, i had to rely on db.entities to pull off Products because my northbreezeService, which extends the cds.ApplicationService, raises an error when I try to declare this.on(..., Product, async(req) => ). Not sure why is that, but I'll dive into it!
2024 Aug 01 7:16 AM
Indeed! Dive in and have fun, that's the idea! 👍
2024 Jul 31 11:51 AM
2024 Jul 31 5:11 PM
Some rework, as I find that some of these requirements are a bit too ambiguous. Still not sure that am using destructuring assignment or whether there's more to the "entity?" parameter.
2024 Aug 01 7:15 AM - edited 2024 Aug 01 7:15 AM
I'm not going to be able to comment on whether you're using destructuring assignment if you don't show me the code ... 🙂
Which bits are ambiguous? Your statement itself is a little ... ambiguous 😉
2024 Jul 31 2:58 PM
Hello,
My submission:
I had problems with including the Products entity name in srv.on method and I didn't use this option:
function srv.on (event, entity?, handler: (
req : cds.Request,
next : function
))
2024 Jul 31 3:22 PM
2024 Jul 31 5:32 PM
2024 Aug 01 7:13 AM
Please don't forget task 12, @gphadnis2000 ! 🙂
2024 Aug 01 3:09 PM
2024 Jul 31 5:36 PM
2024 Jul 31 10:50 PM
2024 Aug 01 11:51 PM
Task 11 - Action call is working
Service is not able to reach
2024 Aug 02 1:27 PM
Might be worth checking your service URL
2024 Aug 02 3:30 PM
Hi DJ
Service seems fine
Still test service seems to fail for me. It worked also for other unbound functions in other tasks
2024 Aug 02 2:22 PM
Final assignement finished 🎉
Thanks for the Challenges!
2024 Aug 05 6:06 PM
Hi @qmacro ,
I am able to test the bound action locally.
However, when I push the app and test it, I am getting below error.
I didn't faced this issue before. I would appreciate any hint on how to fix this?