Skip to content
On this page

March 2023

Welcome to the notes for the March 2023 release for the CAP framework. Find the most noteworthy news and changes in the following sections.

Calculated Elements (beta)

Elements can be specified with a calculation expression, in which you can refer to other elements of the same entity. There are different flavors of calculated elements, which are provided as a series of features in the next releases. As a first part, this release includes calculated elements with a value expression with "on-read" semantics.

cds
entity Employees {
  key ID : UUID;
  firstName : String;
  lastName : String;
  name : String = firstName || ' ' || lastName;
}
entity Employees {
  key ID : UUID;
  firstName : String;
  lastName : String;
  name : String = firstName || ' ' || lastName;
}

These calculated elements essentially are a convenience feature. They serve as a "predefined" calculation expressions that can be used in views or projections on top of the entity instead of writing the expressions there.

cds
service Srv {
  entity EmployeesWithName as projection on Employees { ID, name };
}
service Srv {
  entity EmployeesWithName as projection on Employees { ID, name };
}

A request to read name of EmployeesWithName returns the concatenated name.

Using such a calculated element in a view/projection is equivalent to writing the expression directly into the select list, both with respect to semantics and to performance. The definition above is equivalent to

cds
service Srv {
  entity EmployeesWithName as projection on Employees {
    ID,
    firstName || ' ' || lastName as name : String
  }
}
service Srv {
  entity EmployeesWithName as projection on Employees {
    ID,
    firstName || ' ' || lastName as name : String
  }
}

WARNING

Currently, a calculated element in an entity can't be accessed directly in an OData request or in custom code. It must always be accessed via a view/projection, as shown in the example.

Learn more about Calculated Elements.

New cds-plugin technique

Both @sap/cds and @sap/cds-dk now provide a simple way for plugins: It searches in the projects dependencies for packages with a module cds-plugin.js. If that exists it will be loaded. Here's an example usage of that concept in our GraphQL adapter implementation:

js
// cds-plugin.js
const cds = require('@sap/cds')
const protocols = cds.env.protocols ??= {}
if (!protocols.graphql) protocols.graphql = {
  path: "/graphql", impl: "@cap-js/graphql"
}
// cds-plugin.js
const cds = require('@sap/cds')
const protocols = cds.env.protocols ??= {}
if (!protocols.graphql) protocols.graphql = {
  path: "/graphql", impl: "@cap-js/graphql"
}

Within that module you can basically plug in to everything, similar to what projects can do in local server.js files.

Main benefit of that is that adding a feature, like graphql in this case, to a project just requires a simple npm add, with no additional configuration required:

sh
npm add @cap-js/graphql
npm add @cap-js/graphql

New SQLite Service (beta)

We have completely rebuilt our SQLite Database Service with a streamlined design based on a new database services architecture and the better-sqlite3 driver. The most noteworthy improvements from users' perspective are:

  • Full support for path expressions, including infix filters, and exists predicates. For example, queries like that are possible now:

    js
    SELECT.from (Books, b => { b.ID, b.title, b.author.name }) .where ('genre.name=','Drama')
    SELECT.from (Books, b => { b.ID, b.title, b.author.name }) .where ('genre.name=','Drama')
    sql
    SELECT `from ${Authors} {
       ID, name, books[where title like '%a%']{ title }
    }`
    SELECT `from ${Authors} {
       ID, name, books[where title like '%a%']{ title }
    }`
  • Standard functions like contains, concat, startswith, ... are now supported in database-agnostic ways.

  • Common SAP HANA functions like days_between, months_between, years_between are supported to enhance testing for SAP HANA-based production scopes.

  • Deep Read queries with expands are executed as single native database queries using json_object functions.

So overall, feature scope has been greatly enhanced, as well as quite some performance improvements. For example, in combination with lean draft we saw this for displaying the initial list page of Travels in cap/sflight:

Old: >250ms

image-20230402140522007

New: ~15ms

image-20230402140543488

WARNING

