Reflecting CDS Models
Find here information about reflecting parsed CDS models in CSN representation.
cds. linked (csn)
Methodcds.linked
(orcds.reflect
which is an alias to the same method) turns a given parsed model into an instance ofclassLinkedCSN
, and all definitions within into instances ofclassLinkedDefinition
, recursively.
Declaration:
function* cds.linked (csn: CSN | string)=> LinkedCSN
A typical usage is like that:
let csn= cds.load('some-model.cds')let linked= cds.linked(csn)// linked === csn
Instead of a already compiled CSN, you can also pass a string containing CDL source code:
let linked= cds.linked` entity Books { key ID: UUID; title: String; author: Association to Authors; } entity Authors { key ID: UUID; name: String; }`
The passed in model getsmodified, and the returned linked model is actually the modified passed-in csn.
The operation isidempotent, that is, you can repeatedly invoke it on already linked models with zero overhead.
LinkedCSN
Models passed throughcds.linked
become instances of this class.
. is_linked
A tag property which istrue
for linked models.
. definitions
TheCSN definitions of the model, turned into an instance ofLinkedDefinitions
.
. services
. entities
These are convenient shortcuts to access allservice or allentity definitions in a model.
The value is an instance ofLinkedDefinitions
.
For example:
let m= cds.linked` namespace my.bookshop; entity Books {...} entity Authors {...} service CatalogService { entity ListOfBooks as projection on Books {...} }`// Object naturelet { CatalogService, AdminService }= m.serviceslet { Books, Authors }= m.entities// Array naturefor (let eachof m.entities) console.log(each.name)// Function naturelet { ListOfBooks }= m.entities ('my.bookshop.CatalogService')
In addition to the object and array natures ofLinkedDefinitions
these properties also can be used as functions, which allows to optionally specify a namespace to fetch all definitions with prefixed with that. If no namespace is specified, the model's declared namespace is used, if any.
each()
function* lm.each ( filter : string | def => true/false, defs? : linked_definitions)
Fetches definitions matching the given filter, returning an iterator on them.
let m= cds.reflect (csn)for (let dof m.each('entity')) { console.log (d.kind, d.name)}
The first argumentfilter specifies a filter to match definitions, which can be one of:
- a
string
referring to akind of definition - a
function
returningtrue
orfalse
Derived kinds are supported, for example,m.each('struct')
matches structs as well as entities; kind'any'
matches all.
The second optional argumentdefs allows to specify the definitions to fetch in, defaults tothis.definitions
.
all()
function lm.all ( filter : string | def => true/false, defs? : linked_definitions)
Convenience shortcut to[... model.each()]
, for example, the following are equivalent:
m.all('entity')//> using shortcut[...m.each('entity')]//> using spread operator
find()
function lm.find ( filter : string | def => true/false, defs? : linked_definitions)
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 anyof m.each('service'))return any
foreach()
function lm.foreach ( filter : def => true/false | string, visitor : def => {}, defs? : linked_definitions)
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.
filter
/kind
— the filter or kind used to match definitions→ see.each(x)visitor
— the callback functiondefs
— the definitions to fetch in, default:this.definitions
Examples:
// print the names of all serviceslet m= cds.reflect(csn)m.foreach ('service',s => console.log(s.name))
// print the names of all Associations in Books elementlet { Books }= m.entities()m.foreach ('Association',a => console.log(a.name), Books.elements)
LinkedDefinitions
All objects of a linked model containing CSN definitions are instances of this class.
For example, that applies to:
cds.model
.definitions,.services,.entitiescds.service
.entities,.events,.actionscds.entity
.keys,.associations,.compositions,.actionscds.struct
.elements (hence alsocds.entity
.elements)cds.Association
.foreignKeys
Instances ofLinkedDefinitions
allow both, object-style access, as well as array-like access. For example:
let linked= cds.linked (model)let { Books, Authors }= linked.entities// object-likelet [ Books, Authors ]= linked.entities// array-like
Note: Orders of definitions could change, so you should always prefer object destructuring over array destructuring.
The array-like nature also allows using these shortcuts infor..of
loops, of course. Which means, you can do that:
for (let eachof linked.definitions) console.log (each.name)
... instead of iterating definitions usingfor..in
loops like that:
for (let eachin linked.definitions) { let d= linked.definitions [each] console.log (d.name)}
Each entry in an instance ofLinkedDefinitions
is aLinkedDefinition
.
LinkedDefinition
Allcds.linked
definitions are instances of this class, or subclasses thereof. It is accessible throughcds.linked.classes.any
.
. is_linked
A tag property which istrue
for all linked definitions.
. name
The linked definition's fully qualified name as a non-enumerable property.
. kind
The linked definition's resolved kind as a non-enumerable property. One of:
'context'
'service'
'entity'
'type'
'aspect'
'event'
'element'
'annotation'
... as documented in theCSN specification.
instanceof
You can use JavaScript's standardinstanceof
operator in combination with the built-in classes to check a linked definition's type:
let { Foo }= cds.linked(csn).entitiesif (Fooinstanceof cds.entity) console.log ("it's an entity")
cds. service
Allservice definitions in a linked model are instances of this class.
class cds.service extends cds.context {...}
. is_service
A tag property which istrue
for linked entity definitions.
. entities
. events
. actions
These properties are convenience shortcuts to access a service definition's exposedentity,type,event,action orfunction definitions.
Their values areLinkedDefinitions
.
cds. entity
All entity definitions in a linked model are instances of this class.
class cds.entity extends cds.struct {...}
As
cds.entity
is a subclass ofcds.struct
it also inherits all methods from that.
. is_entity
A tag property which istrue
for linked entity definitions.
. keys
. associations
. compositions
. actions
These properties are convenient shortcuts to access an entity definition's declaredkeys,Association orComposition elements, as well asbound action orfunction definitions.
Their values areLinkedDefinitions
.
. texts
If the entity haslocalized elements, this property is a reference to the respective.texts
entity. If not, this property is undefined
. drafts
If draft is enabled, a definition to easily refer todraft data for the current entity is returned.
cds. struct
This is the base class ofstruct elements and types,aspects, andentities.
class cds.struct extends cds.type {...}
. is_struct
A tag property which istrue
for linked struct definitions (types and elements).
It is alsotrue
for linked entity definitions, that is, instances of ascds.entity
.
. elements
The entity's declared elements asdocumented in the CSN Specification
as an instance ofLinkedDefinitions
.
cds. Association
All linked definitions of typeAssociation
orComposition
, including elements, are instances of this class. Besides the properties specified forAssociations in CSN, linked associations provide the following reflection properties...
. _target
A reference to the association's resolved linked target definition.
. isAssociation
A tag property which istrue
for all linked Association definitions, including Compositions.
. isComposition
A tag property which istrue
for all linked Composition definitions.
. is2one / 2many
Convenient shortcuts to check whether an association definition has to-one or to-many cardinality.
. keys
The declared or derived foreign keys. As specified inCSN spec this is aprojection of the association target's elements.
. foreignKeys
The effective foreign keys ofmanaged association as linked definitions.
The value is an instance ofLinkedDefinitions
.
cds. linked .classes
This property gives you access to the very roots ofcds
's type system. When a model is passed throughcds.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 m= cds.linked` entity Books { author: Association to Authors; } entity Authors { key ID: UUID; }`)let { Books, Authors }= m.entitieslet isEntity= Booksinstanceof cds.entitylet keys= Books.keyslet { author }= Books.elementsif (author.is2many)...
mixin()
Provided a convenient way to enhance one or more of the builtin classes with additional methods. Use it like that:
const cds = require ('@sap/cds')// simplistic csn2cdl enablementcds.linked.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 drivelet m= cds.linked` entity Books : cuid { title:String; author: Association to Authors } entity Authors : cuid { name:String; } aspect cuid : { key ID:UUID; }`m.foreach (d => console.log(d.toCDL()))
cds. builtin. types
This property gives you access to all prototypes of the builtin classes as well as to all linked definitions of thebuiltin pre-defined types. The resulting object is in turn like thedefinitions
in aLinkedCSN
.
Actually, at runtime CDS is in fact bootstrapped out of this using coreCSN object structures andcds.linked
techniques. Think of it to be constructed as follows:
cds.builtin.types= cds.linked` using from './roots'; context cds { type UUID : String(36); type Boolean : boolean; type Integer : number; type UInt8 : Integer; type Int16 : Integer; type Int32 : Integer; type Int64 : Integer; 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; type Map : struct; }`.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.linked.classesconst 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.