2023 Aug 09 6:21 AM - edited 2023 Aug 11 3:21 PM
(Check out the SAP Developer Challenge - APIs blog post for everything you need to know about the challenge to which this task relates!)
In this task you'll learn a bit about actions and functions in OData V4.
While OData V2 did have function imports, the advent of OData V4 brought about enhancements in this area and we now have a clean distinction between different types of orthogonal endpoints that can be presented in an OData service alongside the entity sets, singletons and more. See the Actions and functions sections of the OData V4 and SAP Cloud Application Programming Model talk slides for more info.
With CAP's support for OData V4's actions and functions ready for us to enjoy, it's a good opportunity to try things out here.
In addition to the entity sets we saw in the previous task, the Northbreeze OData V4 service also sports an action. If you check the service's metadata document document you'll see the evidence of this action.
First, it appears alongside the EntitySet elements within the EntityContainer element:
<EntityContainer Name="EntityContainer"> <EntitySet Name="Products" EntityType="Northbreeze.Products"> <NavigationPropertyBinding Path="Category" Target="Categories"/> <NavigationPropertyBinding Path="Supplier" Target="Suppliers"/> </EntitySet> <EntitySet Name="Suppliers" EntityType="Northbreeze.Suppliers"> <NavigationPropertyBinding Path="Products" Target="Products"/> </EntitySet> <EntitySet Name="Categories" EntityType="Northbreeze.Categories"> <NavigationPropertyBinding Path="Products" Target="Products"/> </EntitySet> <EntitySet Name="Summary_of_Sales_by_Years" EntityType="Northbreeze.Summary_of_Sales_by_Years"/> <EntitySet Name="TotalProducts" EntityType="Northbreeze.TotalProducts"/> <ActionImport Name="selectProduct" Action="Northbreeze.selectProduct"/> </EntityContainer>
The action itself is described in more detail along with the EntityType elements. Here it is in full:
<Action Name="selectProduct" IsBound="false"> <Parameter Name="communityid" Type="Edm.String"/> <ReturnType Type="Edm.String"/> </Action>
You can see from this detail that:
Basically, this action expects to receive your SAP Community ID as input, and, based on that ID, will choose a product for you.
Actions vs functions: One of the key differences between actions and functions in OData V4 is that while functions may not have side effects, actions may well have side effects. This means that normally functions will be used for read-only operations, while actions can be used for operations that make some sort of change. But they don't have to. And here, we're using an action, rather than a function, mostly because actions require the POST HTTP method (because they may have side effects), rather than GET. And requiring you to use HTTP POST for this task makes things more interesting.
Your task is to call this unbound action, supplying your SAP Community ID for the communityid parameter. The response you will receive is a JSON representation, containing a value property, the value for which is the product that has been selected for you (based on your SAP Community ID).
This is the value you must hash and then post that hash as a new reply to this discussion thread, as described in Task 0 - Learn to share your task results.
Here's an example. Calling the action with qmacro as the value for communityid causes this to be returned in response (pretty printed for readability):
{ "@odata.context": "$metadata#Edm.String", "value": "Rössle Sauerkraut" }
The product name Rössle Sauerkraut is what needs to be hashed. Note, in this case, that any value passed in the URL path must be properly URL encoded. So while the value test, for example, needs no specific URL encoding, this value must be URL encoded, in order to be successfully transported in the URL path and accurately received and parsed by the hash service. In other words, the value:
Rössle Sauerkraut
needs to be URL encoded so it looks like this:
R%C3%B6ssle%20Sauerkraut
When inserted into the URL path, it will therefore look like this:
/v1/hash(value='R%C3%B6ssle%20Sauerkraut')
You may want to peruse the content of the talk OData V4 and SAP Cloud Application Programming Model, in particular the short Actions and functions section.
You may also wish to watch the replay of the Hands-on SAP Dev live stream episode Back to basics: OData - the Open Data Protocol - Part 5 - Actions & functions.
You can find out what URL encoding is all about in the Wikipedia entry for URL encoding. You're likely to find either a built-in or a library function in your favorite language to do that, for example JavaScript has encodeURIComponent and Python has a quote function in urllib.parse.
Finally, you may wish to read this short post OData query operations and URL encoding the system query options with curl, written in part as a response to a great comment on the previous task.
What's the difference between the meanings of "bound" and "unbound" in this context? What does the fact that the selectProduct action is "unbound" suggest to us?
Remember, if you're posting your thoughts on these discussion questions, remember to do it in a reply separate from your hash reply!
2023 Aug 10 1:38 PM
Wild guess or inner experience from years of learning, experimentation & practice. They're very strongly related 🙂 Nice one.
2023 Aug 10 4:31 PM
Thank you so much guys!!!!!!! Highly appreciate the explanation (even more if you are on vacation today) @vladimirs_semikins, @qmacro. I managed to do this task! 🎉
2023 Aug 27 4:22 PM
Hi @vladimirs_semikins, thanks for the quick tip to enable "Encode URL automatically" in Postman.
After escaping ' got the hash value.🙌
2023 Aug 10 12:09 PM
2023 Aug 09 7:06 PM
2023 Aug 09 3:50 PM
2023 Aug 09 3:52 PM
Discussion point:
Bound actions are linked to the entities and acts on those specific entities. Unbound actions are applicable to the entire service.
Regards
Anupam
2023 Aug 09 4:02 PM
2023 Aug 09 4:03 PM
2023 Aug 09 4:22 PM
2023 Aug 09 4:22 PM
2023 Aug 09 4:32 PM
2023 Aug 09 4:35 PM
2023 Aug 09 7:41 PM
2023 Aug 10 1:54 AM
2023 Aug 23 7:16 AM
2023 Aug 10 2:50 AM
2023 Aug 10 3:46 AM - edited 2023 Aug 10 5:01 AM
what I did for this task:
re-watch this back-to-basic OData hands-on session.
for JavaScript/TypeScript program, it is quite similar to the previous task.
- using fetch command to call a HTTP POST the selectProduct function.
- encode the product get back from the API with function encodeURIComponent.
- use fetch to submit hash value again.
Next, I use bash, curl and jq as a combination to do the job.
- create a bash script ua-selectproduct.sh to call action selectProduct with curl. this script takes no parameter (hard code my community ID) and print out a JSON to stdout.
- create a bash script to submit hash value with curl, this script takes 1 parameter as a string of product name (copied from b2b OData repo) then print the response out to the stdout.
- I had a lot of fun last night with the following chain of Linux commands.
bin/ua-selectproduct.sh | jq -r .value | awk '{printf $0}' | jq -sRr @uri | xargs -I{} bin/submithash.sh {}
what I have learned from the research are
- I understand more about the difference between the user input and stdin in bash shell and I learn how to use command xargs for the first time.
- jq can be used to encode the URL too 😀
btw, it could be done within just 1 bash script but I still don't understand why I have done this. 😅
then about the bound and unbound action, after re-watched the above hands-on again. the conclusion part of the video has a grate summary about this, as dhegde explained in the above comment.
what I want to add here is, I also realized that CAP can help us to understand these in an easy way.
If my understanding is correct (ha), the position of declarations of those actions and function both in the service definition (main.cds) and service implementation (main.js) describe how they work and interact with the entity quite clear.
Thanks and looking forward to the next task.
Wises
2023 Aug 11 9:44 AM
Absolutely great to see, @bztoy , thanks for sharing your workflow and process. It's lovely to see folks experiment with old and new UNIX commands (and the pipeline itself) - such a powerful toolkit for the modern developer.
I'm curious about the pipeline above: can you give us an example of what data is emitted from bin/ua-selectproduct.sh above, so we can trace its flow along the pipeline?
And yes, I was wondering whether to mention the fact that jq has some awesome filters and formatters; I use @csv and @tsv a lot, but @uri is also pretty darn useful!
And yes, you have some good observations about the CDS definitions. In fact, let me share what that looks like:
You can see that the action definition is separate from (not within the definition of) any entity. So it's unbound.
2023 Aug 11 10:51 AM
of Couse, yes. the product I got is
{"@odata.context":"$metadata#Edm.String","value":"Nord-Ost Matjeshering"}
if someone who interested, you can run the following statement in any terminal with bash to see the output (encoded string).
echo '{"@odata.context":"$metadata#Edm.String","value":"Nord-Ost Matjeshering"}' | jq -r .value | awk '{printf $0}' | jq -sRr @uri
it was quite simple because there was just 1 character that has to be encoded. 😀
and many thanks for the explanation about bound/onbound, it's very easy to understand.
Wises
2023 Aug 10 4:32 AM
2023 Aug 10 4:39 AM
2023 Aug 10 6:35 AM - edited 2023 Aug 10 12:17 PM
2023 Aug 10 9:32 AM
2023 Aug 10 10:15 AM
2023 Aug 10 11:43 AM
2023 Aug 10 12:09 PM
2023 Aug 10 12:24 PM
2023 Aug 10 12:51 PM
2023 Aug 10 2:04 PM
2023 Aug 10 3:05 PM
2023 Aug 10 3:59 PM
2023 Aug 10 4:32 PM
2023 Aug 10 7:28 PM
2023 Aug 10 8:33 PM
2023 Aug 10 9:58 PM
2023 Aug 10 10:19 PM
2023 Aug 10 11:00 PM
2023 Aug 10 11:08 PM
2023 Aug 11 9:09 AM