IMPORTANT: This is a beta feature, which is available already now to get early feedback from you. All the Important Disclaimers and Legal Information apply!

The new service will become the default for SQLite in the upcoming major release.

Find more information, in particular instructions about how to use it in the README of the @cap-js/sqlite package npmjs.com.

Lean Draft (beta)

We have revamped our implementation for draft-enabled entities. While the old implementation was spread all over our code base, the new one is implemented in a modular, non-intrusive fashion.

Benefits for draft usages are more intuitive handler registrations and clear distinction of the request target (active or draft instances) within the custom handlers, as illustrated in this sample:

  • List status: All triggers READ requests to the active and draft entities.

    js
    srv.on('READ', '<entity>', /* handle only active instances */)
    srv.on('READ', '<entity>.drafts', /* handle only draft instances */)
    srv.on('READ', '<entity>', /* handle only active instances */)
    srv.on('READ', '<entity>.drafts', /* handle only draft instances */)
  • EDIT triggers a READ on the active instance followed by a CREATE on the draft instance.

Non-intrusive means there's no support for draft in the new database services anymore, hence, New SQLite Service requires lean draft. So it's automatically switched on when using new SQLite service, you can also switch it on for old db services with:

jsonc
{ ...,
  "cds": {
    "fiori": {
      "lean_draft": true
    }
  }
}
{ ...,
  "cds": {
    "fiori": {
      "lean_draft": true
    }
  }
}

As a reference, we've created a branch in SFlight, that shows some of the changes. Simply start cds watch --profile lean-draft to see it in action or cds watch --profile lean-draft,better-sqlite to also use the new SQLite Database Service.

We've also implemented a compatibility mode which mitigates most of the changes. It can be enabled via compatibility flag cds.fiori.draft_compat = true. The SFlight main branch works without any changes when started with cds watch --profile lean-draft,draft-compat.

WARNING

IMPORTANT: This is a beta feature, which available already now to get early feedback from you. All the Important Disclaimers and Legal Information apply!

More details will follow soon.

CAP Security Guide

"How can we develop and run a CAP application in a secure manner?"

"What has CAP in store to protect against typical attack patterns?"

"What is expected to meet production-level security requirements?"

"How are platform security services involved?"

These are typical questions asked by developers, operators, and security experts. The new CAP Security Guide addresses all of these questions and helps you to find your way around the challenging topic of security.

image-20230404174932619

CDS Language and Compiler

Ternary Conditional Operator

Use the new ternary conditional operator <cond> ? <expr> : <expr> to improve the readability of your sources.

This operator is only available as syntactical variant on CDL level. In CSN, it is represented by the same token stream as the corresponding CASE expression.

Example:

swift
SELECT FROM Books {
  (stock>100 ? 0.3 : 0.1) as discount_ternary,
  CASE WHEN stock>100 THEN 0.3 ELSE 0.1 END as discount_case
}
SELECT FROM Books {
  (stock>100 ? 0.3 : 0.1) as discount_ternary,
  CASE WHEN stock>100 THEN 0.3 ELSE 0.1 END as discount_case
}

A ternary conditional operator must always be enclosed in parentheses.

Node.js

Important Changes ❗️

Changed Behavior for Plain SQL Queries

Plain SQL queries now have req.event === undefined, formerly this had nondeterministic values. If you require to intercept plain SQL queries, you can register a custom handler using event *. This change also affects the return of plain SQL INSERT queries that formerly returned an InsertResult. From now on, they return the database driver result.

Change your start script to cds-serve

With the next major version, the cds executable of @sap/cds will be removed. This is to avoid further conflicts with the same executable in @sap/cds-dk.

If you used cds run or cds serve commands in your package.json, make sure to update it and use the successor cds-serve:

sh
npm pkg set scripts.start="cds-serve"
npm pkg set scripts.start="cds-serve"

This results in the following configuration:

jsonc
"scripts": {
  "start": "cds-serve"
}
"scripts": {
  "start": "cds-serve"
}

