shorty | synopsis | status |
---|---|---|
cds.reflect |
Find here information about reflecting parsed CDS models in CSN representation.
|
released |
{{$frontmatter?.synopsis}}
[[toc]]
cds. linked (csn) {#cds-linked .method}
Method cds.linked
(or cds.reflect
which is an alias to the same method) turns a given parsed model into an instance of class LinkedCSN
, and all definitions within into instances of class LinkedDefinition
, 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 gets modified, and the returned linked model is actually the modified passed-in csn.
The operation is idempotent, that is, you can repeatedly invoke it on already linked models with zero overhead.
Models passed through cds.linked
become instances of this class.
A tag property which is true
for linked models. {.indent}
The CSN definitions of the model, turned into an instance of LinkedDefinitions
. {.indent}
These are convenient shortcuts to access all service or all entitiy definitions in a model.
The value is an instance of LinkedDefinitions
.
For example:
let csn = CDL`
namespace my.bookshop;
entity Books {...}
entity Authors {...}
service CatalogService {
entity ListOfBooks as projection on Books {...}
}
`
let m = cds.linked (csn)
// Object nature
let { CatalogService, AdminService } = m.services
let { Books, Authors } = m.entities
// Array nature
for (let each of m.entities) console.log(each.name)
// Function nature
let { ListOfBooks } = m.entities ('my.bookshop.CatalogService')
In addition to the object and array natures of LinkedDefinitions
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.
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 d of m.each('entity')) {
console.log (d.kind, d.name)
}
The first argument filter specifies a filter to match definitions, which can be one of:
- a
string
referring to a kind 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 argument defs allows to specify the definitions to fetch in, defaults to this.definitions
.
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
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 any of m.each('service')) return any
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 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)
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 of LinkedDefinitions
allow both, object-style access, as well as array-like access.
For example:
let linked = cds.linked (model)
let { Books, Authors } = linked.entities // object-like
let [ 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 in for..of
loops, of course. Which means, you can do that:
for (let each of linked.definitions) console.log (each.name)
... instead of iterating definitions using for..in
loops like that:
for (let each in linked.definitions) {
let d = linked.definitions [each]
console.log (d.name)
}
Moreover, you can use common array methods like these:
linked.definitions .forEach (d => console.log(d.name))
linked.definitions .filter (d => d.is_entity)
linked.definitions .find (d => d.name === 'Foo')
linked.definitions .some (d => d.name === 'Foo')
linked.definitions .map (d => d.name)
Each entry in an instance of LinkedDefinitions
is a LinkedDefinition
.
All cds.linked
definitions are instances of this class, or subclasses thereof. It is accessible through cds.linked.classes.any
.
A tag property which is true
for all linked definitions. {.indent}
The linked definition's fully qualified name as a non-enumerable property. {.indent}
The linked definition's resolved kind as a non-enumerable property. One of:
'context'
'service'
'entity'
'type'
'aspect'
'event'
'element'
'annotation'
... as documented in the CSN specification.
You can use JavaScript's standard instanceof
operator in combination with the built-in classes to check a linked definition's type:
let { Foo } = cds.linked(csn).entities
if (Foo instanceof cds.entity) console.log ("it's an entity")
All service definitions in a linked model are instances of this class.
class cds.service extends cds.context {...}
A tag property which is true
for linked entity definitions. {.indent}
These properties are convenience shortcuts to access a service definition's exposed entity, type, event, action or function definitions.
Their values are LinkedDefinitions
.
{.indent}
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.
A tag property which is true
for linked entity definitions.
{.indent}
These properties are convenient shortcuts to access an entity definition's declared keys, Association or Composition elements, as well as bound action or function definitions.
Their values are LinkedDefinitions
.
{.indent}
If the entity has localized elements, this property is a reference to the respective .texts
entity. If not, this property is undefined
{.indent}
If draft is enabled, a definition to easily refer to draft data for the current entity is returned. {.indent}
This is the base class of struct elements and types, aspects, and entities.
class cds.struct extends cds.type {...}
A tag property which is true
for linked struct definitions (types and elements).
It is also true
for linked entity definitions, i.e., instances of as cds.entity
. {.indent}
The entity's declared elements as documented in the CSN Specification
as an instance of LinkedDefinitions
. { .indent}
All linked definitions of type Association
or Composition
, including elements, are instances of this class. Besides the properties specified for Associations in CSN, linked associations provide the following reflection properties...
A reference to the association's resolved linked target definition. {.indent}
A tag property which is true
for all linked Association definitions, including Compositions. {.indent}
A tag property which is true
for all linked Composition definitions. {.indent}
Convenient shortcuts to check whether an association definition has to-one or to-many cardinality. { .indent}
The declared or derived foreign keys. As specified in CSN spec this is a projection of the association target's elements. {.indent}
The effective foreign keys of managed association as linked definitions.
The value is an instance of LinkedDefinitions
.
{.indent}
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) ...
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 enablement
cds.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 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()))
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 (CDL`
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;
}
`) .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.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.