Search

    Reflecting CDS Models

    Find here information about reflecting parsed CDS models in CSN representation.

    cds.reflect (csn) LinkedCSN

    cds.linked (csn) LinkedCSN

    Methods cds.reflect and cds.linked (they are aliases to the same method) turn given parsed models, into instances of class LinkedCSN, thus adding the reflection methods documented in the following section.

    In addition they turn all definitions, and all elements thereof, to linked definitions by prototype-chaining them to their base definitions up to one of cds.builtin.types. This in turn makes all definitions instances of the respective cds.builtin.classes, and allows to use the methods as documented below on them.

    For example this usage of cds.linked:

    const m = cds.linked (CQL`
      type Bar : String(22);
      entity Foo { bar: Bar }
      entity Woo as projection on Foo;
    `)
    

    … will result in the equivalent of this:

    const { entity, 'cds.String':String } = cds.builtin.types
    const Bar = {__proto__: String }
    const Foo = {__proto__: entity, elements: { bar: {__proto__: Bar } }}
    const Woo = {__proto__: Foo }
    const m = new LinkedCSN ({ definitions: { Bar, Foo, Woo }})
    

    Returned instances are cached, so subsequent calls to cds.reflect with the same parsed model return the same cached instance.

    let model = await cds.load ('some-cds-model')
    let reflected = cds.linked (model)       //> result is cached
    let reflected2 = cds.linked (model)      //> === reflected
    let reflected3 = cds.linked (reflected)  //> === reflected
    

    Class LinkedCSN

    Models passed through cds.linked become instances of this class, and hence inherit the following properties and methods:

    m.services [defs]

    This is a getter property providing convenient and cached access to all service definitions in a model.

    let csn = CQL`
      service CatalogService { ... }
      service AdminService { ... }
    `
    let m = cds.linked (csn)
    let [ CatalogService, AdminService ] = m.services
    

    m.entities (namespace) {defs}

    m.events (namespace) {defs}

    m.operations (namespace) {defs}

    These properties / methods provide convenient and cached access to a model’s definitions within a given namespace. If no namespace is specified, the model’s declared namespace is used, if any.

    For example:

    let csn = CQL`
      namespace my.bookshop;
      entity Books {...}
      entity Authors {...}
      service CatalogService {
        entity ListOfBooks as projection on Books {...}
      }
    `
    let m = cds.linked (csn)
    let { Books, Authors } = m.entities
    let { ListOfBooks } = m.entities ('my.bookshop.CatalogService')
    

    The methods each return an object of respective definitions. Object destructuring operators allow to easily access single definitions by name as shown above.

    m.each* (x, defs?) Iterator<defs>

    Fetches definitions matching the given filter, returning an iterator on them.

    let m = cds.reflect (csn)
    for (let d of m.each('entity')) {
      console.log (d.kind, d.name)
    }
    

    The first argument x specifies a filter to match definitions, which can be one of:

    • a function returning true or false
    • a string referring to a kind of definition

    Derived kinds are supported, for example, m.each('struct') matches structs as well as entities; kind 'any' matches all.

    The second optional argument defs allows to specify the definitions to fetch in, defaults to this.definitions.

    m.all (x, defs) [defs]

    Convenience shortcut to [... model.each()], for example, the following are equivalent:

    m.all('entity')        //> using shortcut
    [...m.each('entity')]  //> using spread operator
    

    m.find (x, defs) def

    Convenience shortcut to fetch definitions matching the given filter, returning the first match, if any. For example:

    let service = m.find('service')
    

    The implementation uses to .each() as follows:

    for (let any of m.each('service'))  return any
    

    m.foreach / forall (x, visitor, defs)

    Calls the visitor for each definition matching the given filter. foreach iterates through the passed in defs only, forall in addition walks through all nested element definitions hierarchically.

    • x — the filter to match definitions → see .each(x)
    • visitor — the callback function
    • defs — the definitions to fetch in, default: this.definitions

    Examples:

    // print the names of all services
    let m = cds.reflect(csn)
    m.foreach ('service', s => console.log(s.name))
    
    // print the names of all Associations in Books element
    let { Books } = m.entities()
    m.foreach ('Association', a => console.log(a.name), Books.elements)
    
    // print the names of all Associations in the model
    m.forall ('Association', a => console.log(a.name))
    
    // print hierarchy of all definitions recursively
    let m = cds.linked(csn)
    m.forall (d => {
      let s=''; for (let p=d.parent; p; p=p.parent)  s += '  - '
      console.log (s, d.kind, d.name)
    })
    

    cds.builtin.classes

    This property gives you access to the very roots of cds’s type system. When a model is passed through cds.linked all definitions effectively become instances of one of these classes. In essence they are defined as follows:

    class any {...}
    class context extends any {...}
    cds.service = class service extends context {...}
    cds.type = class type extends any {...}
                  class scalar extends type {...}
                    class boolean extends scalar {...}
                    class number extends scalar {...}
                    class date extends scalar {...}
                    class string extends scalar {...}
    cds.array  = class array extends type {...}
    cds.struct = class struct extends type {...}
    cds.entity = class entity extends struct {...}
    cds.event = class event extends struct {...}
    cds.Association = class Association extends type {...}
    cds.Composition = class Composition extends Association {...}
    

    A few prominent ones of the above classes are available through top-level shortcuts as indicated by the cds.<classname> = prefixes in the above pseudo code, find more details on these in the following sections.

    For example, you can use these classes as follows:

    let model = CDL`
       entity Books { author: Association to Authors; }
       entity Authors { key ID: UUID; }
    `)
    let m = cds.linked(model)
    let { Books, Authors } = m.entities
    let isEntity = Books instanceof cds.entity
    let keys = Books.keys
    let { author } = Books.elements
    if (author.is2many) ...
    

    mixin ({classes})

    Provided a convenient way to enhance one or more of the builtin classes with additional methods. Use it like that:

    mixins.js

    const cds = require ('@sap/cds')
    
    // simplistic csn2cdl enablement
    cds.builtin.classes .mixin (
      class type {
        toCDL(){ return `${this.kind} ${this.name} : ${this.typeAsCDL()};\n` }
        typeAsCDL(){ return `${this.type.replace(/^cds\./,'')}` }
      },
      class struct {
        typeAsCDL() { return `{\n${ Object.values(this.elements).map (
          e => `  ${e.toCDL()}`
        ).join('')}}`}
      },
      class entity extends cds.struct {
        typeAsCDL() { return (
          this.includes ? this.includes+' ' : ''
        ) + super.typeAsCDL() }
      },
      class Association {
        typeAsCDL(){ return `Association to ${this.target}` }
      },
    )
    
    // test drive
    let csn = CDL`
    entity Books : cuid { title:String; author: Association to Authors }
    entity Authors : cuid { name:String; }
    aspect cuid : { key ID:UUID; }
    `
    cds.linked(csn).foreach (d => console.log(d.toCDL()))
    

    class any

    All cds.linked definitions are instances of this class, or subclasses thereof, and hence support the following properties and methods:

    any instanceof built-in class true|false

    You can use JavaScript’s standard instanceof in combination with the built-in classes to check a linked definition’s type:

    let m = cds.linked(csn)
    let { Foo } = m.entities
    if (Foo instanceof cds.entity)  console.log ("it's an entity")
    

    any.parent def

    Refers to the parent definition of elements or bound actions; undefined for top-level definitions.

    any.name string

    The linked definition’s fully qualified name as a non-enumerable property.

    any kind string

    The linked definition’s resolved kind as a non-enumerable property. One of context, service, entity, type, aspect, event, element, or annotation as documented in the CSN specification.

    any.is (kind) true|false

    Checks if a linked definition is of certain kind. Besides the specified kinds, the argument may also be struct, array, or view.

    any.own (property) any

    Returns the value of linked definition’s own property, excluding properties inherited from the prototype chain.

    cds.entity

    All linked entity definitions are instances of this class, which primarily provides convenience getters to quickly access all keys, Associations, or Compositions within the entity’s elements.

    entity.elements {defs}

    The entity’s declared elements as documented in the CSN Specification.

    entity.keys {defs}

    A getter returning a cached object with an entity definition’s declared primary keys by name. The returned object adheres to the specification of CSN Definitions.

    entity.associations {defs}

    A getter returning a cached object of all Associations from an entity definition’s elements. The returned object adheres to the specification of CSN Definitions.

    entity.compositions {defs}

    A getter returning a cached object of all Compositions from an entity definition’s elements. The returned object adheres to the specification of CSN Definitions.

    entity.texts string

    Returns the linked definition’s fully qualified name + '.texts' to easily refer to the texts entity containing translations for localized elements, if any.

    Learn more about Localized Data

    entity.drafts string

    Returns the linked definition’s fully qualified name + '.drafts' to easily refer to draft data for the current entity, if draft is enabled.

    Learn more about Draft Data

    cds.Association

    All linked definitions of type Association or Composition, including elements, are instances of this class, which primarily provides convenience getters access the target definition, or to check its type or cardinality.

    assoc._target def

    Refers to the association’s resolved target definition.

    assoc.isAssociation true|false

    Convenient shortcut to check whether a definition is an Association. Returns true for all Associations, including Compositions.

    assoc.isComposition true|false

    Convenient shortcut to check whether a definition is a Composition. Returns true for all Compositions.

    assoc.is2one / is2many true|false

    Convenient shortcut to check whether an association definition is to-one or to-many. See also the specification of CSN Associations

    cds.builtin.types

    This property gives you access to all prototypes of the builtin classes as well as to all linked definitions of the builtin pre-defined types. The resulting object is in turn like the definitions in a LinkedCSN.

    Actually, at runtime CDS is in fact bootstrapped out of this using core CSN object structures and cds.linked techniques. Think of it to be constructed as follows:

    cds.builtin.types = cds.linked (CQL`
      using from './roots';
      context cds {
        type UUID         : String(36);
        type Boolean      : boolean;
        type Integer      : number;
        type Integer64    : Integer;
        type Decimal      : number;
        type Double       : number;
        type Date         : date;
        type Time         : date;
        type DateTime     : date;
        type Timestamp    : date;
        type String       : string;
        type Binary       : string;
        type LargeString  : string;
        type LargeBinary  : string;
      }
    `) .definitions
    

    With ./roots being this in-memory CSN:

    const { any, context, service , 
      type, scalar, string, number, boolean, date, 
      array, struct, entity, event, aspect
      Association, Composition
    } = cds.builtin.classes
    
    const roots = module.exports = {definitions:{
      any: new any,
      context: new context ({type:'any'}),
      type: new type ({type:'any'}),
        scalar: new scalar ({type:'type'}),
          string: new string ({type:'scalar'}),
          number: new number ({type:'scalar'}),
          boolean: new boolean ({type:'scalar'}),
          date: new date ({type:'scalar'}),
        array: new array ({type:'type'}),
        struct: new struct ({type:'type'}),
          entity: new entity ({type:'struct'}),
          event: new event ({type:'struct'}),
          aspect: new aspect ({type:'struct'}),
        Association: new Association ({type:'type'}),
          Composition: new Composition ({type:'Association'}),
      service: new service ({type:'context'}),
    }}
    

    Indentation indicates inheritance.

    Show/Hide Beta Features