November 2021
Welcome to the release of CAP. Find the most noteworthy news and changes in the following sections.Events & Messaging General Available
We finalized our messaging implementations now and can hereby declare first general availability. In course of this, we also documented key concepts and usage scenarios in the new comprehensive Events & Messaging Cookbook Guide:
Schema Evolution General Available
Schema Evolution on SAP HANA Cloud is finally released for productive usage now.
CAP supports schema evolution based on migration tables for the SAP HANA database. Compatible changes including column rename can be applied to the database without any data loss and without the cost of an internal table-copy operation.
Hybrid Tests with cds bind
With hybrid testing, you stay in your local development environment and use services from the cloud. With the new cds bind
, you connect your CAP Node.js application to Cloud Foundry services. The cds watch
gets the service credentials from Cloud Foundry and keeps it until exit. There is no need to save service credentials on your hard drive anymore.
Exists Predicates in CQL beta
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; for example, the above is equivalent to this:
SELECT FROM Authors WHERE exists books [
where exists pages [
where wordcount > 1000
]
]
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
Node.js and Java Runtimes care for the same, especially 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.
WARNING
Status: Experimental --- Consider status of this features as experimental, which means behaviors might change in details. Always ensure functional correctness for your project when making changes with regards to authorization! Support for draft-enabled entities may be limited.
Java Runtime
Important Changes ❗️
- The
CdsModelProvider
is now also triggered when usingCdsRuntime.getCdsModel(UserInfo, FeatureTogglesInfo)
with aUserInfo
object, where the tenant is set tonull
. Earlier, the method always returned the default CDS model directly. - CQL API cleanup
- GROUP BY - groups are now represented as a
List<CqnValue>
- ORDER BY - the sort item is now represented as a
CqnValue
CqnLimit
- Using the CQL tree nodeCqnLimit
is deprecated in favor of the newSelect.top()
and -.skip()
methods.- Some legacy methods are deprecated in favor of cleaner substitutes. Check the build log for deprecation warnings.
- GROUP BY - groups are now represented as a
Observability
Most features described in the Java Observability guide are now demonstrated in the Java Bookshop sample.
Error Messages
Built-in handlers, for example for @assert.range
validation, now set the corresponding target in error messages. This ensures that error messages in SAP Fiori are shown directly at the relevant field, instead of in a generic popup.
Remote OData
- It's now possible to build a destination for a Remote Service declaratively in the configuration. All destination properties supported by SAP Cloud SDK can be used under the new section
cds.remote.services.<key>.destination.properties
. For a full list of supported destination properties, look at SAP Cloud SDK'scom.sap.cloud.sdk.cloudplatform.connectivity.DestinationProperty
class. - New configuration options
cds.remote.services.<key>.destination.headers
andcds.remote.services.<key>.destination.queries
have been added and allow to configure key-value pairs of headers / queries to be added to every outgoing request of the Remote Service.
anyMatch/allMatch Predicates
The anyMatch/allMatch predicates can now be applied to paths with multiple segments:
Select.from(BOOKS)
.where(b -> b.author().books().anyMatch(b -> b.title().eq("Capire")));
Path Access to Data
The Row
's get
method now supports paths, to simplify extracting values from nested maps:
CqnSelect select = Select.from(BOOKS).columns(
b -> b.title(), b -> b.author().expand()).byId(101);
Row book = dataStore.execute(select).single();
Object author = book.get("author.name"); // path access
Enhanced QL Support for Parameters
byParams
is now also supported for Update
and Delete
statements, simplifying filtering by parameters as an alternative to where
and CQL.param
:
// using where
Delete.from(BOOKS)
.where(b -> b.title().eq(param("title"))
.and(b.author().name().eq(param("author.name"))));
// using byParams
Delete.from(BOOKS).byParams("title", "author.name");
Enhanced QL Support for ORDER BY
Use the new CQL.sort
method to create a sort specification in tree style:
CqnElementRef authorName = CQL.get("author.name");
CqnSortSpecification sort = CQL.sort(authorName, CqnSortSpecification.Order.ASC);
CqnSelect query = Select.from("bookshop.Books").orderBy(authorName);
Modify an existing sort specification using Modifier.sort
:
CqnSelect sel = Select.from("Book").orderBy("title");
CqnSelect modified = copy(sel, new Modifier() {
@Override
public CqnSortSpecification sort(Value<?> value, Order order) {
return value.desc();
}
});
Simplified Representation of top
/skip
Use the top()
and skip()
methods to get the values of top
and skip
of Select
statements and expands:
CqnSelect query = Select.from(BOOKS).orderBy(b -> b.ID()).limit(10, 50);
assertThat(query.top()).isEqualTo(10);
assertThat(query.skip()).isEqualTo(50);
Node.js Runtime
Important Changes ❗️
@sap/xsenv
is no longer used for credentials look-up and can be removed from projects.cds.ql
: Keys passed as arguments into SELECT.from() are put into the target path (SELECT.from.ref
) instead of adding a where expression at root level. This change is necessary in order to distinguish between resource paths and filters/ restrictions.jsSELECT.from('Books', 1)
Result now:
jsSELECT: { from: { ref: [{ id: 'Books', where: [{ ref: ['ID'] }, '=', { val: 1 }] }] }, one: true }
Result previously:
jsSELECT: { from: { ref: ['Books'] }, where: [{ ref: ['ID'] }, '=', { val: 1 }] }, one: true }
The change can be deactivated during two-month grace period via compatibility feature flag
cds.env.features.keys_into_where = true
.
Service Consumption
There are two new configuration options for remote services:
- SAP Cloud SDK's destination options destination options can be passed via configuration
destinationOptions
. See Use Destinations with Node.js for more details. This enables you to configure if destinations should be loaded from provider or subscriber accounts of a multitenant application using destination options. - With
forwardAuthToken
, the incoming JWT is forwarded to the remote service (instead of the standard token switch). See Forward Authorization Token with Node.js for more details.
Further, GET
requests to Remote OData Services are automatically sent as $batch
if the generated URL is too long.
[Learn more in the enhanced Consuming Services Cookbook.]((../../../guides/using-services)
Project-Specific Configuration
- You can put your local and private project settings in a .cdsrc-private.json file in your project directory.
- The
CDS_CONFIG
environment variable allows to load configuration from JSON files and from the file system now.
New REST Adapter beta
The November release includes the beta version of the new REST adapter.
The new implementation uses the CAP-native OData URL to CQN parser. Hence, almost all OData requests are supported. For example, you can use query options like $filter
and $expand
, request deep resources such as GET /Foo/1/bars/2/baz
, as well as (un)bound actions and functions.
Limitations/ out of scope (compared to OData protocol adapter):
- OData query option
$apply
- OData batch requests (
/$batch
; with or without atomicity groups) - Draft handling
The new REST adapter can be activated via cds.env.features.rest_new_adapter = true
. Don't forget that you need to serve to rest
, either via the cds.serve()
API or the @protocol
annotation.
GraphQL Adapter for Node.js (experimental)
The November release includes a first experimental implementation of a generic GraphQL adapter, which turns each CAP service into a GraphQL server.
The GraphQL adapter can be activated by setting config option:
cds.env.features.graphql = true
Further, you need to install the following additional dependencies:
@graphql-tools/schema
express-graphql
graphql
The adapter serves a single endpoint for all services based on the served
event at /graphql
. At that path, you'll find a generated UI (via third party library) that allows you to interact with your application.
Follow these steps to run @capire/bookshop with graphql
:
npm add graphql express-graphql @graphql-tools/schema
cds_features_graphql=true cds watch bookshop
- Open GraphiQL client at http://localhost:4004/graphql.
- Browse schema and enter queries in the GraphiQL client.
For example, you can execute queries like the following:
{
CatalogService {
Books {
ID
title
genre {
name
}
}
}
}
The equivalent query in OData:
GET /browse/Books?$select=ID,title&$expand=genre($select=name)
Various limitations apply. For example, authentication and authorization are out of scope, and annotations aren't considered during schema generation.
WARNING
Note that the GraphQL adapter isn't supported for productive usage!