December 2021
Welcome to the release of CAP. Find the most noteworthy news and changes in the following sections.Exists Predicates in CQL
This release adds thorough support for exists
predicates with path expressions, to CDS compiler, as well as Node.js and Java runtimes. Exists path predicates allow to express complex relationships while hiding the complexity of nested subselects. For example:
SELECT FROM Authors WHERE exists books.pages[wordcount > 1000]
Learn more about the Exists Predicates in CQL.
Exists predicates can also be nested. The previous example is equivalent to:
SELECT FROM Authors WHERE exists books [
where exists pages [
where wordcount > 1000
]
]
WARNING
Paths inside infix filters aren't yet supported.
Support in CDS Compiler
The CDS compiler translates this into plain SQL like that:
SELECT FROM sap_capire_bookshop_Authors a
WHERE EXISTS (
SELECT 1 from sap_capire_bookshop_Books b
WHERE b.author_ID = a.ID and EXISTS (
SELECT 1 from sap_capire_bookshop_BookPages p
WHERE p.book_ID = b.ID
)
)
Support in Runtimes
The EXISTS
predicate is also supported by the CAP Node.js and CAP Java runtimes. In CAP Java EXISTS
is expressed by the anyMatch
predicate. In an OData request a Lambda Operator is used.
Instance-based Authorization
The EXISTS
predicate is especially useful when enforcing instance-based access control:
@restrict: [{ grant: 'READ',
where: 'exists teams.members [userId = $user and role = `Editor`]'
}]
entity Projects {
// ...
teams : Association to many Teams;
}
Learn more about Exists Predicate in the Authorization guide.
In the example, only those users may read projects' data, which are associated members with role Editor.
Persistent Outbox beta
The persistent outbox combines asynchronism with resilience. It allows to defer the emit of messages until the success of the current transaction, to avoid that recipients falsely receive messages in case of a rollback.
In addition, the persistent outbox safely stores the messages in the used persistence, so that they are not lost in case an application crashes. The deferred emits will then be done asynchronously. In case the emit is currently not possible, for example because of a temporary unavailability of the targeted service, it will be retried with an exponentially growing waiting time.
Currently, the persistent outbox can be enabled to be used with messaging and audit logging services.
See Java - Persistent Outbox or Node.js - Persistent Outbox for more details.
Resilient Audit Logging via Outbox beta
The out-of-the-box audit logging makes use of the new persistent outbox, if enabled. See Persistent Outbox for more details. That is, logs that shall be sent to the audit log sink are first written into the persistent outbox, which is transactionally safe, and only sent to the audit log sink once the original transaction was committed. This ensures that there are no "ghost" logs (for example, modifications that eventually were not committed) or lost logs (for example, modifications that were committed but not logged because there was an issue in between).
Declarative Audit Logging in Java
Audit logs of type "Personal data access" and "Personal data modification" are now automatically created in case the model is annotated with @PersonalData
and @AuditLog
as described in section Audit Logging:
annotate Authors with
@PersonalData : { EntitySemantics : 'DataSubject'}
@AuditLog.Operation : { Read : true, Update : true }
{
ID @PersonalData.FieldSemantics : 'DataSubjectID';
name @PersonalData.IsPotentiallyPersonal;
}
In the example, modifying the field Authors.name
generates a log event of type DataModificationLog
.
For more information, see the respective section for Java.
Database Constraints beta
CDS can now automatically generate database 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.
SQL Window Functions
CQL now supports the syntax for SQL window functions:
select from Foo {
...,
sum(x) over (partition by a order by b
rows between unbounded preceding and current row) as s
}
No semantic checks are performed for the window functions, they are simply translated to SQL. Consult the documentation of your database for more information about the supported window functions.
Learn more about representing SQL window functions in CXN.
GraphQL Schema Using CLI (experimental)
The GraphQL schema used in the experimental GraphQL Adapter can be generated stand-alone via cds compile -2 gql
.
Node.js Runtime
Important Changes ❗️
From this release on, debug logs of cds.DatabaseService
and cds.RemoteService
have sanitized values in production. The sanitization can be deactivated:
cds.env.log.sanitize_values = false
TIP
In production, it's strongly recommended to use log level info
or higher.
Generic Providers
The media type of a stream property is now updated based on the content type header of the PUT .../<stream property>
request. With this, only one request is needed to update both, the media data and the media type.
Further, the Node.js runtime now supports filtering based on the count of records in a collection. For example, you can get all hard-working authors via GET /Author?$filter=books/$count gt 10
.
Developer Experience
For the December release, we added some improvements with regard to developer experience.
Typically, running with mocks in production isn't sensible and, hence, the command line option
--with-mocks
was ignored. However, there may be some scenarios during development that would benefit from this option.cds.env.features.with_mocks = true
now allows--with-mocks
in production.Authorization-related local development was tricky if the respective (mock) user had a tenant other than
anonymous
, as, by default, a new, empty SQLite database would have been created duringcds watch
. Now, in single tenant mode, the default SQLite database is used regardless ofcontext.tenant
.The npm module
passport
is no longer required for authentication strategiesdummy
andmock
. See Authentication Strategies for more details.Requests to remote services are logged (similar to the debug logs for executed SQL statements) if the log level for module
remote
is set todebug
. See Module Names Used by the Runtime for more details.
Java SDK
Important Changes ❗️
Use Double Star (**
) to Exclude Paths
To exclude
complete namespaces from code generation with the CDS Maven Plugin, now **
placeholder must be used. Adjust the configuration in the pom.xml
accordingly.
Configure DB Connection Properties in MT Scenario
Pool configurations of multitenant database bindings now support map-based parameters. This enables additional connection properties to be specified. For Hikari connection pool, you can use cds.datasource.<binding-name>.hikari.data-source-properties.<key>: <value>
.
For instance, to configure the packet size of connections created by SAP HANA JDBC driver, add following configuration to your application.yaml file:
cds:
datasource:
<binding-name>:
hikari:
data-source-properties:
packetSize: 300000
Learn more about datasource configuration.
CSV Import of Array-Typed Elements
Values for elements with arrayed types can now be specified in CSV files for test data import. The values need to be presented as JSON arrays as shown in the following example:
entity Samples : cuid {
records : array of {
index: Integer;
flag: Boolean
}
}
A CSV file Samples.csv
in folder db/data
containing a single entity instance to be imported could look like this:
ID;records
08[...]15;[{"index": 1, "flag": true}, {"index": 2, "flag": false}]
Learn more about providing initial data.
OData
Expose a Service with OData V2 and V4 in Parallel
In CAP Java, you can expose a service with OData versions V2 and V4 in parallel. Formerly this wasn't possible when using the MTX sidecar. This limitation has now been lifted.
Remote Service Consumption
You can now configure Cloud SDK's destination retrieval strategy and the token exchange strategy for a Remote Service by setting cds.remote.services.<key>.destination.retrievalStrategy
resp. cds.remote.services.<key>.destination.tokenExchangeStrategy
.
CQL Runtime
Null Values in Results
Clarification: The result row of a query execution may contain null
values. Therefore, the containsKey
method is not appropriate to check if a value is present in a result row and isn't equal to null
.
Aliased Expands
Expands in CQL Select
statements can now have an alias:
CqnSelect select = Select.from(BOOKS).columns(
b -> b.author().as("writer").expand()).byId(101);
Row book = dataStore.execute(select).single();
Object writer = book.get("writer.name"); // path access
Associations on the Select List
Managed to-one associations and compositions can now be added to the select list of Select
statements.
CqnSelect select = Select.from(BOOKS).columns(
b -> b.author()).byId(101);
Row book = dataStore.execute(select).single();
Object authorId = book.get("author.Id"); // path access
Reflection API: get/find Elements by Path
When using the Reflection API, you can now specify a path to reflect on elements of a structured type or of an associated entity. Considering the following model:
entity People {
key id : UUID;
name : { first : String; last : String; };
car : Composition of one { brand : String; color : String; };
}
The following snippets are equivalent.
Long version:
@Autowired
CdsModel model;
CdsEntity people = model.getEntity("People");
CdsElement name = people.getElement("name");
CdsStructuredType nameType = name.getType().as(CdsStructuredType.class);
CdsElement firstName = nameType.getElement("first");
CdsElement car = people.getElement("car");
CdsStructuredType carType = car.getType(CdsAssociationType.class).getTarget();
Optional<CdsElement> colorOfCar = carType.findElement("color");
New short version:
@Autowired
CdsModel model;
CdsEntity people = model.getEntity("People");
CdsElement firstName = people.getElement("name.first");
Optional<CdsElement> colorOfCar = people.findElement("car.color");
CDS Maven Plugin: Include/Exclude Definitions
The CDS Maven Plugin now allows to selectively include and exclude definitions during code generation. With the following configuration of the generate
goal, Java artifacts are generated for the definitions of the CatalogService
but not for definitions of the namespace localized
:
<plugin>
<groupId>com.sap.cds</groupId>
<artifactId>cds-maven-plugin</artifactId>
<version>${cds.services.version}</version>
<executions>
<execution>
<goals>
<goal>generate</goal>
</goals>
<configuration>
<includes>
<include>CatalogService.**</include>
</includes>
<excludes>
<exclude>localized.**</exclude>
</excludes>
</configuration>
</execution>
</executions>
</plugin>
New Lint Rule for CSV Files
A new CDS lint rule validates header lines of CSV files for whether the columns match to CDS entities. In addition, quick fixes suggest the correct names in VS Code:
To get the VS Code integration for this rule, run
cds add lint
once in your project.
Hybrid Testing: K8s and Java
You can bind to Kubernetes service bindings and secrets and run your CAP Java applications with bindings using cds bind --exec
now. Examples for important use cases have been added to the hybrid testing guide.
CDS Editor Performance Optimizations
The following applies to CDS editors in SAP Business Application Studio and Visual Studio Code.
The editor is now faster at startup and requires less memory.
Progress is now indicated when configuration was changed, and during references and workspace symbols.