2024 Jul 15 7:04 AM - edited 2024 Jul 20 8:31 AM
This is a task in the July Developer Challenge - "Reverse APIs".
In this task, you'll create a new, second service, and within that you'll add a single API endpoint. The difference to the previous service and endpoints is that here a plain "REST" protocol is required.
What does that mean, exactly? Well, you may know that CAP's design is wonderfully modular, and agnostic as well as opinionated. I'm sure you will also know that if you define a service in your CDS model and serve it with the CAP server, you'll basically have an OData service. Standing up and serving OData services was the original killer app for the SAP Cloud Application Programming Model. It's hard now to remember how much effort it was, before CAP came along, to create and serve an OData service - and there was even more effort, much more, in fact, to facilitate all Create, Read, Update, Delete & Query (CRUD+Q) operations for the data model you had defined. We take it for granted that with CAP we can spin up an OData service with fully functional support for all standard CRUD+Q operations in a couple of minutes.
When talking about OData, I refer specifically to OData V4. This is for two reasons: CAP's default for OData is also V4, and, well, OData V4 is already over a decade old.
While CAP will default to serving OData services, its modular design allows for services to be served using different protocols. Yes, OData is more than a protocol, but the protocol component is critical.
And that leads us on to the other protocol that CAP can serve, out of the box: "REST". I personally put "REST" here in quotes, because REST is not a protocol, it is an architectural style, with a set of constraints that should inform the design of HTTP-based APIs, if they are to be accurately referred to as "RESTful". Incidentally, if an API conforms to all of the architectural constraints described, it is referred to as "Fully RESTful", and yes, that's the "hidden" (second, or first) meaning behind the name of my narrowboat where I live and work.
When you see "REST" referred to as a protocol, think of it as a "plain HTTP" style API.
Anyway, for the sake of this Developer Challenge, and for common understanding and consistency with Capire, the CAP documentation, we can think of "REST" as a protocol. You can see in the cds.serve() - cds.protocols section of Capire which protocol adapters are available both out of the box and as an open source package.
It's important then to think of a CAP service in different contexts, or at different layers:
And as the primary protocols used to serve it are all based on the application protocol that is HTTP, there's a link between the protocol used, and how that is indicated, or exposed, as part of the URL path. Here are the default paths for the standard protocols:
And OData V4 is the default protocol. So by default, if you define a service x, it will be served as an OData V4 service, at the service path /odata/v4/x.
With the @path annotation you can specify a custom path for the service, and this is what you were required to do for the service that contained the API endpoints described in Tasks 1, 2 and 3, in that the required path for the OData service was /basic, rather than /odata/v4/basic.
With the @protocol annotation you can specify the protocol.
If you take a basic CAP service x that defaults to being served as an OData protocol, at /odata/v4/x, and then switch protocols by annotating it with @protocol: 'rest' (or simply @rest) you'll see that while there are differences between the key resources (such as the entity sets) they are only very slight. Perhaps most notably there's an absence of any notion of metadata or metadata context. Even the standard OData system query options (such as $filter and $select) are supported.
That lack of differences, in my opinion, is because it makes a lot of sense, based on a combination of reasons:
This and the next couple of tasks give you a chance to explore these ideas, and the "REST" protocol in particular.
Here are the specific requirements for this task.
You must create a new service called plain. One separate to the basic service that you already have. The service must be served via the "REST" protocol, at the default endpoint for such a service.
Within this new service, you should define a very simple API endpoint that returns a static value, the answer to life, the universe, and everything. Very much like the endpoint in Task 1 - Your first service and endpoint.
It should expect no arguments (and therefore be defined with no parameters), and be standalone, i.e. "unbound". It should be callable via the HTTP GET method and have no side-effects, i.e. a "function".
The terms bound and unbound, and the idea of and semantic differences between functions and actions, are taken from the OData world, but make sense here in the land of more plain HTTP based APIs, especially as with CAP, the service definition (CDS model), and the serving of the service (protocol), are separate.
To underline, however, that we've now moved away from the OData protocol, the API endpoint should be addressable via the following simpler path:
/rest/plain/theAnswer
Like always, once you've got your service defined, and a simple implementation ready, you're done.
It is definitely worth testing it yourself first, e.g. with curl, Postman, or even the REST Client extension to VS Code that some of you are using (going on what I can see from some of your responses to the previous task). Use whatever tool you prefer for making HTTP calls.
With your server running (on, let's say, the default local CAP server port of 4004), make a request like this:
curl -s --url "localhost:4004/rest/plain/theAnswer"
42
CAP offers flexibility, not least in service definitions (in the overall CDS model) and implementations. In needing to define and implement a new, second service, you have lots of choices:
and of course you could always:
Which way you go is up to you. It would be great to hear from you, again, in the comments below, which approach you took.
Now you're ready to submit your CANDIDATE service, with the specific API endpoint, to the TESTER!
The task identifier you need to supply in the payload of your submission is: plain-theAnswer.
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.
Now, to have your freshly minted API endpoint in this task tested, you'll need to submit a JSON payload like this:
{ "communityid": "<your-community-id>", "serviceurl": "<the-URL-of-your-service>", "task": "plain-theAnswer" }
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), not the full URL of the specific API endpoint itself
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.
Until the next task, have fun, and if you have any questions or comments, leave them below!
2024 Jul 15 8:02 AM
Q: What is the value of the Content-Type header in the HTTP responses served for your endpoint?
A: content-type: text/plain; charset=utf-8
Q: What is the value of the Content-Type header in the HTTP response when serving an array of Integers?
A: content-type:application/json; charset=utf-8
2024 Jul 15 11:40 AM
Excellent, yes - the different Content Type values were worth noticing, I think 👍
2024 Jul 15 8:13 AM
Done!
value of the Content-Type header in the HTTP responses served for your endpoint : text/plain;charset=utf-8
for an array of Integers: application/json;charset=utf-8
2024 Jul 15 11:41 AM
Nice. Yes, different, right? Makes sense, too!
2024 Jul 15 8:53 AM - edited 2024 Jul 15 2:35 PM
I kept the same project, and created a new .js/.cds pair of files.
During development I use cds watch, and then cf push for the final deployment.
I prefer cf push, as it creates a new CF app, which means I can run it even if my BAS workspace is stopped.
2024 Jul 15 11:41 AM
2024 Jul 15 12:46 PM
2024 Jul 15 3:10 PM
Hello,
Good explanation of the difference between Odata V4 and REST!
My submission:
2024 Jul 15 4:32 PM
2024 Jul 15 4:06 PM
2024 Jul 15 4:33 PM
2024 Jul 15 7:34 PM
2024 Jul 15 9:15 PM
Here's my submission:
Content-Type for Integer (or for String aswell) : "text/plain; charset=utf-8"
Content-Type for Array of Integer: "application/json; charset=utf-8"
But I have a question for you too: why are the values for task (or for communityid) translated to lower case? "plain-theAnswer" becomes "plain-theanswer" here.
2024 Jul 16 6:45 AM - edited 2024 Jul 16 6:46 AM
Nicely observed! I love it when folks have eagle eyes 🙂
It is simply because I wanted to "normalise" the task identifiers, mostly because I load the tasks dynamically in the TESTER, like this:
Each task's requirement is encapsulated into a separate JS file.
But also partially because it makes reporting (via the Testlog entity set) easier and more consistent.
cheers!
dj
2024 Jul 16 7:21 AM
2024 Jul 16 7:33 AM
2024 Jul 16 9:47 AM
2024 Jul 16 11:23 AM
2024 Jul 16 3:33 PM
2024 Jul 16 12:49 PM
Hello,
Here is my submission for Task-4
Thanks,
Manoj Kumar Potharaju.
2024 Jul 16 9:12 PM
My submission for task 4
What is the value of the Content-Type header in the HTTP responses served for your endpoint? - Content-Type: text/plain; charset=utf-8
What would it be if you served an array of Integers? - Content-Type: application/json; charset=utf-8
2024 Jul 17 8:46 AM
2024 Jul 18 6:18 AM
@qmacro : can you able to check logs and let me know the reason for failure
i m still getting it as failed.
2024 Jul 18 7:30 AM
2024 Jul 18 5:05 PM
2024 Jul 18 6:50 PM
Hi @Ruthiel, 101 is an answer, but not the answer (to life, the universe, and everything) 😉
2024 Jul 19 9:34 AM
Thank you very much @M-K !
Indeed 101 is not the Answer!
2024 Jul 20 8:29 AM
Thank you @M-K , I didn't actually make that clear enough for some folks (I wrongly assumed that _everyone_ has read The Hitchhiker's Guide To The Galaxy :-))
2024 Jul 19 9:43 AM
2024 Jul 18 5:53 PM
2024 Jul 19 4:03 PM
2024 Jul 21 6:00 AM
2024 Jul 23 5:31 AM
The answer to life, the universe and everything 😀
2024 Jul 23 6:11 AM
2024 Jul 27 2:05 PM
2024 Aug 02 3:19 PM
Pass 🙂
When array is served the Content-Type header in the response application/json; charset=utf-8 otherwise text/plain; charset=utf-8.