March 2022
Welcome to the release of CAP. Find the most noteworthy news and changes in the following sections.Database Integrity Constraints
CDS can now automatically generate native database referential integrity constraints for managed to-one Associations and Compositions:
entity Books {
...
author : Association to Authors;
}
entity Authors {
key ID : Integer;
...
}
Association author
triggers the generation of a constraint:
CONSTRAINT Books_author ON Books
FOREIGN KEY(author_ID) REFERENCES Authors(ID)
ON UPDATE RESTRICT
ON DELETE RESTRICT
VALIDATED
ENFORCED
INITIALLY DEFERRED
Database constraints are available for SQL dialects hana
and sqlite
.
Switch them on with the configuration cds.env.features.assert_integrity
that can have the values:
'db'
: Database constraints'app'
: Runtime checks (default, only effective in Node.js runtime)false
: No database constraints and no runtime checks
The Node.js runtime features integrity checks in the application. We intend to replace these rather expensive checks in the application by database constraints with the next major release in 2022. For the migration period until then you can choose what kind of integrity checks are performed.
Generation of database constraints is of course also possible on the CAP Java stack, where no runtime checks are available.
Learn more about Database Constraints.
Native Database Clauses
Using the annotations @sql.prepend
and @sql.append
, you can add arbitrary SQL snippets to the DDL statements that are generated by the compiler. This allows you to use database features that are not natively supported by CDS.
Model:
@sql.append: ```sql
GROUP TYPE foo
GROUP SUBTYPE bar
```
entity E {
...,
@sql.append: 'FUZZY SEARCH INDEX ON'
text: String(100);
}
Result:
create table E (
...,
text nvarchar(100) FUZZY SEARCH INDEX ON
) GROUP TYPE foo
GROUP SUBTYPE bar
If you use native database clauses in entities where schema evolution has been enabled using annotation @cds.persistence.journal
, see Schema Evolution Support of Native Database Clauses.
❗ Warning
The compiler doesn't check or process the provided SQL snippets in any way. You are responsible to ensure that the resulting statement is valid and doesn't negatively impact your database or your application. We don't provide support for problems caused by using this feature.
Learn more about Native Database Clauses.
Native HANA Functions with non-standard syntax
CDS now supports SAP HANA aggregate functions with an additional order by
clause in the argument list, like:
first_value(name order by price desc)
Learn more about Native SAP HANA Functions.
CDS Language
Simplified Syntax for Extending or Annotating Elements
Until now, for annotating or extending elements of an entity, you had to "repeat" the relevant part of the entity definition:
annotate Foo with {
nestedStructField {
existingField @title:'Nested Field';
}
}
extend Foo with {
extend nestedStructField {
newField : String;
}
}
This is a bit cumbersome if you only want to annotate or extend single elements. You can now directly address the element you want to annotate or extend:
annotate Foo:nestedStructField.existingField with @title:'Nested Field';
extend Foo:nestedStructField with { newField : String; }
Learn more about annotate. Learn more about extend.
Default in Type Definitions
A default value can now be specified also in scalar custom type definitions:
type CreatedAt : Timestamp default $now;
Node.js SDK
Important Changes ❗️
Fixed: Keys of an entity were always fetched in addition. This does not happen anymore. Example:
jslet { CatalogService } = cds.services let books = await CatalogService.read('title').from('Books')
→ formerly this returned:
jsbooks = [{ ID:201, title:'Wuthering Heights' }, ...]
→ now it is:
jsbooks = [{ title:'Wuthering Heights' }, ...]
If you need the keys in the result, make sure you request them explicitly in the query as well.
Driver-Agnostic Results for Stored Procedures
We added a driver-agnostic way for SAP HANA procedure calls with table output data.
Example:
// P1 -> table output, P2 -> primitive output, P3 -> input
> await cds.run(' CALL PROC(P1 => ?,P2 => ?,P3 => ?)', 42)
{
P1: [...]
P2: 4711
}
Java SDK
Important Changes ❗️
Elements with type UUID
that are annotated with @odata.Type:'Edm.String'
are not normalized anymore. The comparison is case sensitive and arbitrary values can be stored (example).
Relaxed Deep Insert/Update
If the data of deep Insert or deep Update contains values of an associated entity but the (forward mapped) association does not cascade the Insert/Update operation, only the association itself is updated, but not the values of the associated entity.
Assumed you have the following model:
entity Orders {
key id : UUID;
book : Association to Books; // not cascading
}
entity Books {
key id : Integer;
title : String;
}
In addition, you run this code:
Map<String, Object> order = Map.of("book",
Map.of("id", 17, "title", "Capricorn"));
CqnInsert insert = Insert.into("Orders").entry(order);
db.run(insert);
This creates a new order and associates this order to the book with id
17. But it doesn't create the book.
Collectors for AND and OR
New methods CQL.withAnd()
and CQL.withOr()
allow to use a Collector
to connect a stream of CQN predicates with AND
or OR
:
List<Map<String, Object>> values = ...
Stream<CqnPredicate> predicates = values.stream().map(CQL.matching);
CqnPredicate filter = predicates.collect(CQL.withOr());
Learn more about Connecting Streams of Predicates
Additional Values for Managed Data
The annotations @cds.on.insert
and @cds.on.update
now support additional values. The special references $user.locale
and $user.tenant
allow to automatically set respective tenant or locale from the user info.
The value $uuid
can be used to automatically set a generated UUID value.
The following snippets are equivalent:
entity Orders {
@cds.on.insert : '$uuid'
id : String;
}
entity Orders {
@odata.Type : 'Edm.String'
id : UUID;
}
Security & Compliance
Default Mock Users to Ease Testing
In case mock user security configuration is active, default mock users reflecting pseudo roles are available by default now. They are named authenticated
, system
, and privileged
and can be used with an empty password. For instance, requests sent during a Spring MVC unit test with annotation @WithMockUser("authenticated")
will pass authorization checks that require authenticated-user
.
Learn more mock user authentication.
Improved Propagation of Authentication Information
The AuthenticationInfo (for example, storing a JWT) can now be accessed from the RequestContext and EventContext and is provided as a Spring bean:
@Autowired AuthenticationInfo authInfo;
...
JwtTokenAuthenticationInfo jwtTokenInfo = authInfo.as(JwtTokenAuthenticationInfo.class);
String jwtToken = jwtTokenInfo.getToken();
It is also propagated to child threads when propagating the RequestContext
.
Learn more in Reading Request Contexts.
Fine-Grained Control of Outbox Usage
Added properties cds.auditlog.outbox
and cds.messaging.services.<key>.outbox
that control the usage of the Outbox for Auditlog/Messaging events.
Learn more about CDS Properties.
Misc
@Core.ContentID
is now present on OData V4 error responses. For batch requests this allows to relate the error messages of an OData change set to the individual request that causes the error.When using SAP HANA Cloud, you can now enable a shared connection pool using property
cds.multiTenancy.dataSource.combinePools.enabled
without having to specify all database instances using propertycds.multiTenancy.dataSource.hanaDatabaseIds
.Added integration with Cloud SDK's RequestHeaderFacade ensuring HTTP headers are propagated to Cloud SDK.
The goal install-cdsdk of the cds-maven-plugin provides the new parameter
arguments
to pass additional arguments to the command line.
Preview on Save in VS Code
The preview for a .cds file is now automatically refreshed each time you save the corresponding .cds file.
You can disable this behavior with setting
Cds > Preview: Refresh On Save
.
Simplified Deploy Guides
We have reworked and simplified the Deploy and Operate guides. Most notably, the Deploy to Cloud Foundry and Multitenancy cookbooks are much simpler to execute, as they make use of the project facets below.
Improved cds add <facets>
We've enhanced the CAP project facets to ease application setup for various configurations and deployment scenarios:
cds add xsuaa
prepares for JWT authentication via XSUAA.cds add mtx
configures your application for SaaS deployments.cds add approuter
allows for serving your application's UI using SAP App Router.cds add kibana-logging
sets up your application for the BTP logging service and the Kibana dashboard.cds add mta
creates the mta.yaml deployment descriptor as before, but in addition, the other templates update mta.yaml if needed.
The --for <profile>
parameter allows to specify the Node.js configuration profile. For example, cds add mtx --for production
only adds multitenancy configuration to the [production]
profile.
Take a look at the revised deploy guides to see the facets in context.
Names Check in cds lint
A new lint rule warns about entities and elements that start with $
.
Such names may conflict with reserved variables that also start with $
(like $self
).
Security in Multitenancy
Tokens downloaded to the command line client are now reduced in scope. That means, they only contain those scopes necessary for client operation. This follows the principle of least privileges and aims to reduce the risk of token misuse.
The optional ExtendCDSdelete
scope is not part of the token unless the user is assigned that scope. If the ExtendCDSdelete
scope is not assigned when activating a new extension, requests to delete a previous version of an extension are rejected.
If the user's privileges change, for example, by assigning the ExtendCDSdelete
scope via a role collection, the user has to run cds logout
and then cds login
. This fetches a token containing the extended set of scopes.