Skip to content
This repository has been archived by the owner on Nov 13, 2021. It is now read-only.

Latest commit

 

History

History

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 
 
 
 
 

Exercise 06 - Enhancing the service with annotations

In this exercise you'll enhance the service definition with annotations that restrict the OData operations that are allowed.

Steps

At the end of these steps, your OData service will have different levels of access for each of the entities.

1. Import a collection of HTTP requests into Postman

👉 In the same way that you did in exercise 05, import a collection of HTTP requests into your Postman client via the URL to postman-06.json resource.

This contains a number of different requests ready for you to try.

Note: If you still want to use the command line, you'll find the invocations in the appropriate steps below.

Postman collection

2. Test the existing write access to Books and Authors

Right now the Books and Authors entities are exposed in the CatalogService service. In the subsequent steps in this exercise we'll be tightening the restrictions down to read-only. Before we do, let's check to see that, at least currently, we have write access. We'll do that by making a couple of OData Create operations, one to create a new author, and the other to add a book by that author.

👉 In the Postman collection you imported, try out the requests in the folder "(A) Before @readonly annotations", running them in the order they're presented (the creation of the new book is for the new author, which needs to exist first).

If you want to use curl on the command line instead of Postman, use the following invocations.

First, add the author "Iain M Banks":

curl \
  -d '{"ID": 162, "name": "Iain M Banks"}' \
  -H 'Content-Type: application/json' \
  http://localhost:4004/catalog/Authors

For Windows users:

curl ^
  -d "{\"ID\": 162, \"name\": \"Iain M Banks\"}" ^
  -H "Content-Type: application/json" ^
  http://localhost:4004/catalog/Authors

Now add the book "Consider Phlebas":

curl \
  -d '{"ID": 44138, "title": "Consider Phlebas", "stock": 541, "author_ID": 162 }' \
  -H 'Content-Type: application/json' \
  http://localhost:4004/catalog/Books

For Windows users:

curl ^
  -d "{\"ID\": 44138, \"title\": \"Consider Phlebas\", \"stock\": 541, \"author_ID\": 162 }" ^
  -H "Content-Type: application/json" ^
  http://localhost:4004/catalog/Books

Check that the creation requests are successful, and that you can see the new author and book in an OData Query operation: http://localhost:4004/catalog/Authors?$expand=books.

3. Restrict access to the Books and Authors entities

Now, we want to only allow read-only operations on the master data. This can be achieved with OData annotations that are encapsulated into a convenient @readonly shortcut.

👉 Add this @readonly shortcut to each of the Books and Authors specifications in the CatalogService thus:

service CatalogService {
    @readonly entity Books as projection on my.Books;
    @readonly entity Authors as projection on my.Authors;
    entity Orders as projection on my.Orders;
}

What does this do, precisely? Let's find out.

👉 After saving the file, start up cds watch to get back into the auto restart mode. The, examine the OData service's metadata. You should find annotations that look like this:

readonly annotations

Are these annotations just recommendations that can be overridden? Let's find out.

4. Attempt to modify the Books and Authors entitysets

We can think of the annotations that we saw in the metadata document as guidelines for a UI, but we want to ensure that the restrictions are really in effect in the service itself. Let's try to create another entry in the Books entityset.

👉 In the same Postman collection you imported, try out the first request in the folder "(B) After @readonly annotations". If you want to use curl on the command line instead of Postman, use the following invocation to add the book "The Player of Games":

curl \
  -d '{"ID": 47110, "title": "The Player of Games", "stock": 405, "author_ID": 162 }' \
  -H 'Content-Type: application/json' \
  http://localhost:4004/catalog/Books

For Windows users:

curl ^
  -d "{\"ID\": 47110, \"title\": \"The Player of Games\", \"stock\": 405, \"author_ID\": 162 }" ^
  -H "Content-Type: application/json" ^
  http://localhost:4004/catalog/Books

The request is an OData Create request for a new book. You should see that this request is rejected with HTTP status code 405 "Method Not Allowed", with an error like this supplied in the response body:

{
    "error": {
        "code": "405",
        "message": "Method Not Allowed"
    }
}

You should also see a line in the terminal (where you invoked cds serve all) like this:

[2019-05-29T15:16:39.694Z | WARNING | 1939700]: Method Not Allowed

👉 Next, try out the second request in that same folder - it's an OData Delete operation, to remove the book "The Raven". If you want to use curl on the command line instead of Postman, use this invocation:

curl \
  -X DELETE \
  'http://localhost:4004/catalog/Books(251)'

For Windows users:

curl ^
-X DELETE ^
"http://localhost:4004/catalog/Books(251)"

It should also fail in a similar way.

TIP: If you end up destroying your test data, you can easily restore it by redeploying (cds deploy), as the test data will be re-seeded from the CSV files.

5. Restrict access to the Orders entityset

In a similar way to how we restricted access to the Books and Authors entitysets to read-only operations, we will now restrict access to the Orders entityset so that orders can only be created, and not viewed, amended or removed.

As you might have guessed, this is achieved via the @insertonly annotation shortcut.

👉 Before making this edit, switch back (if you haven't already) to using cds watch so that restarts will be automatic after changes.

👉 In the CatalogService service definition in srv/service.cds, annotate the Orders entity with @insertonly so it looks like this:

service CatalogService {
    @readonly entity Books as projection on my.Books;
    @readonly entity Authors as projection on my.Authors;
    @insertonly entity Orders as projection on my.Orders;
}

👉 Now create a couple of orders using the Postman collection from exercise 05 - there should be a couple of POST requests against the Orders entityset (refer to the step in exercise 05 for the command line invocations if you wish).

Postman request collection

Note at this point that the requests are successful: HTTP status code 201 is returned for each request, along with the newly created entity in the response payload, like this example:

{
    "@odata.context": "$metadata#Orders/$entity",
    "@odata.metadataEtag": "W/\"qItYMyHC4RMSWG6mehaOHDxo+o/HzUCPMchqSx7hd1k=\"",
    "ID": "527ef85a-aef2-464b-89f6-6a3ce64f2e14",
    "modifiedAt": null,
    "createdAt": "2019-03-26T06:51:52Z",
    "createdBy": "anonymous",
    "modifiedBy": null,
    "quantity": 9,
    "book_ID": 427,
    "country_code": null
}

Further, you can see the request logged in the terminal, with no sign of any errors:

POST /catalog/Orders

This confirms we can insert new orders. But can we see what they are?

👉 Try to perform an OData Query operation on the Orders entityset, simply by requesting this URL: http://localhost:4004/catalog/Orders.

The operation should be denied, and you'll receive something like this in the body of the response in your browser:

<error xmlns="http://docs.oasis-open.org/odata/ns/metadata">
<code>405</code>
<message>Method Not Allowed</message>
</error>

Summary

In this exercise you used shortcut annotations to restrict access to the entities expose in the service definition. The annotations provide not only information to be used by consumers (such as frontends) but also control access at the HTTP level.

Questions

  1. Did you notice anything special about your request to http://localhost:4004/catalog/Authors?$expand=books in step 2?
  1. How might the annotations relating to the read-only restrictions be useful in a UI context?
  1. What was the format of the OData Delete operation - did we need to supply a payload?