This is a task in the July Developer Challenge – “Reverse APIs”.
This task gets you to add another API endpoint to the plain “REST” service you have, but will be a little different in its definition, implementation, and how it’s called.
Background
HTTP-based API endpoints, whatever the protocol, can differ in various ways, such as:
- how they’re called (which HTTP method)
- whether they expect a payload (a body) in the HTTP request
- what they do, what effect they have at the server side
- what resource and representation they return (if any)
HTTP is an application protocol and there are already well understood semantic meanings for each of the methods (also known as “verbs”, where the URLs are the “nouns”). There are some protocols that abuse or ignore these semantics, particularly the set of heavyweight “Web Services” from a decade or so ago, known as “WS-Deathstar” (due to their weight and complexity), SOAP, and even today we have GraphQL that arguably abuses HTTP as a mere transport layer. Yes, “abuse” is a strong word, but, as my bio hints at, it’s an opinion I hold, and is one of many 🙂
Anyway, in addition to those differences listed earlier, one must also consider the duumvirate (yes, I did just use that word) of:
- idempotency
- side effects
Idempotency (from the Latin, as is, of course, duumvirate) essentially means “having the same effect”. To quote the excellent MDN docs on Idempotency:
An HTTP method is idempotent if the intended effect on the server of making a single request is the same as the effect of making several identical requests.
Examples of HTTP methods that have this idempotency characteristic are: GET, HEAD, PUT and DELETE (yes!).
A side effect in this context means that in handling or otherwise fulfilling an HTTP request, state is changed on the server. HTTP methods that do not have side effects are known as safe. Examples of HTTP methods that do have side effects, i.e. that are not safe, are POST, PUT and DELETE. Examples of HTTP methods that are side effect free, i.e. safe, are GET, HEAD and OPTIONS.
Semantic meanings are important, as they form part of the “contract” of behaviour between clients and servers.
Why am I telling you all this? Because CDL, CAP’s definition language that you use to define your overall CDS model, has a couple of keywords that are important in this context.
Actions and functions
In Capire’s section on Providing Services, there’s a subsection on Actions & Functions that explain more. I recommend you go and read that section, then come back here.
Did you notice the explanation was in the context of OData? That’s because it’s where the specific definitions originate, but these definitions make sense even outside the context of OData, such as when serving via the “REST” protocol.
In summary, we can see that both actions and functions are for providing API endpoints that represent resources beyond the standard CRUD+Q requests, although:
- Functions are for defining API endpoints that are safe, these endpoints are to be requested with HTTP GET, and any data should be supplied in the URL. There’s a related rule here that functions must be addressed (in the URL) with brackets, even if there’s no data to supply. Functions, on the whole, are for returning data.
- Actions are for defining API endpoints that may have side effects, and these endpoints must be requested with HTTP POST; any data that is to be supplied must be as a payload to the POST request. In addition, actions may return nothing (just an HTTP status code and headers).
In addition (but not relevant for this task), both functions and actions can be bound or unbound. The idea of “unbound” is what we generally think of in terms of simple API endpoints. The “bound” concept is from OData, and is about calling an action or function relative to a specific entity. In such cases there’s an extra implicit “binding parameter” that the implementation receives in the request, as a connection to the instance of the entity to which the called function is bound.
The first task in the context of this current plain “REST” service was an unbound function (note the brackets, despite no parameters):
/rest/plain/theAnswer()
- use the action keyword
- call it using a request with the HTTP POST method
- send the data in the payload of the request
You will also not need to use brackets at the end of the URL.
The requirements
Here are the specific requirements for this task.
In the plain service you have already, define an unbound action that expects a list of integers. The implementation of that unbound action must determine the highest of those numbers, and return it as a single integer.
Submitting to the TESTER
Now you’re ready to submit your CANDIDATE service, with this new API endpoint, to the TESTER!
Note that the TESTER will be calling your action via HTTP POST, and supplying the list of integers as an array, in a JSON representation, in other words, like this:
POST /rest/plain/highestValue HTTP/1.1 Host: localhost:8000 Content-Type: application/json Content-Length: 19 [54, 203, -3, 0, 1]
The payload
The task identifier you need to supply in the payload of your submission is: plain-highestValue.
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": "plain-highestValue" }
-
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.
Until the next task, have fun, and if you have any questions or comments, leave them below!