Access Targeted Instances with req.subject

This API simplifies the access of the targeted instances within custom implementation. It's handy for custom actions but is also available for the other events.

Example of SFlight application:

js
this.on ('acceptTravel', req => {
  return UPDATE (req.subject) .with ({TravelStatus_code:'A'})
})
this.on ('acceptTravel', req => {
  return UPDATE (req.subject) .with ({TravelStatus_code:'A'})
})

It can also be used for operating on draft enabled entities as showcased in SFlight.

For more details, see Node.js > cds.Requests > req.subject

Java

Important Changes ❗️

Minimum CloudSDK version is now 4.10.

Retrieving SAP HANA Database ID

The TenantProviderService now provides, as part of the TenantInfo class, the id of the database on which a specific tenant has been onboarded. If the information is not available null is returned:

java
@Autowired ServiceCatalog serviceCatalog;

TenantProviderService tenantProvider =
    serviceCatalog.getService(TenantProviderService.class, TenantProviderService.DEFAULT_NAME);

tenantProvider.readTenantsInfo().forEach(tenantInfo -> {
  String tenantId = tenantInfo.getTenant();
  String dbId = tenantInfo.get("database_id");
});
@Autowired ServiceCatalog serviceCatalog;

TenantProviderService tenantProvider =
    serviceCatalog.getService(TenantProviderService.class, TenantProviderService.DEFAULT_NAME);

tenantProvider.readTenantsInfo().forEach(tenantInfo -> {
  String tenantId = tenantInfo.getTenant();
  String dbId = tenantInfo.get("database_id");
});

Auditlog with IAS

Audit Log integration now supports writing entries on behalf of a named user when the CAP application is using IAS instead of XSUAA.

Propagation of Correlation ID

If the execution of a RequestContext is dispatched to a different thread, the correlation id of the parent thread is now always propagated to the new thread. If the receiving thread already had a correlation id it will be restored once the execution of the RequestContext is completed.

Deployment Service

The DeploymentService API no longer validates the subscription and deployment scope. The scopes are now only validated on incoming HTTP requests (e.g. SaaS Registry subscription), that trigger the DeploymentService API internally. In case the MtSubscriptionService compatibility mode is used, the scopes are still enforced when using the API.

Two new main methods com.sap.cds.framework.spring.utils.Subscribe and com.sap.cds.framework.spring.utils.Unsubscribe allow triggering subscriptions or unsubscriptions as tasks.

Simplified Configuration Properties

  • Some new CDS properties are introduced. The previous properties are still supported for backwards compatibility.
  • The properties cds.dataSource.csv* are moved into a new cds.dataSource.csv.* sub-section.
  • The new property cds.multiTenancy.healthcheck.interval allows to specify the health check interval as a duration. Use it in favor of the deprecated property cds.multiTenancy.healthcheck.intervalMillis, which specifies the health check interval in ms: cds.multiTenancy.healthcheck.interval: 5m

Tools

Update to Cloud Foundry CLI 8 ❗️

The usage of Cloud Foundry CLI versions < 8 is deprecated and will be removed in the near future. Please update your local installations accordingly.

For now, you'll get a warning when using an older version of the cf CLI in cds deploy:

[Warning] You are using Cloud Foundry client version 7.8.9.
We recommend version 8 or higher. Deployment will stop in the near future
for Cloud Foundry client versions < 8.

Learn more about upgrading to cf CLI v8.

Extension Point Validation by cds build

cds build now validates the extension point restrictions defined by the SaaS application provider. If any restriction is violated, the extension build aborts and the errors are logged.

Improved Code Completion in VS Code for using Paths with Support for Mono Repos

Code completion for paths of using statements now accounts for the (dev)dependencies of the corresponding package.json file.

using_code_completion.gif

Improved Visualization of Model File Dependencies

Visualize CDS file dependencies command now highlights sub modules of mono repos.

visualize_file_dependencies.png