---
title: December 2023
versions:
  cdsjs: 7.5.0
  cdsdk: 7.5.0
  cdsc: 4.5.0
  cdsmtxs: 1.14.0
  java: 2.5.0

---

# December 2023

<ReleaseBadges />

<!--
This is a preview of the release notes of our upcoming release.
With these notes we want to share with you in advance what to expect soon.
Note though that these notes are still work in progress, not official yet,
and might still change.
{.important} -->

[[toc]]

## Lazy Loading in Change Tracking Plugin

The CDS plugin for change tracking now comes with lazy loading of the *Change History* table:

<video src="./assets/dec23/change-history_compressed.mp4" autoplay loop muted webkit-playsinline playsinline />

## CDS Language & Compiler {#cds}

### Association-Valued Calculated Elements <Beta />

You can now define a calculated element where the expression is an association with a filter
("association-like calculated element").
The calculated element effectively is an association, where the ON condition is the ON condition
of the original association plus the filter condition, combined with `and`.
The behavior is similar to [publishing an association with a filter](./sep23#publish-association-with-filter-beta).

Example:

```cds
entity Authors : managed {
  key ID       : Integer;
  name         : String;
  books        : Association to many Books on books.author = $self;
  availableBooks = books[stock > 0];
}
```

[Learn more about **Calculated Elements**.](/@external/cds/cdl#calculated-elements){ .learn-more}

### Expressions in Annotations <Beta /> { #expressions-in-annotations-beta }

We're working on a set of features with the goal to provide full support
for [expressions](/@external/cds/cxn#expressions) as "first class" annotation values.

As a first step, you now can provide an expression as annotation value.

Example:
<!-- cds-mode: ignore -->
```cds
entity Orders @(restrict: [
    { grant: 'READ', to: 'Auditor', where: (AuditBy = $user.id) }
  ]) {/*...*/}
```

The expression must be enclosed in parentheses. Supported expressions are the same as in
calculated elements. You get code completion, and the compiler checks the syntax and the
paths in the expression.

:::warning
Runtime support in CAP is today only available for the `where` property of the `@restrict`
annotation. Expressions are not supported in other [standard annotations](/@external/cds/annotations#common-annotations).
The support will be extended in future releases.
:::

Currently, paths are not rewritten in propagated annotations. In a projection, for example,
all elements used in the expression must be propagated without renaming.
Thus, for the time being we recommend using the feature mainly in top-level projections.
Expressions in OData annotations are not yet handled properly, either.

In later releases we plan to extend the support for expressions in annotation values:

* Properly rewrite paths in propagated annotations.
* Support expressions in OData annotations with improved path handling.

This feature can of course also be used for your custom annotations:
<!-- cds-mode: ignore -->
```cds
@MyCustomAnnotation : (a + b)
entity Foo {
  a : Integer;
  @MyCustomAnnotation : (a + b)
  b : Integer;
}
```

::: warning
Expressions as annotation values are released as beta feature.
We provide an early preview for the functionality to allow you to experiment with it and provide feedback.
The behavior and the CSN representation of paths in propagated annotations will change.
The behavior of expressions in OData annotations will change.
:::

[Learn more about **Expressions as Annotation Values**.](/@external/cds/cdl#expressions-as-annotation-values){ .learn-more}
[Learn more about support in **CAP Java**](#support-for-expressions-in-annotations){ .learn-more}

## Node.js {#cds-js}

### Important Changes ❗️ { #changes-in-node-js .important }

* In rare cases, it was required to oversteer the default VCAP lookup, for example, when multiple instances of the same service are bound to your application.

  From now on, it's required to explicitly deactivate the default lookup if a different mechanism should be used.
  Previously, `name` and the default `label` were used individually as lookup in sequential order, which led to erratic behavior.

  ```json
  "cds": {
    "requires": {
      "messaging": {
        "kind": "enterprise-messaging-shared",
        "vcap": {
          "name": "<my-em-instance-name>",
          "label": false
        }
      }
    }
  }
  ```

### Type Definitions Are Open Source

The [type definitions of the `@sap/cds` APIs](/@external/node.js/typescript#typescript-apis-in-sapcds) are now maintained on github.com as package [`@cap-js/cds-types`](https://github.com/cap-js/cds-types).  The new package allows us to provide fixes faster and in better quality with the help of the CAP community.

::: tip No additional dependency needed
You don't need to do anything to get the new package.  It's shipped with `@sap/cds`.
:::

However, please make sure to **import types through the [`cds` facade class](/@external/node.js/cds-facade) only**:

::: code-group

```ts [Typescript]
import { Request } from '@sap/cds'  // [!code ++]
import { Request } from '@sap/cds/apis/events'  // [!code --]

function myHandler(req: Request) { }
```

```js [JavaScript]
/** @param { import('@sap/cds').Request } req */ // [!code ++]
/** @param { import('@sap/cds/apis/events').Request } req */ // [!code --]

function myHandler(req) { }
```

:::

In this release, we've also fixed a number of type export issues that have forced you to reference the internal files from above.

<div id="gh-releases" />

### Log Formatting in Production

We reworked log formatting, especially with regards to naming and defaults. `cds.log` now exports two log formatters, namely `plain` and `json`. The JSON log formatter is the default log formatter in production. Hence, it's no longer necessary to set feature flag `kibana_formatter: true`. You can opt-out of this new default behavior via config `cds.log.format: 'plain'`.

Consequently, `cds add kibana-logging` was deprecated. Please use `cds add application-logging` going forward. Further, this initializer now adds an instance of SAP Application Logging Service with plan `standard` instead of `lite` to your *mty.yaml*. The *lite* service plan isn't meant for production as the quota is not sufficient to avoid dropped logs.

Additionally, we reworked the support for SAP Application Logging Service's custom fields and introduced configurable header value masking.

[For more details, see revised Logging in Production.](/@external/node.js/cds-log#logging-in-production){.learn-more}

### SAPUI5 Mass Edit

The ["Mass Edit" feature of SAPUI5](https://sapui5.hana.ondemand.com/sdk/#/topic/965ef5b2895641bc9b6cd44f1bd0eb4d.html) is now supported. It allows modifying multiple rows of a table at the same time without needing to create drafts for each row. The feature must be activated by setting `cds.fiori.bypass_draft = true`.

<video src="./assets/dec23/fiori-direct-mod_compressed.mp4" autoplay loop muted webkit-playsinline playsinline />

::: warning
Be aware that this feature creates an additional entry point to your application. Custom handlers might require adaptations because the handlers are triggered with partial payloads instead of the complete business object.
:::

### CSRF Token Configuration

Fetching CSRF tokens of remote services is configurable at a more fine granular level. If `method` and/or `url` are provided, those are used for fetching the token.

```jsonc
"cds": {
  "requires": {
    "API_BUSINESS_PARTNER": {
      "kind": "odata-v2",
      "csrf": { // this configuration implies `csrf: true`
        "method": "get",
        "url": "..."
      }
    }
  }
}
```

[Learn more about the fetching CSRF tokens.](/@external/node.js/remote-services#csrf-token-handling){.learn-more}

### Eliminated `passport` Dependency

The generic authentication implementation doesn't rely on the 3rd party dependency `passport` anymore.

You can remove it running `npm uninstall passport` or delete it from the dependencies in the *package.json* by hand.

## Java {#cds-java}

### Important Changes ❗️ { #changes-in-java .important }

* The free [OSS support](https://spring.io/projects/spring-boot#support) for Spring Boot `2.7.x` has ended in November. Accordingly, the support for maintenance version CAP Java `1.34.x`, which is based on Spring Boot `2.7`, will be discontinued in May 2024. We **strongly recommend** [migrating](/@external/java/migration#one-to-two) to CAP Java 2 as soon as possible. See also CAP Java [release schedule](/releases/schedule#cap-java).

* The execution order of the system query option `$apply`, with regards to other system query options, has been fixed. In accordance with [the specification](https://docs.oasis-open.org/odata/odata-data-aggregation-ext/v4.0/cs03/odata-data-aggregation-ext-v4.0-cs03.html#SystemQueryOptionapply), the system query option `$apply` is now always evaluated first, then the other system query options are evaluated *on the result of `$apply`*. Before, it was possible that, for example, `$filter` or `$search` were evaluated before `$apply`.

### Support for Expressions in Annotations

With this release, the CDS compiler introduces [expressions in annotations](#expressions-in-annotations-beta).

Custom code can use the [reflection API](/@external/java/reflection-api#get-and-inspect-an-association-element-of-an-entity) to obtain the expression value from an annotation. This allows to use expressions as annotation values in *custom annotations* that are handled by custom code. This example defines a custom annotation `@my.check` to perform a consistency check after an update:

```cds
annotate Authors with @my.check: (yearOfDeath is null or
                                  yearOfBirth <  yearOfDeath);
annotate Books with @my.check: (year > author.yearOfBirth);
```

Evaluate the annotation's expression value in a generic handler:

```java
@After
public void checkAfterWrite(CdsUpdateEventContext ctx) {
   CdsEntity target = ctx.getTarget();

   CqnExpression xpr = target.getAnnotationValue("my.check", CQL.TRUE);
   ctx.getResult().stream().forEach(row -> {
      CqnSelect query = Select.from(row.ref()).where(xpr.asPredicate()).limit(1);
      if (db.run(query).first().isEmpty()) {
        throw new ServiceException(BAD_REQUEST, "check failed");
      }
   });
}
```

:::warning
Besides in `@restrict`, CAP Java doesn't support using expressions in standard annotations.
:::

[Learn more about expressions in annotations.](/@external/cds/cdl#expressions-as-annotation-values){.learn-more}

### Update with Expressions

The `Update` now supports expressions using the new `set` method, for example, to decrease the stock of Book 101 by 1:

```java
Update.entity(BOOKS).byId(101).set("stock", CQL.get("stock").minus(1));
```

[Learn more about Update with Expressions.](/@external/java/working-with-cql/query-api#update-expressions){.learn-more}

### On-the-fly Localization of EDMX

Internationalization of an SAP Fiori elements UI requires that the server provides translated EDMX metadata documents. So far, localized EDMX files were created by `cds build` or retrieved from the MTX model provider API and served by the runtime as is.

This approach resulted in EDMX versions for multiple languages being cached by the runtime, which consumed significant memory.

Now, the `cds build` and MTX model provider API provide a nonlocalized EDMX template as well as a text bundle with the translated texts of each locale. If an OData `$metadata` request is served, the runtime mixes the translations into the EDMX on the fly and streams the response. For now, you need to enable this feature in the *application.yaml* with the configuration `cds.odata-v4.lazy-i18n.enabled: true`. It requires `@sap/cds-dk` >= `7.4.0` and `@sap/cds-mtxs` >= `1.12.0`.

The `cds build` currently creates localized and nonlocalized EDMX files as static resources by default.
To save disk space, the creation of translated EDMX files can be disabled with the `cds build` option `--opts contentLocalizedEdmx=false`

:::tip
On-the-fly localization of EDMX is only available with OData V4.
:::

### RegEx Pattern Matching

Use the new `matchesPattern` predicate to test if a string value matches a given regular expression.
The following query returns books with a title that starts with the letter "C" and ends with the letter "e":

```java
Select.from(BOOKS).where(t -> t.title().matchesPattern("^C\\w*e$"));
```

You can also use `matchesPattern` with `CQL` in [tree-style](/@external/java/working-with-cql/query-api#cql-helper-interface):

```java
Select.from("bookshop.Books").where(CQL.matchesPattern(CQL.get("title"), "^C\\w*e$"));
```

Furthermore, you can now use the `matchesPattern` function [in OData v4](https://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part2-url-conventions.html#_Toc23836468):

```txt
GET Books?$filter=matchesPattern(title, '^C\\w*e$')
```

:::warning Possible performance penalty
Pattern matching with regular expressions can result in significant performance degradation, if applied on large entity sets.
:::

[Learn more about the matchesPattern predicate.](/@external/java/working-with-cql/query-api#matches-pattern){.learn-more}

### Auditlog Premium Plan

Feature `cds-feature-auditlog-v2` now supports public service plan `premium` of the Audit Log Service.
External customers can consume it by binding the CAP application to the service instance of Audit Log and adding the following dependency to the service *pom.xml*:

```xml
<dependency>
 <groupId>com.sap.cds</groupId>
 <artifactId>cds-feature-auditlog-v2</artifactId>
</dependency>
```

[Learn more in the Audit Log documentation](/@external/java/auditlog#handler-v2){.learn-more}

### Open Telemetry Support

CAP Java now provides end-to-end observability based on [Open Telemetry](https://opentelemetry.io/) format.
Collected metrics, traces, and logs can be exported to [SAP Cloud Logging service](https://discovery-center.cloud.sap/index.html#/serviceCatalog/cloud-logging) and/or Dynatrace.

On SAP BTP, Cloud Foundry environment, configuration is simplified as shown as follows.
Bind the application to a cloud logging service instance with Open Telemetry ingestion activated:

```sh
cf create-service cloud-logging standard cls -c '{"ingest_otlp": {"enabled": "true"}}'
```

Don't forget to add the logging extension library for Open Telemetry in your service *pom.xml*:

```xml
<dependency>
  <groupId>com.sap.hcp.cf.logging</groupId>
  <artifactId>cf-java-logging-support-opentelemetry-agent-extension</artifactId>
  <version>${logging.support.version}</version>
</dependency>
```

Finally, configure the Open Telemetry Java agent from the buildpack as well as the logging extension library in the environment of the CAP application:

```yaml
JBP_CONFIG_JAVA_OPTS:
  from_environment: false
  java_opts: >
    -javaagent:META-INF/.sap_java_buildpack/otel_agent/opentelemetry-javaagent.jar
    -Dotel.javaagent.extensions=BOOT-INF/lib/cf-java-logging-support-opentelemetry-agent-extension-<logging.support.version>.jar
```

Beside automatic instrumentations given by the Java agent, CAP Java additionally does the following:

* Adds nested spans for [Request](/@external/java/event-handlers/request-contexts) and [ChangeSet](/@external/java/event-handlers/changeset-contexts) contexts.
* Handles thread propagation of spans out of the box.

[Learn more about observability with Open Telemetry.](/@external/java/operating-applications/observability#open-telemetry){.learn-more}

### Miscellaneous

* The code generator supports annotating services with `@cds.java.name` to customize the name of the corresponding generated service interface.

* Draft garbage collector  and outbox collector introduced model loading bursts in MTX sidecar as all subscriber tenants have been processed periodically.
This has been improved so that such CPU overload situations are avoided.

<span id="custom-build-plugins" />
<span id="intellij-cds" />
