July 2020
Welcome to the release of CAP. Find the most noteworthy news and changes in the following sections.Important News and Changes❗️
Streamlined Compiler Switched on by Default
The streamlined compiler implementation is switched on by default. Learn more in the Compiler section below
Refactored Node.js Runtime --- Version 4
Behind the scenes of stable APIs the Node.js runtime implementation has been improved and refactored substantially.
Learn more in the Node.js section below
CLI Commands moved to @sap/cds-dk
To minimize the footprint of the Node.js runtime, close to all CLI commands have been moved out of @sap/cds
and into @sap/cds-dk
now.
Learn more in the CLI section below
OData V2 Officially Supported
From now on, CAP officially supports OData V2 and SAP Fiori Elements UIs based on V2. In the beginning, we use the OData V2 proxy, which we'll integrate intrinsically in the long run to reduce TCO.
Find details in the Serving UIs guide
SAP HANA Cloud Edition Supported Out-of-the-Box
Already since May, CAP provides out-of-the-box support for SAP HANA Cloud Edition. No changes are required to models or code, only a single configuration is to be added → see details in the Databases guide.
New and Revised Guides
- About CAP → received another major overhaul
- Features Overview → adjusted to latest additions
- Cookbook > Authorization and Access Control
- Java > Event Handlers
- Java > Application Services > Result Builder
- CDS > Node.js APIs has been moved here from Node.js reference docs
- Cookbook > Extensibility has been moved here from Advanced
Command Line / Toolkit
CLI Commands Moved to @sap/cds-dk
❗️
As already started with the introduction of @sap/cds-dk
in 2019, we now moved all CLI commands to the -dk
packages, except for run
and serve
, which are used to launch Node.js runtime servers.
Background: We want to drastically strip down runtimes to minimum footprint.
If you already followed the recommendations to install cds-dk
globally and use that in your setups accordingly, this shouldn't affect you at all. Yet, in case you didn't, ensure to follow these steps to avoid pitfalls:
In Java projects, use the new cds-maven-plugin; no other changes are necessary, as this plugin uses
@sap/cds-dk
out of the box.In Node.js projects with existing mta.yaml files, replace all occurrences of
npx cds
withnpx -p @sap/cds-dk cds
in those files.In freestyle CI setups, either (A) install
cds-dk
prior to the execution of build commands, that isnpm i @sap/cds-dk && npm run ...
, or (B) add a dev dependency to your project, for example:npm i @sap/cds-dk --save-dev
.
Note: (B) increases the installation time for everynpm install
call for every developer, so rather prefer option (A).
Streamlined and Modularized cds build
Builds for Node.js and SAP HANA have been optimized for better performance. In addition, we renovated our build infrastructure to allow flexible customization using respective CLI options.
For example:
# choose specific build task cds build --for hana # override task-specific options/defaults: cds build --for hana --opts model=[db,srv,schema.cds] # build with production profile: cds build --production # ...which is a shell-independent equivalent to: NODE_ENV=production cds build # Learn more with: cds build --help
See also how database configuration uses profiles.Learn more about Node.js configuration in general.
Deprecated/Obsolete
cds build/all
→ simply usecds build
instead.cds build --clean
→ is the default now.
Using await
in cds repl
We now support using await
directly on cds repl
prompt inputs. For example:
[demo] cds repl Welcome to cds repl v4.1.5 > m = await cds.load('*')
This feature is provided through Node.js --experimental-repl-await option.
CDS Editors & Tools
The following features are available for all editors based on our language server implementation for CDS in SAP Business Application Studio, Visual Studio Code, and Eclipse. The plugin for Eclipse is available for download at https://tools.hana.ondemand.com.
We Are on VS Code Marketplace --- Finally
Finally, the VS Code extension for the core data services (CDS) language can be installed now from the Visual Studio Marketplace. See Getting Started > Tools for more details. If the extension is already installed and enabled in VS Code, it will be updated automatically.
Welcome Page in VS Code
The VS Code extension now comes with a welcome page, which shows latest information about CAP. It starts automatically once whenever an update arrives for the extension. Later, you can open the page through the command CDS: Show CAP Release Notes
.
Issue Reporting in VS Code
You can now ask questions or report issues directly from VS Code. Just open the Report Issue
dialog, select the VS Code extension, and follow the link to SAP Q&A. Give us as much information as possible in the pre-filled SAP Q&A form to enable better processing of your report.
Editor Support for doc comments (/** ... */
)
Hovering over a definition now displays doc comments, if available:
As well as details for code completion:
Use the doc...
snippet to create a doc comment. You can use markdown to structure a doc comment. Formatting also beautifies doc comments.
An information hint informs you about deprecation of the @cds.doc
annotation. A quick fix allows you to convert a @cds.doc annotation into a doc comment.
You can specify the maximum line length of doc comments using the command CDS: Show Formatting Option Configuration
. On the Other tab, edit the option Max doc comment line length.
For better visibility doc comments are highlighted differently than standard block comments.
QuickFix to Generate using
Statements
You can now type an existing artifact name that isn't yet imported (through a using
statement). A quick fix for the error marker suggests possible artifacts from the workspace. Select the corresponding one to generate the using
statement. The new import is inserted or merged among existing using
statements, sorted alphabetically. The using
statement is formatted.
More CDS Editor Improvements
Semantic Code Completion --- Code completion now also suggests identifiers for elements, enums, actions, and parameters in
annotate
andextend
withinannotate
andextend
statements.Syntax highlighting for identifiers --- Until now identifiers, including variants delimited with
\![...]
, haven't been classified so themes couldn't color them.Related Info in Messages:
CDS Language & Compiler
Streamlined Compiler by Default ❗️
The already mentioned streamlined compiler implementation is now used by default. This new implementation brings significantly improved performance in compilation and bootstrapping servers, as well as reduced memory consumption at runtime.
In the unlikely case that the new default causes problems in your project, you can switch back as follows in your package.json or .cdsrc.json:
"cds": {
"features": { "snapi": false }
}
Note: This fallback option will be offered only for a limited grace period. Make sure to switch to snapi as soon as possible.
Managed Compositions for Improved Domain Modeling
We deliver a major improvement to keep your domain models concise and comprehensible. Use Managed Compositions to nicely reflect document structures, without the need for separate entities, reverse associations, and unmanaged on
conditions. For example:
entity Orders { ...
Items : Composition of many { ...
product : Association to Products;
quantity : Integer;
}
}
Managed Compositions are especially handy for many-to-many relationships:
entity Teams { ...
members : Composition of many { key user: Association to Users; }
}
entity Users { ... }
And here's an example of an attributed many-to-many relationship:
entity Teams { ...
members : Composition of many {
key user : Association to Users;
role : String enum { Lead; Member; Collaborator; }
}
}
entity Users { ... }
Learn more in the CDS Reference Documentations
IMPORTANT: CSN not stable yet. --- Means: While you can safely use Managed Compositions as shown above, don't rely on the current CSN output of for example,
cds compile
for managed compositions, as that may change.
Arrayed Elements in Entities to Avoid Joins
You can now declare arrayed elements in entities using keywords many
or array of
:
entity User { ...
emails: many { kind:String; address:String }
}
Arrayed elements are stored as JSON-stringified values in single NCLOB columns in relational tables. Node.js runtime already supports this generically; Java will follow.
Performance Benefits --- On the one hand, this removes a lot of clutter from your models. On the other hand, and even more important, it greatly improves performance during data access. This results from eliminating a lot of joins to separate entities.
See also: Feature MatrixSee also: Avoid Overly Normalized Models in the Domain Modeling GuideLearn more in the CDS Reference Documentations
Structs and Associations in on
Conditions
We now allow comparing struct elements and managed(!) associations in on conditions of other associations. Have a look at the following example, which is only possible since this release:
entity Project { ...
teams : Association to many Teams
on teams.country = $self.country
and teams.validity = $self.validity;
country : Association to Country;
validity : Validity;
}
entity Teams { ...
country : Association to Country;
validity : Validity;
}
type Validity : { _from:Date; to:Date }
Both operands must be structurally compatible, that is both structures must be expandable to an identical set of leaf paths. Each leaf path must terminate on a builtin CDS scalar type. The original relational term of the form
s1 op s2
is replaced by the resulting expressions1.leafpath_0 op s2.leafpath_0 (AND s1.leafpath_i op s2.leafpath_i)*
with i < n leaf paths.
Enhanced Projection Clauses
You likely noticed already that as projection on
and as select from
are mostly interchangeable alternatives, however the former should be preferred over the latter whenever you don't need the full power of SQL.
This is especially true when defining projections on entities from remote services in service consumption scenarios. However, while in the past the following clauses weren't allowed, they're now:
where
having
group by
order by
limit
For example, provided content of these clauses will be translated to respective query options in OData in Service Consumption Scenarios.
Learn more in the CDS Reference Documentations
Not Null for Parameters and Return Types
Parameters and return types can now also be declared to be (not) null
.
Node.js Runtime Version 4 ❗️
Behind the scenes of given APIs this release provides a broad range of major improvements. We removed inconsistencies, closed gaps and fixed several little glitches. In effect, release 4 is the basis for us to add new features and qualities faster than before, and at the same time gives you more stable and more future-proof APIs.
While public, documented APIs weren't affected, you might nevertheless have written code, which relied on internal interfaces and behaviors, which you should correct accordingly. → see Important Changes below.
We strongly recommend doing so, and to upgrade as soon as possible, as former releases will receive only critical bug fixes from now on.
Important Changes ❗️
The following changes mostly affect undocumented, internal interfaces.
Eliminated Promise
Voodoo : Former versions did quite some magic, meant to ease Promise Hell. With broad availability of async/await
this became useless overhead, removed in release 4. Now, always call cds.connect.to
with await
:
const srv = await cds.connect.to('some-service')
Eliminated next()
Voodoo : Before, the framework called next()
implicitly if an .on
handler returned undefined
. Now, always call next()
explicitly, either with await
, or with return
:
this.on(..., async (req,next)=>{ /* ... */ await next(); /* ... */ })
this.on(..., (req,next)=>{ /* ... */ return next() })
See revised docs for .on
handlers
Fallback: Set config
cds.features.implicit_next:true
to restore former behavior, but correct your handlers, as soon as possible.
Deprecated and Removed
req.user.has(<roles>)
→ usereq.user.is(<role>)
insteadreq.user.<attr>
→ usereq.user.attr.<attr>
insteadin-process-messaging
kind → in-process messaging is always enabled.db.run(()=>{})
→ usecds.run([...multiple queries])
instead.- init.js variant
(db)=>{...}
→ usedb.run([...multiple queries])
instead. - cds.ql
.where('x',1)
or.where('x','=',1)
→ use documented variants only
Important Fixes
$user.<attr>
===undefined
erroneously evaluated totrue
→ now it'sfalse
$metadata
requests erroneously got authorization-checked → not anymore@restrict
did permit all operations not mentioned, for example, the following annotation allowed all other operations, like UPDATE or DELETE, for every user:
@restrict:[ {grant:'READ',to:'some-role'} ]
GET /Foo(1)/bar
erroneously translated to semi joins → now correctly yields:
{SELECT:{from:{ref:[ { id:'Foo', where:[{val:1}] }, 'bar' ]}}
Refactored and Enhanced Core Services API
These enhancements are the main effects of the refactorings. They close many API gaps and glitches, and provides us with a clean core, for the things to come...
Classes cds.Service
, cds.Request
, and cds.Event
cds.Service
is the common base class for all connected or provided services.- Instances of
cds.Request
represent synchronous requests. - Instances of
cds.Event
represent asynchronous event messages.
New Service and Request Methods
cds.emit
for both, synchronous requests, and asynchronous event messages.req.warn,info,notify
allow to return messages with different severity- HTTP-style convenience methods complementing the CRUD-style ones.
Learn more about the refactored Core Services APIs
Streamlined, Extensible Bootstrapping by cds.server
The bootstrapping process has been greatly optimized, in particular by loading only one consolidated effective model, instead of many per service independently. In effect, both, bootstrapping times, as well as memory consumption improve by magnitudes, especially with larger projects.
In addition, the built-in server.js
implementation has been redesigned, and allows to plug-in custom logic, without loosing the convenience of cds watch
et al:
// custom server.js
const cds = require('@sap/cds')
cds.on('bootstrap', (app)=>{ /* add your own middleware */ })
cds.on('served', ()=>{ /* add more middleware */ })
module.exports = cds.server //> delegate to default server.js
Learn more about cds.server
-based bootstrapping
IMPORTANT: In case you're using own
server.js
implementations, we strongly recommend revisiting these and considering refactoring them to extend the built-in one, as documented in customserver.js
. This ensures you benefit from the mentioned and future optimizations.
Custom cds.Service
Subclasses
Besides providing impl functions, service implementations can now provide specific subclasses of cds.Service
, as shown below:
// srv/cat-service.js
const cds = require('@sap/cds')
class CatalogService extends cds.ApplicationService {
async init(){
this.on ('UPDATE','Books', (req)=>{...})
await super.init()
this.before ('CREATE','Books', (req)=>{...})
}
}
module.exports = CatalogService
Common Service Factory : This is one of many more effects of the new service factory, commonly used by cds.serve
and cds.connect
now. This applies the same consistent ways to register new implementations via cds.requires
options, model annotations @impl
and @kind
, or the well-known .cds/.js sibling files mechanism.
Learn more about new ways of Providing Service ImplementationsLearn more about new ways of Configuring Required Services
Custom Handlers on cds.connect
ed Services
As a CAP Node.js developer, you're used to add event handlers to provided services to add custom-specific implementations to the service. Starting with this release, you can also do the same from the consumer side to services you connected to, such as Database Services or Remote Services:
const db = await cds.connect('db')
db.prepend (()=>{
db.on ('INSERT','Orders', req => {...})
db.after ('READ','*', each => {...})
})
Learn more about Adding Handlers to cds.connect
ed Services
For example, you could use that to fix gaps in our generic support, by providing your own generic handlers to our built-in database services for SQLite and HANA.
With this, we make these statements in the About CAP page even more true:
- Every active thing in CAP is a service, that is a
cds.Service
, and... - Everything happens in response to events, that is in Event Handlers.
All service implementations essentially are just a bunch of registered event handlers. There's actually no difference between handlers added from provider side, or from consumer side. There's also no difference between the handlers we add to provide all the generic framework features of CAP, or the handlers you add. At the end of the day, we all eat the same dog food --- with this release more than ever before.
Custom and Dummy Authentication Strategies
New Dummy Authentication Strategy : The dummy strategy creates a dummy user that passes all authorization checks. It's meant for temporarily disabling the @requires
and @restrict
annotations at development time.
Custom Authentication Middleware : In case the supported authentication strategies don't fulfill the necessary requirements, developers can provide a custom express middleware for authentication.
Learn more about the revamped Authentication framework
INSERT Queries Return Generated Keys
INSERT queries now return iterables, similar to results from SELECT queries, which allow to reflect on (generated) keys of the inserted entities, as shown in this snippet.
const [ Emily, Charlotte ] = await INSERT.into (Authors,[
{name:'Emily Brontë'},
{name:'Charlotte Brontë'},
])
INSERT.into(Books).columns('title','author_ID').rows(
[ 'Wuthering Heights', Emily.ID ],
[ 'Jayne Eyre', Charlotte.ID ],
)
Find details in the updated API docs of cds.run
Generic Support for Arrayed Elements
See Arrayed Elements in the CDS Language section
Localized Messages / i18n
Application developers can now use localized error messages. For example:
# i18n/messages_en.properties
ORDER_EXCEEDS_STOCK=The order of {0} books exceeds the stock by {1}
req.reject( 400, 'ORDER_EXCEEDS_STOCK', [
order.quantity,
order.quantity - book.stock
])
Learn more about Localized Messages
Miscellaneous
New
srv.after('each', row => ...)
— the former technique to register per-row handlerssrv.after('READ', each => ...)
broke when code was minified. The new method using pseudo event'each'
is minifier-safe.New
srv.prepend(srv => ...)
— usesrv.prepend(...)
to register event handlers to be executed before the already registered handlers. For example, extensions of reused implementations sometimes need to use this.New
req.user.tenant
to access the current user's tenant ID.Reflect
srv.events
— base classcds.Service
provides a new gettersrv.events
to reflect on declared events in the service definition, similar to the already existingsrv.entities
,srv.types
, andsrv.operations
.Experimental
cds.ql(req)
— event handlers can now use the like ofconst {SELECT} = cds.ql(req)
to ensure transaction-managed and tenant-isolated execution of queries, instead ofsrv.tx(req)
. Note though, that this is an experimental feature, which might change or be removed in future versions.Switch on debug logs by setting the environmental variable
DEBUG
toy
if you want to show more detailed log output.
More to Come Soon
As stated above, this major version update was just the start, setting the stage for new features with a greatly cleaned up implementations of our Node.js runtime's core.
In the documentation, you already find outlines, of not-yet documented or rarely filled-in guides and APIs, such as:
We didn't document and release these APIs right now, as we want to give them some more polishing before general availability.
Stay tuned for more to come in there.
Java Runtime
Important Changes for Former Java Stack ❗️
A minimum version 1.40.6
of the former CAP Java runtime is required.
Check your project's pom.xml
and bump projects-parent-odatav2 or projects-parent to the latest version. We recommend doing this regularly.
Also, to benefit from new features, see our migration guide for the new CAP Java SDK.
Important Changes ❗️
- The API
CdsRuntime.runInRequestContext()
has been deprecated and replaced byCdsRuntime.requestContext().run()
. - The API
CdsRuntime.runInChangeSetContext()
has been deprecated and replaced byCdsRuntime.changeSetContext().run()
. - Some error codes were incorrect and have been corrected: The codes ranging from
40008
to40017
have been changed to400008
to400017
respectively, to prevent conflicts in error codes in the future. - The method
ServiceException.getLocalizedMessage()
can't be used anymore for non-OData requests. Hence, if no activeRequestContext
object exists, use the new methodServiceException.getLocalizedMessage(Locale)
to explicitly set a locale. The requests locale can be obtained from the CdsRuntime usingruntime.getProvidedParameterInfo().getLocale()
. - XSUAA user names aren't normalized to lower case by default anymore (property
cds.security.xsuaa.normalizeUserNames
) because XSUAA delivers stable case-sensitive user names as of now.
Modeling
- Support managed associations that don't target
key
elements- Support using
$now
inon
conditions of unmanaged associations
- Support using
Code Generation
EventContext
interfaces for eventscreate
method for consumption interfaces for structured types
CDS Data Store
- Managed Data
- handle
@cds.on.insert
|update
annotations on data store level to set the values of current user ($user.id
), session timestamp ($now
) or a literal value - support managed data also upon deep insert or deep update
- handle
- support using the session context variable
$now
,$at.from
,$at.to
,$user.id
, and$user.locale
in filters
New RequestContext API
A new RequestContext API enables accessing and modifying request metadata like user information, query parameters, and HTTP headers.
In addition, a new Provider API enables applications to control how user or request parameter information is derived from the request.
Parametrized Views
Read requests on Parameterized views are now supported in OData V4. See section CDS > Definition Language > Exposed Entities for more details.
OData Features
The following new OData V4 features have been added:
- Updates on OData singletons and navigations from singletons to other entities are now supported.
- The expression
not
is now supported in$filter
requests - The
$metadata
endpoint now supports proper browser caching by setting the http headerCache-Control: max-age=0
.- Data Aggregation (
$apply
) - support for one level of data aggregation
- using transformations
groupby
,aggregate
,filter
,search
andidentity
- using the standard aggregation methods
sum
,min
,max
,average
andcountdistinct
- ordering by aggregated values
- using
$count=true
together with
- Data Aggregation (
Fiori Drafts
You can now use field from draft enabled entities in orderBy
clauses.
Miscellaneous
- Event handler methods can now use the types
CdsData
,List<CdsData>
, orStream<CdsData>
for method arguments. - Support multiple bound XSUAA service instances --- The application configuration property
cds.security.xsuaa.serviceName
now controls which XSUAA service binding is chosen for authorization.
Bug Fixes
- For an OData query with
$count
option, the inline count must be specified in the result. In the previous version, if no inline count was contained in the result, the row count was used, which could have resulted in wrong data. - Fixed a bug that caused queries using
COUNT(*)
, for example, OData's/$count
to fail on SAP HANA databases. - Fixed a bug that caused PUT requests and actions to fail on Etag-enabled entities on SAP HANA.
- Fields annotated with
@cds.on.insert
are now preserved in the draft when editing a draft enabled entity.
Performance
- Improved performance when querying a draft enabled entity.
- Suppress additional query for inline count if possible
- Reduce number of queries when expanding to-one associations
Multitenancy
Building Multitenant SaaS Applications with CAP
Create cost-efficient SaaS applications with CAP and SAP HANA Cloud by leveraging the multitenancy concepts of SAP Cloud Platform and the new Service Management instance management capabilities.
The cds-mtx
library offers you out-of-the-box support to switch your single tenant scenario to multitenant persistency with tenant data separation using HDI containers and to integrate it with the SAP Cloud Platform SaaS Manager. See [Multitenancy] for more details.