Working with CDS Models
The Model Reflection API is a set of interfaces, which provide the ability to introspect a CDS model and retrieve details on the services, types, entities, and their elements that are defined by the model.
Content
The CDS Model
The interface CdsModel
represents the complete CDS model of the CAP application and is the starting point for the introspection.
The CdsModel
can be obtained from the EventContext
:
import com.sap.cds.services.EventContext;
import com.sap.cds.reflect.CdsModel;
@On(event = "READ", entity = "my.catalogservice.books")
public void readBooksVerify(EventContext context) {
CdsModel model = context.getModel();
[...]
}
or, in Spring, be injected.
On a lower level, the CdsModel
can be obtained from the CdsDataStoreConnector
, or using the read
method from a CSN String or InputStream:
InputStream csnJson = ...
CdsModel model = CdsModel.read(csnJson);
Examples
The following examples are using this CDS model:
namespace my.bookshop;
entity Books {
title : localized String(111);
author : Association to Authors;
...
}
entity Authors {
key ID : Integer;
...
}
entity Orders {
OrderNo : String @title:'Order Number';
...
}
Get and Inspect an Element of an Entity
In this example, we introspect the details of the type of the element title
of the entity Books
:
CdsEntity books = model.getEntity("my.bookshop.Books");
CdsElement title = books.getElement("title");
boolean key = title.isKey(); // false
CdsType type = title.getType(); // CdsSimpleType
if (type.isSimple()) { // true
CdsSimpleType simple = type.as(CdsSimpleType.class);
String typeName = simple.getQualifiedName(); // "cds.String"
CdsBaseType baseType = simple.getType(); // CdsBaseType.STRING
Class<?> javaType = simple.getJavaType(); // String.class
Boolean localized = simple.get("localized"); // true
Integer length = simple.get("length"); // 111
}
Get and Inspect All Elements of an Entity
CdsEntity books = model.getEntity("my.bookshop.Books");
Stream<CdsElement> elements = books.elements();
The method elements()
returns a stream of all elements of the given entity, structured type, or event. It’s important to note that the Model Reflection API doesn’t guarantee the element order to be exactly like in the source CSN document. However, the order is guaranteed to be stable during multiple consecutive model reads.
In case the element names are known beforehand it’s recommended to access them by name through the getElement(String name)
method.
Get and Inspect an Association Element of an Entity
We can also analyze the details of an association:
CdsElement authorElement = book.getAssociation("author");
CdsAssociationType toAuthor = authorElement.getType();
CdsEntity author = toAuthor.getTarget(); // Entity: my.bookshop.Authors
boolean association = toAuthor.isAssociation(); // true
boolean composition = toAuthor.isComposition(); // false
Cardinality cardinality = toAuthor.getCardinality();
String sourceMax = cardinality.getSourceMax(); // "*"
String targetMin = cardinality.getTargetMin(); // "0"
String targetMax = cardinality.getTargetMax(); // "1"
Stream<CdsElement> keys = toAuthor.keys(); // Stream: [ ID ]
Optional<CqnExpression> onCondition = toAuthor.onCondition(); // empty
Find an Annotation by Name and Get Its Value
Here, we programmatically check if the element OrderNo
carries the annotation title
and set the value of displayName
depending on the presence of the
annotation:
CdsEntity order = model.getEntity("my.bookshop.Orders");
CdsElement orderNo = order.getElement("OrderNo");
Optional<CdsAnnotation<String>> annotation = orderNo
.findAnnotation("title");
String displayName = annotation.map(CdsAnnotation::getValue)
.orElse(orderNo.getName()); // "Order Number"
Filter a Stream of Services for non-abstract Services
Using a stream we determine all non-abstract services:
Stream<CdsService> services = model.services()
.filter(s -> !s.isAbstract());
List<CdsService> serviceList = services.collect(Collectors.toList());
Filter a Stream of Entities by Namespace
The static method com.sap.cds.reflect.CdsDefinition.byNamespace
allows to create a predicate to filter a stream of definitions
(for example, entities, elements, …) for definitions contained in a given namespace:
import static com.sap.cds.reflect.CdsDefinition.byNamespace;
...
Stream<CdsEntity> entities = model.entities()
.filter(byNamespace("my.bookshop"));
Get All Elements with Given Annotation
The static method com.sap.cds.reflect.CdsAnnotatable.byAnnotation
allows to create a predicate to filter a stream of annotatable model components (for example, entities, elements, …) for components that carry a given annotation:
import static com.sap.cds.reflect.CdsAnnotatable.byAnnotation;
...
CdsEntity order = model.getEntity("my.bookshop.Orders");
Stream<CdsElement> elements = order.elements()
.filter(byAnnotation("title"));