Skip to content
Search

    June 2022

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

    Find bugfixes and enhancements in the Changelog.

    Important Changes ❗

    Authentication Enforced in Production

    In a productive scenario with an authentication strategy configured, for example XSUAA, all CAP service endpoints are now authenticated by default regardless of the authorization model.

    In Node.js this can be disabled via feature flag cds.env.requires.auth.restrict_all_services: false or by using mock authentication explicitly in production.

    For Java find details below.

    Database-Level Constraints & @assert.target

    With general availability of database-level integrity constraints, service-level integrity checks as provided in the Node.js stack, are now deprecated and planned to be removed with the next major release. In essence, take notice of these changes:

    DB-level constraints are the recommended way to ensure referential integrity.
    Yet they are not enabled by default → do that explicitly by setting cds.env.features.assert_integrity = 'db'.

    Service-level referential integrity checks are deprecated from now on, and not enabled by default anymore. They will likely be removed with next major.

    Integrity Checks are not for end users — Always keep in mind: Referential integrity checks are designed and meant for avoiding corruption of your database’s integrity. The error messages that they produce are not suitable for end users of your apps. → use @assert.target for that.

    Input Validation for Associations — @assert.target

    To address input validation on references suitable for end users, we introduced new annotation @assert.target, which ensures, that the target of a managed to-one association exists.

    entity Books {
      ...
      author : Association to Authors @assert.target;
    }
    

    Learn more about @assert.target

    Improved Read After Write

    OData CREATE and UPDATE requests, as well as draft-related EDIT and SAVE actions, are now followed by an additional READ request to the application service, instead of mixing the handler result with the database state.

    As a Consequence, …
    • It’s only required to register READ handler to adjust the response, for example to handle virtual properties, instead of registering the same handler also for the above mentioned events.
    • A user only sees the full response if authorized.
    • If the subsequent READ results in an error, the response status is 204 without payload.
    Formerly:
      // Apply a discount for over-stocked books
      this.after ('READ','Books', each  => {
        if (each.stock > 111) each.title += ' -- 20% off'
      })
    
      // We additionally have to do that for responses after write
      this.on (['CREATE','UPDATE'], 'Books', async (_, next) => {
        const book = await next()
        // Results of write operations might be minimized
        if (!book.stock) {
          let {stock} = await SELECT `stock` .from (Books,book.ID)
          book.stock = stock
        }
        if (book.stock > 111) book.title += ' -- 20% off'
        return book
      })
    
    Now:
      // Apply a discount for over-stocked books
      this.after ('READ','Books', each  => {
        if (each.stock > 111) each.title += ' -- 20% off'
      })
    

    SAP Cloud SDK v2 (Node.js)

    The usage of the SAP Cloud SDK dependency is now aligned with other dependencies. It is now an optional package that needs to be installed if required, similar to SAP HANA driver. To have it working as before, make sure to install the required packages as described in section Mock Remote Service as OData Service (Node.js).

    Dropped Support for Node.js v12, …

    Following the plans laid out in the Release Schedule, please take notice of these changes and transitions:

    CAP cds 6 doesn’t support Node v12 — Node.js v14.15 is now the minimum required Node.js version. You should better upgrade to Node.js v16 (current Active LTS version).

    CAP cds 5 goes into Maintenance Status — Essentially means that it will receive only emergency fixes from now on → plan to upgrade to cds 6 as soon as possible.

    CAP cds 4 reached End of Life Status — Essentially means that you urgently should upgrade to cds 6 now, unless your project reached end-of-life as well.

    Highlights

    Feature-Toggled Aspects

    Feature-Toggled Aspects complement the range of CAP’s extensibility offerings by one that allows SaaS providers to prepare prebuilt extensions, which can be toggled dynamically at runtime.

    image-20220628101642511

    Learn more about Features-Toggled Aspects in the Cookbook.

    Streamlined MT(X)

    We thoroughly redesigned and refactored our MTX Services. The new package @sap/cds-mtxs (note the trailing s) provides a modular set of standard CAP services, which implement multitenancy, features toggles, and extensibility (‘MTX’ stands for these three functionalities).

    image-20220706115840909

    Key Benefits

    • Test-Drive Locally → While the ‘old’ MTX always required SAP HANA and an SAP BTP setup, you can now run fully MTX-enabled apps locally, with minimal complexity, using SQLite in-memory database, and so on.
    • Grow as you go… → This also allows you to enable MTX only when required, as well as to simplify and speed up your test pipelines significantly; in turn accelerating development.
    • MTX = CAP services → Being plain standard CAP services allows to benefit from all the flexibility CAP provides, such as customizing service definitions using CDS Aspects, adding custom event handlers, and so on. You can enhance and adapt MTX to your needs.
    • Resource Consumption → The minimized and refactored implementations are the basis for some optimizations already applied, and much more to follow going forward

    Learn more about Streamlined MTX.

    Limitations → to Come Soon…

    Extensibility with streamlined MTX is not finalized and documented, hence not released yet → will follow soon with upcoming releases.

    Gaps in documentation — there are still a few gaps in our documentation that we will fill in on the go, also between releases.

    Migration from ‘Old’ MTX

    The old MTX is still available through @sap/cds-mtx (without suffixed s). Means that you don’t have to adopt the new, streamlined MTX in a hurry.

    Migration to New MTX is very much straight forward. No content needs to be changed, configurations stay very much the same, only stored extensions need to be migrated, which we plan to support generically. We will document how to Migrate to New MTX soon.

    No need to hurry, yet… — you should plan to adopt new MTX as soon as possible within the next 12 months, as the old MTX goes into Maintenance Status now (that means, will only receive emergency bug fixes from now on), and will reach End of Life Status with the next major release.

    Experimental Support for ECMAScript Modules (ESM)

    You can now write CAP Node.js applications using ECMAScript Modules (ESM). Let’s use, for example, the following handler code:

    const cds = require('@sap/cds')
    module.exports = function() { ... }
    

    This is how you can turn it into an ES module:

    import cds from '@sap/cds'
    export default function() { ... }
    

    Learn more about ES modules

    Enabling ESM support

    The easiest to enable that, is to set type module in your package.json:

      ...
      "type": "module",
      ...
    

    Find more information on this in the Node.js docs

    With ESM enabled, CAP will load all your Javascript files (custom handlers, server.js) and asynchronously using dynamic imports.

    Limitations

    Some third party libraries still have issues with ESM modules. For example, ESM support in Jest v28 is still experimental, and we encountered lots of issues, so we currently disable ESM support when running in Jest.

    Please note that this feature is rated experimental so far.

    Import OpenAPI

    The cds import command can now import OpenAPI documents into CSN file. It translates all operations into unbound actions and functions on service level.

    To import OpenAPI documents, use:

    cds import ~/Downloads/OpenAPI_sample.json
    

    Specify the --from option to import from EDMX, OpenAPI sources explicitly.

    To import OpenAPI documents programmatically, use the APIs.

    GraphQL GA

    As a first step to a better package structure, the implementation of the GraphQL protocol adapter has moved to a separate package @sap/cds-graphql. The GraphQL adapter has reached an early general availability state and can be found on the default npm registry, including instructions on how to get started.

    Shared Locks

    Use shared locks in pessimistic locking to prevent data from concurrent modification without blocking concurrent readers.

    In CAP Java use the Select.lock() method to obtain a shared lock:

    import static com.sap.cds.ql.cqn.CqnLock.Mode.SHARED;
    Select.from("bookshop.Books").byId(17).lock(SHARED);
    

    In CAP Node.js use the forShareLock method:

    SELECT.from(Books,17).forShareLock()
    

    CAP on Kyma/K8s

    You can now run CAP applications on SAP BTP, Kyma runtime.

    You can add a Helm chart containing the Kubernetes deployment files to your CAP project with the cds add helm command. There is a new guide in the section Deployment that explains the deployment to Kyma Runtime:

    image-20220706123629266

    Explore the many opportunities Kyma and its underlying components, Kubernetes and Istio, gives you. For example, how to easily secure communication between services.

    For the deployment to SAP BTP, Kyma runtime, CAP supports reading service bindings from volume mounts in Java and in Node.js.

    Learn more about the SAP BTP, Kyma Runtime

    CAP Notebooks in VS Code

    Since November 2021, Notebooks have become part of the core functionality of VS Code. This allows to build VS Code extensions that support custom notebook extensions for any language or purpose, for example, GitHub Issue Notebook or REST Book.

    Thus, we have substituted the formerly known CAP Jupyter Notebooks by the native VS Code CAP Notebooks within the CDS Editor. They are thus no longer isolated web views (Jupyter), but can actually interact with the rest of VS Code and any other extension enabling syntax highlighting, indentation, code completion, and so on.

    Furthermore, we have extended the number of CAP notebooks you can try out. These can be started using the command “CDS: Open CAP Notebooks Page”:

    CAP CDS on github.com

    CAP CDS is now a publicly known language on github.com.

    You get syntax highlighting for CAP CDS files: Syntax highlighting of CAP CDS files

    CAP CDS files are considered in language overview statistics: Github language overview shows CAP CDS

    CAP Samples: Tree View

    There is new sample code available that showcases hierarchical data using SAP UI5’s TreeTable, the @sap.hierarchy annotations, OData V2, and the CDS OData V2 adapter.

    Hierarchical Data in TreeTable

    In addition, there is a new, detailed blog post by Oliver Klemenz on this topic.

    CDS Language & Compiler

    Important Changes ❗️

    Removed “Compatibility” Options

    In the step from Compiler v1 to v2 there had been a few incompatible changes. We had introduced some “compatibility” options to keep the old behavior for some time to ease the upgrade. These options have now been removed, using them leads to an error: “Deprecated flag … has been removed in CDS compiler v3”. If you see this error, you have to adapt your code to the v2 behavior.

    Learn more about Upgrade to Compiler v2.

    Remove Compiler v1 Options

    Early versions of Compiler v1 used different options. Since Compiler v1.24, new options are available. With Compiler v3, support for old option names was removed. If you have a "cdsc" section in your .cdsrc.json or package.json, please ensure that the following options are replaced:

    • magicVars: This option was replaced by variableReplacements. See Using Special Variables for details.
    • toSql / toOdata / toHana / toCdl / toCsn: These command specific options were removed. Command specific options are now top-level.
    • <command>.names: Use sqlMapping instead.
    • <command>.dialect: Use sqlDialect instead.
    • toOdata.version: Use odataVersion instead.

    Note that options such as sqlMapping, sqlDialect, as well as odataVersion are already set by @sap/cds and @sap/cds-dk. Usually you don’t need these options in your .cdsrc.json at all.

    Generated Objects and @cds.persistence.exists/skip

    We made the behavior of the annotations cds.persistence.skip and cds.persistence.exists more consistent.

    Now, no foreign key constraints on the database are generated for a managed association if the source entity or the target entity are annotated with @cds.persistence.skip or @cds.persistence.exists. With compiler v2, a foreign key constraint was generated even if the target entity is annotated with @cds.persistence.exists.

    Learn more about Database Constraints.

    If an entity with a localized element or a managed composition is annotated with @cds.persistence.exists or @cds.persistence.skip, this annotation is now also applied to the generated text or child entities, respectively. Thus, no database tables are generated for these text or child entities any more. With compiler v2, database tables were generated for the text or child entities.

    If a different behavior is wanted, the generated text or child entities can be annotated explicitly with @cds.persistence.exists/skip: false.

    Learn more about unfolding text entities. Learn more about Managed Compositions.

    Localized helper views are no longer generated for entities annotated with @cds.persistence.exists, so that now the behavior is the same as for @cds.persistence.skip.

    Learn more about Localized Helper Views.

    Define Association in a Projection

    An unmanaged association can now be defined directly in the select list of a view or projection:

    entity BookReviews as projection on Reviews {
      ...,
      subject as bookID,
      book : Association to Books on book.ID = bookID
    };
    

    The new association becomes part of the projection signature, but cannot be used in the query itself. In the ON condition you can, besides target elements, only reference fields of the select list. Elements of the query’s data sources are not accessible. In comparison to mixins, this syntax is simpler and also available in projections.

    Learn more about Defining Associations in a select list.

    With this syntax, it’s now possible to add new, unmanaged associations to a projection or view in an extension:

    extend BookReviews with columns {
      subject as bookID,
      book : Association to Books on book.ID = bookID
    };
    

    Learn more about Extending Views and Projections.

    Annotating OData Annotations

    Adding a nested annotation to a scalar or array valued OData annotation has been simplified: add a parallel annotation that has the nested annotation name appended to the outer annotation name:

    @UI.LineItem: [
        {Value: ApplicationName},
        {Value: Description}
    ]
    @UI.LineItem.@UI.Criticality: #Positive
    

    Generated EDMX:

    <Annotation Term="UI.LineItem">
      <Collection>...</Collection>
      <Annotation Term="UI.Criticality" EnumMember="UI.CriticalityType/Positive"/>
    </Annotation>
    

    The old way of annotating a single value or a Collection by turning them into a structure with an artificial property $value is still possible, but deprecated.

    Overwriting or extending annotated annotations via annotate now works as expected.

    Learn more about Annotating Annotations.

    Arguments for Simple Custom Types

    It is now possible to provide arguments when using a simple custom type.

    type NumericString : String;
    
    entity Address {
      ...,
      zipCode : NumericString(5);
    }
    

    Load On Demand

    Requiring an API function of the compiler will no longer load the whole compiler, but only the necessary parts.

    Overriding OData Type Mapping

    Overriding the OData type mapping with annotation @odata.Type can now be done with any EDM type.

    entity Foo {
      ...,
      @odata: { Type: 'Edm.GeometryPolygon', SRID: 0 }
      geoCollection : LargeBinary;
    };
    

    Learn more about Overriding OData type mapping.

    Node.js

    Important Changes ❗

    The following are changes in behavior, which you should take notice of.

    Removed Support for Inofficial APIs

    The following APIs were never documented or rolled out and therefore should never be used. As we remove those unofficial APIs, we give you a heads-up in case you used them anyway.

    • Legacy CQN syntax for representation of values for IN operator
      • where: [..., 'IN', { val: [1,2,3] }] isn’t supported anymore → use:
      • where: [..., 'IN', { list: [{val:1},{val:2},{val:3}] }] instead
    • Legacy CQN syntax for aliases within ref:
      • { ref: ['column as c'] } isn’t supported anymore → use:
      • { ref: ['column'], as: 'c' } instead
    • Internal Feature Flags
      • cds.env.features.implicit_sorting
      • cds.env.features.auto_fetch_expand_keys
      • cds.env.features.throw_diff_error
      • cds.env.features.delay_assert_deep_assoc
      • cds.env.features.update_managed_properties
      • cds.env.sql.spaced_columns
      • cds.env.features.extract_vals
      • cds.env.features.resolve_views
    • Annotations
      • @odata.contained → use cds.Composition to use deep payloads instead
      • @odata.on.insert/update → use cds.on.insert/update instead
    • Sub-Selects in @restrict annotations → use the exists predicate instead
    • As documented, req.tenant is undefined instead of 'anonymous' for single tenant applications

    Optimized Search on SAP HANA as Default

    We now translate $search requests into the SAP HANA native contains function instead of generic LIKE expressions whenever possible. In the previous major version, this was hidden behind the feature flag cds.features.optimized_search. It still can be disabled by setting the feature flag to false.

    Optimized Server Startup Times

    OData transformation and corresponding checks are no longer applied to the whole model, but only to the relevant services. This reduces server start-up times and avoids OData related error messages for services which are not intended for OData exposure.

    New Rest Adapter as Default

    The new REST adapter includes a more powerful request parser, that allows using associations and compositions as well. We already shipped this new implementation before, but hidden behind the feature flag cds.features.rest_new_adapter. With cds6, it completely replaces the old implementation.

    Added <Entity>.data(...) (experimental)

    Linked definitions as obtained from cds.linked() models now provide proxy wrappers around raw data that provide canonic structured access to elements as defined in corresponding CDS models.

    For example, raw data you get from Fiori clients, or from databases frequently contains flattened elements:

    this.on ('UPDATE','Books', req => {
      let {author_ID} = req.data //> author_ID = 111
      // ... 
    })
    this.after ('READ','Books', each => {
      let {author_ID} = each //> author_ID = 111
      // ... 
    })
    

    If you prefer to access these things in their canonical, structured shape you can now do so like this:

    let { Books } = this.entities
    this.on ('UPDATE','Books', req => {
      let {author} = Books.data(req.data) //> author = {ID:111}
      // ... 
    })
    this.after ('READ','Books', each => {
      let {author} = Books.data(each) //> author = {ID:111}
      // ... 
    })
    

    Please note that this feature is rated experimental so far.

    Added cds.context.http

    It’s now possible to reliably access the incoming Express.js request and response objects from anywhere within your implementation, if it was initiated by an HTTP request. For other inbound channels like messaging, cds.context.http isn’t defined.

    const { req, res } = cds.context.http
    if (!req.headers.authentication)
      return res.status(403).send('Please login')
    

    Previously, it was required to use req._.req or req.context._.req depending on the invocation context, which was quite fragile.

    Improved cds.tx()

    When using the function block variant of cds.tx(), the new tx will be set as global root tx for the function body’s continuation from now on. All nested service or database operations will be executed within this transaction automatically. In effect, this works now as intuitively expected:

    cds.tx (()=>{ 
      // following are expected to run within the same transaction
      await INSERT.into (Authors). entries ({...})
      await INSERT.into (Books). entries ({...})
    })
    

    Before this improvement, this didn’t work, but one of these was required:

    cds.tx (tx => { 
      cds.context = tx // ensure all subsequent cds.db calls are in this tx
      await INSERT.into (Authors). entries ({...})
      await INSERT.into (Books). entries ({...})
    })
    
    cds.tx (tx => { 
      await tx.run( INSERT.into (Authors). entries ({...}) )
      await tx.run( INSERT.into (Books). entries ({...}) )
    })
    

    Improved req.error()

    To ease debugging, this release improves req.error() to always turn each recorded error in to an instance of Error with own stack trace.

    For example:

    req.error (`Somethings's wrong with your $`)
    req.error (`Failed to update some related record`)
    

    → These will now be reported with own stack traces, each.

    Multiple errors are finally thrown as a wrapping Error object with .message = 'MULTIPLE_ERRORS' and `.details = the array of collected errors.

    Non-Singleton @sap/cds

    With this release @sap/cds behaves like a regular Node.js module, so that require('@sap/cds') returns different objects for different install locations in the same process. Previously, require('@sap/cds') would return the same object even for different install locations.

    While such install scenarios are rather exotic, code might need to be adjusted if you use jest.resetModules() to wipe the module cache. You now really get a new cds object:

    let cds = require('@sap/cds')
    cds.foo = {}
    func ()
    function func() {
      jest.resetModules()
      cds = require('@sap/cds')
      cds.foo.bar // fails, as cds.foo is not available because `cds` is a fresh object
    }
    

    The fix is either to cope with the new object or to share the same cds variable:

    let cds = require('@sap/cds')
    cds.foo = {}
    func (cds)
    function func(cds) {
      jest.resetModules()
      cds.foo.bar // works because cds is passed on
    }
    

    Further Changes

    • The file option for file-based messaging moved from credentials to top level
    • Remote Services now appended original errors as reason instead of innererror
    • Property innererror of OData error responses is now propagated to client
    • Boolean keys are properly parsed into JS boolean values

    Java

    Important Changes ❗

    Security by Default

    The Spring security (auto-)configuration now authenticates all CAP endpoints that are not explicitly annotated with @restrict or @requires. Please note that cds.security.openUnrestrictedEndpoints = true will only open endpoints that are explicitly exposed to any. To model open endpoints, you can either adapt your model accordingly or reactivate the former behavior with cds.security.defaultRestrictionLevel = any.

    UUIDs in CSV Import

    Values imported from the CSV files for fields having type UUID and annotated with @odata.Type: 'Edm.String' are not longer be normalized.

    Feature Toggles Configuration for Mock Users

    If mock users are used, a default FeatureToggleProvider is registered, which assigns feature toggles to users based on the mock user configuration.

    Configuration per User

    Feature toggles can be configured directly per mock user:

    cds:
      security:
        mock:
          users:
            - name: Bob
              tenant: CrazyCars
              features:
                - wobble
            - name: Alice
              tenant: SmartCars
              features:
                - cruise
                - parking
    

    For mock user Bob the feature wobble is enabled while for Alice the features cruise and parking are enabled.

    Configuration per Tenant

    Alternatively, feature toggles can be configured on mock tenant level:

    cds:
      security:
        mock:
          users:
            - name: Bob
              tenant: CrazyCars
            - name: Alice
              tenant: SmartCars
          tenants:
            - name CrazyCars
              features:
                - wobble
            - name: SmartCars
              features:
                - cruise
                - parking
    

    For the mock tenant CrazyCars the feature wobble is enabled while for tenant SmartCars the features cruise and parking are enabled.

    Create Active Version of Draft-Enabled Entity

    OData V2 now also supports directly creating active versions of draft-enabled entities through POST by passing {"IsActiveEntity": true} in the payload. In OData V4 this was already possible.

    Protocol Configuration

    Use the new annotation @protocol as alias for @protocols to configure by which protocol a service is served. Both annotations allow single and arrayed values. You can use @protocol: 'none' to completely disable serving a service.

    Auto-Build

    The cds-maven-plugin is enhanced with new goal auto-build, which eases use of Spring Developer Tools. Once started, it reacts to changes in the CDS model and starts CDS builds automatically. In contrast to the watch goal it does not start the Java application and hence is suitable to enable development with IDEs.

    Tools

    Important Changes ❗️

    • Note the changes in the cds build area as they may affect the behaviour of your application in production environment

    • cds deploy --to hana now stores connection information without credentials.
      This is the same behavior as cds bind and replaces the unsafe local default-env.json file which contained credentials in clear text. If you still need the old behavior, you can add the option --store-credentials.

    New CDS Lint Rules for Authorization

    A series of new lint rules (with names auth-*) has been added to ensure that restrictions on CDS models enforce proper access control. Any models containing the @restrict / @requires annotations are now checked to catch empty restrictions, misspelled privileges, redundant events, and so on.

    Changes to cds build

    Improved Initial Data Handling for SAP HANA Deployments

    CSV files containing initial data are now found in reuse modules as well, which is the same bahavior as with SQLite. More specifically, they can be located in any csv or data subfolder, including db/data and db/csv, for which a CDS model file of your application exists. Previously, csv files would need to be manually copied from the reuse module into the application’s deployment folder.

    Use the following option to return to the former behaviour:

    { "for": "hana", "options": { "csvFileDetection": false } }
    

    Learn more about providing initial data.

    More Configuration Files Available in Production

    The files .cdsrc.json, .npmrc located in root, srv or db folders of your project are now copied into the deployment folder (usually gen/srv, gen/db) by default. Files including package.json, package-lock.json located in the srv folder have precedence over the corresponding files located in the project root directory. As these files are used in production environment, make sure that the folders do not contain one of these files by mistake. Consider using profiles development or production in order to distinguish environments. CDS configuration that should be kept locally can be defined in a file .cdsrc-private.json.

    For security reasons the files default-env.json and .env are no longer copied into the deployment folder.

    The contents of the node_modules folder is no longer copied into the deployment folder.

    Learn more about cds build configuration

    New CDS Build Task Aliases

    The new build task names nodejs and java should be used instead of the deprecated aliases node-cf and java-cf, which are still supported for compatibility reasons.

    Cloud Foundry Manifest Files No Longer Auto-Generated

    Manifest files for the srv and db modules are no longer created as default by cds build. If you prefer cf push-based deployment in contrast to MTA-based deployment, you can create manifest.yml and services-manifest.yml using the command cds add cf-manifest. Add them to your Git repo as regular source files.

    Learn more about Cloud Foundry native deployment.

    Learn more about MTA deployment.