Search

    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:

    @Autowired
    CdsModel model;
    

    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"));
    
    Show/Hide Beta Features