Application Development Discussions
Join the discussions or start your own on all things application development, including tools and APIs, programming models, and keeping your skills sharp.
cancel
Showing results for 
Search instead for 
Did you mean: 

SAP Developer Challenge - APIs - Task 3 - Have a Northbreeze product selected for you

qmacro
Developer Advocate
Developer Advocate
19,307

(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.

Background

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:

  • it's an unbound action
  • it expects a single parameter communityid
  • it returns a result

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

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')

Hints and tips

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.

For discussion

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!

149 REPLIES 149

3,022

Wild guess or inner experience from years of learning, experimentation & practice. They're very strongly related 🙂 Nice one.

2,999

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! 🎉

2,754

Hi @vladimirs_semikins, thanks for the quick tip to enable "Encode URL automatically" in Postman.
After escaping ' got the hash value.🙌

3,072

Thank you, this worked for me as well.

0 Kudos
3,346

My product has also an '. I am not sure if the program likes the %27... Interesting

ADR
Participant
0 Kudos
3,405

a1abe56064d700523c9b7f5bf9affb304b631c4c6919955b7da08fcb7a153497

ADR
Participant
3,389

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

RaulVega
Participant
0 Kudos
3,383

a5789ab0a1bdff561d16a3d62d690fe86b7aa605ea494a92416cf7ff9fbcc73b

Frank_Haschick
Explorer
0 Kudos
3,348

e531f6f00f8e3bb5088d8e2b1c69f177683199a7f94502926dede62f747637b5

mvaibhav
Contributor
0 Kudos
3,306

ce7e5bfd3ee599ab815a2d0a94d6e8fd83bf13669b7105ac24f32c41335f3132

SebastianSchuck
Active Participant
0 Kudos
3,313

44644c6eabcebce123b34e1cd30458bdfc7ec498320545f18fbf658654ec4df0

Petchimuthu_M
Associate
Associate
0 Kudos
3,184

1a61847d8ce7cf02dd8b1e65e9c4ccbb78fa6aeadd934b05f6119bb986b14563

stickman_0x00
Participant
0 Kudos
3,147

9a0ca0d758bea602f97d105a33933958657a37902e7128f514689141dc2f6481

Ritik
Participant
0 Kudos
3,143

02dae22fa7566e8d554d243cee715dff85d5ffd5bcc2870064f603a1e155c772

dhegde
Participant
3,133
  • We can loosely compare bound and unbound actions to instance and static methods in a class.
  • Bound actions also get/need keys of the entity type as an importing parameter along with any other parameters explicitly defined (I know in RAP, not sure about CAP though) where as unbound actions only get the parameters that are explicitly defined and no keys.

qmacro
Developer Advocate
Developer Advocate
0 Kudos
2,821

I like and agree with that loose comparison with aspects of OO. Nice one @dhegde !

bztoy
Participant
0 Kudos
3,137

f9df5a30142243f1161863e6b7ea3988311ddf4724c352999a19b83962ea4c02

3,132

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

qmacro
Developer Advocate
Developer Advocate
2,974

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:

screenshot 2023-08-11 at 09.43.18.png

You can see that the action definition is separate from (not within the definition of) any entity. So it's unbound.

2,969

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

sandeepmalhotra
Participant
0 Kudos
3,136

320a2af4156f8fce1902ab90fb5f0b41187d0141e72496e2319ceeafdaed5884

dhegde
Participant
0 Kudos
3,135

fde8091455af6a1f81c3aeed9753b39b028bea7fdfd77d61eb80de99e81b646d

shotokka
Explorer
0 Kudos
3,127

83359110b4f21e9849afeeda2bb58a5f340bfead3d22e94ea1fc500a5c0f39a1

geek
Participant
0 Kudos
3,109

7ee4fc1c5e339a694fab036451f5e4f299290392db9c4b902a29e99748519fc8

emiliocampo
Explorer
0 Kudos
3,107

d9e6b996b44fbe4ac7aca416b88e64d8ec5e6e720bd5a279e73cb8b81b020e3e

pedropeixoto
Discoverer
0 Kudos
3,097

97abad7fadf79bd78ceba1d14307d6041677aaa6915eea32adced21cf61b7482

berserk
Explorer
0 Kudos
3,093

1ff8badef6dc189e7dd359bbc9267595dacb0b3f8d9951001aee8eeadf593f3b

ajay_soreng
Developer Advocate
Developer Advocate
0 Kudos
3,091

8e9ec51324b326a27d8faf79a516a551a346f5c87b4973f9063178cafb19e903

kasch-code
Participant
0 Kudos
3,083

b9284a64d78db031688976c941fe0bfe6caf7c425bc123447f45358e4c554bfa

former_member156175
Discoverer
0 Kudos
3,073

ada2bc7e9d06596bf5b4f9f7815b5ffc3af0cda4cde125c80e1677f58092759f

eshrinivasan
Developer Advocate
Developer Advocate
0 Kudos
3,075

a699accf7fc036352a7bd5c2ad0531275f4dc8c2aa6f25f65d722323d8f1e1d6

AAncos
Participant
0 Kudos
3,070

59bfd759f388d468db3c2a09ad53a4b32357177a864c21abe8b339a7de314a76

Naguco
Explorer
0 Kudos
3,064

2443b793da047a2638b96a187a1c8b06fa9d6805cb6d90a2d0ee1a80448b1865

ajos
Explorer
0 Kudos
3,055

092118cdbe783ddcbc1172de1c84f1e4a6c9ae5f45758bc43b9544b2218201d9

Chaitanyat
Participant
0 Kudos
3,056

608e1a87c0a31a9dfef912689d707c8cbe47eb48171485c3c60178bf113af747

Ruthiel
Product and Topic Expert
Product and Topic Expert
0 Kudos
3,050

c331f4cfc70b493edc34c455d910a0f38d7eb2cc922b13f2ca5cdf4711e9191b

nilsb
Explorer
0 Kudos
3,053

1c17d139f8f68dcb4b4dcbabaabb1a308a0f5b7d1e4e182c786a0bae838250fa

andyl
Explorer
0 Kudos
3,051

4041a8b6e848f2e14ad2a9504f1717c452e792e4fa728ddf1dbeb4c39fcb7b96

aslan1906
Participant
0 Kudos
3,049

77e50b260945be599024f5080af77233558af902ba49b86aa694eb0f87236319

govardhansahil
Explorer
0 Kudos
3,017

712846394e4ddcbebcba38513d28f24f5f87863f75351b069ce854d51a9f145c