Lab 02 — Create a Service Projection
Goal
Expose the Location entity (from Lab 01) through the existing StarWarsPeople service, with appropriate access controls.
Prerequisite: Complete Lab 01 first.
Background
CDS service projections let you decide which entities to expose, under what shape, and with what constraints. This lab teaches you:
- How to add an entity to an existing service
- How
@readonlyprevents writes at the service boundary - How
redirected toresolves associations in the service scope - How service-level and entity-level access annotations work together
Steps
Step 1 — Expose Location in the People service
Open srv/people-service.cds and add a Location projection inside the service StarWarsPeople block, after the last entity definition:
@readonly : true
entity Location as projection on StarWars.Location {
*, planet : redirected to Planet
};Step 2 — Verify it appears in $metadata
Start the service:
npm run sqliteThen fetch the metadata and check that Location is listed:
GET http://localhost:4004/odata/v4/StarWarsPeople/$metadataSearch for Location in the XML response.
Step 3 — Test read and write behavior
Read (should work):
GET http://localhost:4004/odata/v4/StarWarsPeople/LocationTry to write (should be rejected with 405 Method Not Allowed):
POST http://localhost:4004/odata/v4/StarWarsPeople/Location
Content-Type: application/json
{ "name": "Test Location" }Verify you get a 405 or a 400 error — @readonly blocks all mutations.
Step 4 — Add an OData test
Add a test to test/model.test.js (inside the existing describe block):
it('Location entity is exposed read-only in StarWarsPeople service', async () => {
const { status, data } = await GET('/odata/v4/StarWarsPeople/Location')
assert.equal(status, 200)
assert.ok(Array.isArray(data.value), 'Should return OData value array')
})Step 5 — Run tests
npm run testExpected Outcome
Locationappears in$metadataforStarWarsPeopleGET /odata/v4/StarWarsPeople/Locationreturns HTTP 200POSTto Location is rejected (read-only)- New test passes
Hints
@readonlyis a shorthand for@Capabilities.Insertable: false,@Capabilities.Updatable: false,@Capabilities.Deletable: false.redirected to Planetis needed so that OData$expand=planetworks — without it, the association target is unresolved in the service scope.- If you get a compile error about
Locationnot being found, make sure you added it inside thenamespace star.wars;block inschema.cds.
Stretch Exercise
Make Location writable (remove @readonly) and add it to services-auth.cds so that only Editor role users can create or update Locations.