diff --git a/.vitepress/config.js b/.vitepress/config.js index 9237463824..b2baa5b6a7 100644 --- a/.vitepress/config.js +++ b/.vitepress/config.js @@ -2,12 +2,12 @@ const base = process.env.GH_BASE || '/docs/' // Construct vitepress config object... -import { dirname, join, resolve } from 'node:path' import { readFileSync } from 'node:fs' +import { dirname, join, resolve } from 'node:path' import { fileURLToPath } from 'node:url' import { defineConfig } from 'vitepress' -import playground from './lib/cds-playground/index.js' import languages from './languages' +import playground from './lib/cds-playground/index.js' import { Menu } from './menu.js' const __dirname = dirname(fileURLToPath(import.meta.url)) @@ -44,6 +44,13 @@ const config = defineConfig({ toc: { level: [2,3] }, + anchor: { + // VS Code-compatible GitHub-style slugifier (mirrors markdown-language-features/src/slugify.ts) + slugify: (str) => str + .trim().toLowerCase() + .replace(/[^\p{L}\p{N}\p{M}\s_-]/gu, '') + .replace(/\s/g, '-'), + }, container: { // Doesn't seem to work yet infoLabel: 'Info', noteLabel: 'Note', @@ -203,10 +210,10 @@ config.themeConfig.search = { // Add custom markdown renderers... import { dl } from '@mdit/plugin-dl' -import * as MdAttrsPropagate from './lib/md-attrs-propagate' -import * as MdTypedModels from './lib/md-typed-models' import * as MdLiveCode from './lib/cds-playground/md-live-code' +import * as MdAttrsPropagate from './lib/md-attrs-propagate' import * as MdDiagramSvg from './lib/md-diagram-svg' +import * as MdTypedModels from './lib/md-typed-models' config.markdown.config = md => { MdAttrsPropagate.install(md) diff --git a/cds/_menu.md b/cds/_menu.md index 2b0b726016..7accf24202 100644 --- a/cds/_menu.md +++ b/cds/_menu.md @@ -1,10 +1,10 @@ # [Definition Language (CDL)](cdl) - ## [Keywords & Identifiers](cdl#keywords-identifiers) + ## [Keywords & Identifiers](cdl#keywords--identifiers) ## [Built-in Types & Literals](cdl#built-in-types) - ## [Entities & Type Definitions](cdl#entities-type-definitions) - ## [Views & Projections](cdl#views-projections) + ## [Entities & Type Definitions](cdl#entities--type-definitions) + ## [Views & Projections](cdl#views--projections) ## [Associations](cdl#associations) ## [Annotations](cdl#annotations) ## [Aspects](cdl#aspects) diff --git a/cds/annotations.md b/cds/annotations.md index b89c834c03..c1b56e2bcd 100644 --- a/cds/annotations.md +++ b/cds/annotations.md @@ -38,9 +38,9 @@ uacp: Used as link target from Help Portal at https://help.sap.com/products/BTP/ |------------------|----------------------------------------------------------------------| | `@readonly ` | see [Input Validation](../guides/services/constraints#readonly) | | `@mandatory` | see [Input Validation](../guides/services/constraints#mandatory) | -| `@assert.target` | see [Input Validation](../guides/services/constraints#assert-target) | -| `@assert.format` | see [Input Validation](../guides/services/constraints#assert-format) | -| `@assert.range` | see [Input Validation](../guides/services/constraints#assert-range) | +| `@assert.target` | see [Input Validation](../guides/services/constraints#asserttarget) | +| `@assert.format` | see [Input Validation](../guides/services/constraints#assertformat) | +| `@assert.range` | see [Input Validation](../guides/services/constraints#assertrange) | @@ -56,21 +56,21 @@ uacp: Used as link target from Help Portal at https://help.sap.com/products/BTP/ | `@cds.api.ignore` | see [OData](../guides/protocols/odata#omitting-elements-from-apis) | | `@cds.query.limit` | see [Providing Services](../guides/services/served-ootb#annotation-cds-query-limit) | | `@cds.localized` | see [Localized Data](../guides/uis/localized-data#read-operations) | -| `@cds.valid.from/to` | see [Temporal Data](../guides/domain/temporal-data#using-annotations-cds-valid-from-to) | +| `@cds.valid.from/to` | see [Temporal Data](../guides/domain/temporal-data#using-annotations-cdsvalidfromto) | | `@cds.search` | see [Search Capabilities](../guides/services/served-ootb#searching-data) | ## Persistence | Annotation | Description | |---------------------------|------------------------------------------------------------------------------| -| `@cds.persistence.exists` | see [Generating DDL Files](../guides/databases/cdl-to-ddl#cds-persistence-exists) | -| `@cds.persistence.table` | see [Generating DDL Files](../guides/databases/cdl-to-ddl#cds-persistence-table) | -| `@cds.persistence.skip` | see [Generating DDL Files](../guides/databases/cdl-to-ddl#cds-persistence-skip) | +| `@cds.persistence.exists` | see [Generating DDL Files](../guides/databases/cdl-to-ddl#cdspersistenceexists) | +| `@cds.persistence.table` | see [Generating DDL Files](../guides/databases/cdl-to-ddl#cdspersistencetable) | +| `@cds.persistence.skip` | see [Generating DDL Files](../guides/databases/cdl-to-ddl#cdspersistenceskip) | | `@cds.persistence.mock` | `false` excludes this entity from automatic mocking | | `@cds.on.insert` | see [Providing Services](../guides/services/providing-services) | | `@cds.on.update` | see [Providing Services](../guides/services/providing-services) | -| `@sql.prepend` | see [Generating DDL Files](../guides/databases/cdl-to-ddl#sql-prepend-append) | -| `@sql.append` | see [Generating DDL Files](../guides/databases/cdl-to-ddl#sql-prepend-append) | +| `@sql.prepend` | see [Generating DDL Files](../guides/databases/cdl-to-ddl#sqlprepend--append) | +| `@sql.append` | see [Generating DDL Files](../guides/databases/cdl-to-ddl#sqlprepend--append) | ## OData diff --git a/cds/cdl.md b/cds/cdl.md index 59c52b5184..48cad8ff5f 100644 --- a/cds/cdl.md +++ b/cds/cdl.md @@ -25,7 +25,7 @@ The *Conceptual Definition Language (CDL)* is a human-readable language for defi -- [Keywords & Identifiers](#keywords-identifiers) +- [Keywords & Identifiers](#keywords--identifiers) - [Built-in Types](#built-in-types) - [Literals](#literals) - [Model Imports](#model-imports) @@ -696,7 +696,7 @@ entity Orders { } ``` -To enforce your _enum_ values during runtime, use the [`@assert.range` annotation](../guides/services/constraints#assert-range). +To enforce your _enum_ values during runtime, use the [`@assert.range` annotation](../guides/services/constraints#assertrange). For localization of enum values, model them as [code list](./common#adding-own-code-lists).
@@ -858,7 +858,7 @@ Result result = service.run(Select.from("UsingView"), params); ### Runtime Views { #runtimeviews } -To add or update CDS views without redeploying the database schema, annotate them with [@cds.persistence.skip](../guides/databases/cdl-to-ddl#cds-persistence-skip). This advises the CDS compiler to skip generating database views for these CDS views. Instead, CAP resolves them *at runtime* on each request. +To add or update CDS views without redeploying the database schema, annotate them with [@cds.persistence.skip](../guides/databases/cdl-to-ddl#cdspersistenceskip). This advises the CDS compiler to skip generating database views for these CDS views. Instead, CAP resolves them *at runtime* on each request. Runtime views must be simple [projections](#as-projection-on), not using *aggregations*, *join*, *union* or *subqueries* in the *from* clause, but may have a *where* condition if they are only used to read. diff --git a/cds/compiler/hdbcds-to-hdbtable.md b/cds/compiler/hdbcds-to-hdbtable.md index fe605d3b4d..c0863a4913 100644 --- a/cds/compiler/hdbcds-to-hdbtable.md +++ b/cds/compiler/hdbcds-to-hdbtable.md @@ -69,7 +69,7 @@ If the table doesn't contain much data, this process won't significantly impact ## Annotations -Annotations [`@sql.append/prepend`](../../guides/databases/cdl-to-ddl#sql-prepend-append) are used to generate native SQL clauses to the _.hdbtable_ files, or add native SAP HANA CDS clauses to the _.hdbcds_ files. +Annotations [`@sql.append/prepend`](../../guides/databases/cdl-to-ddl#sqlprepend--append) are used to generate native SQL clauses to the _.hdbtable_ files, or add native SAP HANA CDS clauses to the _.hdbcds_ files. If you have used these annotations in your model, a simple switchover from `hdbcds` to `hdbtable` is unlikely as such an annotation written for `hdbcds` in general is not valid for `hdbtable`. You'll have to adapt your model before the migration. diff --git a/cds/cqn.md b/cds/cqn.md index cdd5d6d9a2..1a0ee89cf4 100644 --- a/cds/cqn.md +++ b/cds/cqn.md @@ -41,7 +41,7 @@ let results = await cds.run (query) ``` Following is a detailed specification of the CQN as [TypeScript declarations](https://www.typescriptlang.org/docs/handbook/declaration-files/introduction.html), including all query types and their properties, -as well as the fundamental expression types. Find the [full CQN type definitions in the appendix below](#full-cqn-d-ts-file). +as well as the fundamental expression types. Find the [full CQN type definitions in the appendix below](#full-cqndts-file). ## SELECT diff --git a/cds/cxl.md b/cds/cxl.md index fe41f9a421..d54d551014 100644 --- a/cds/cxl.md +++ b/cds/cxl.md @@ -234,7 +234,7 @@ This syntax diagram describes the possible expressions: ### In Queries - Expressions can be used in various parts of a query, e.g., on the select list, in the where clause, in order by clauses, and more: + Expressions can be used in various parts of a query, for example,, on the select list, in the where clause, in order by clauses, and more: ```cds live SELECT from Books { @@ -576,10 +576,10 @@ Following table gives an overview of the guaranteed supported operators in CXL: | `and`, `or` | Logical operators. | `x>1 or y<2` | > [!tip] Bivalent `==` and `!=` Operators -> In addition to standard SQL's `=` and `<>` operators, CXL also supports `==` and `!=` as bivalent variants as opposed to the trivalent semantics of `=` and `<>` when it comes to null handling. Learn more about this in the [_Bivalent `==` and `!=` Operators_](../guides/databases/cap-level-dbs#bivalent-and-operators) section of the databases documentation. +> In addition to standard SQL's `=` and `<>` operators, CXL also supports `==` and `!=` as bivalent variants as opposed to the trivalent semantics of `=` and `<>` when it comes to null handling. Learn more about this in the [_Bivalent `==` and `!=` Operators_](../guides/databases/cap-level-dbs#bivalent--and--operators) section of the databases documentation. > [!tip] Ternary `?:` Operator -> In addition to the standard SQL `case when then` expression, CXL also supports the ternary `?:` operator as a more concise syntax for simple case expressions. Learn more about this in the [_Ternary `?:` Operator_](../guides/databases/cap-level-dbs#ternary-operator) section of the databases documentation. +> In addition to the standard SQL `case when then` expression, CXL also supports the ternary `?:` operator as a more concise syntax for simple case expressions. Learn more about this in the [_Ternary `?:` Operator_](../guides/databases/cap-level-dbs#ternary--operator) section of the databases documentation. diff --git a/cds/models.md b/cds/models.md index ab67d21abc..d0cdd739c0 100644 --- a/cds/models.md +++ b/cds/models.md @@ -20,7 +20,7 @@ For example, a *data model describes the type structure (commonly also called *' ### Representations -Models can come in different *representations*, which follow different *syntaxes*. For example, we use the *CDL* syntax for *human-readable* representations of CDS models, while CSN is an *object notation*, i.e. a special form of *syntax*, used for *machine-readable* representations of CDS models. +Models can come in different *representations*, which follow different *syntaxes*. For example, we use the *CDL* syntax for *human-readable* representations of CDS models, while CSN is an *object notation*, that is a special form of *syntax*, used for *machine-readable* representations of CDS models. ::: details On CSN representations... diff --git a/cds/types.md b/cds/types.md index c876abf6da..748431d5e7 100644 --- a/cds/types.md +++ b/cds/types.md @@ -24,8 +24,8 @@ ANSI SQL types, when deployed to a relational database (concrete mappings to spe | `UInt8` | Unsigned 8-bit integer, range *[ 0 ... 255 ]* | _TINYINT_ | | `Decimal`(`p`,`s`) | Decimal with precision `p` and scale `s` | _DECIMAL_ | | `Double` | Floating point with binary mantissa | _DOUBLE_ | -| `Date` | e.g. `2022-12-31` | _DATE_ | -| `Time` | e.g. `23:59:59` | _TIME_ | +| `Date` | for example, `2022-12-31` | _DATE_ | +| `Time` | for example, `23:59:59` | _TIME_ | | `DateTime` | _sec_ precision | _TIMESTAMP_ | | `Timestamp` | _µs_ precision, with up to 7 fractional digits | _TIMESTAMP_ | | `String` (`length`) | Default *length*: 255; on HANA: 5000 | _NVARCHAR_ | diff --git a/get-started/bookshop.md b/get-started/bookshop.md index b97a71973a..2d9baedc93 100644 --- a/get-started/bookshop.md +++ b/get-started/bookshop.md @@ -135,7 +135,7 @@ entity Genres : sap.common.CodeList { ###### Focus on Domain > [!tip] Primary Focus on Domain -> Strive to keep your domain models simple, concise and comprehensible, focused on the core concepts of your domain, i.e., [_“Keep it simple, stupid!”_](https://en.wikipedia.org/wiki/kiss_principle). Factor out secondary concerns into separate sources, which _extend_ and _annotate_ the core models.\ +> Strive to keep your domain models simple, concise and comprehensible, focused on the core concepts of your domain, that is, [_“Keep it simple, stupid!”_](https://en.wikipedia.org/wiki/kiss_principle). Factor out secondary concerns into separate sources, which _extend_ and _annotate_ the core models.\ > See also: [_Separation of Concerns_](#separation-of-concerns). [Learn more about _Domain Modeling_.](../guides/domain/index){ .learn-more} @@ -597,7 +597,7 @@ We can also send such OData requests programmatically, for example, doing the ve await cds.service.bindings ``` ::: details About _cds.service.bindings_ ... - The `cds.service.bindings` command fetches the service bindings from a running CAP server instance in another process, and makes them available in the current process, e.g., within `cds repl`. + The `cds.service.bindings` command fetches the service bindings from a running CAP server instance in another process, and makes them available in the current process, for example, within `cds repl`. ```js [cds] - using bindings from: { registry: '~/.cds-services.json' } Bindings { @@ -686,7 +686,7 @@ While the generic providers serve most CRUD requests out of the box, you can add - Use [declarative constraints](#declarative-constraints) for most input validation cases, which are enforced by generic runtimes automatically. -- Add [custom event handlers in Node.js](#custom-handlers-in-node-js) or [in Java](#custom-handlers-in-java) to CAP services for more complex programmatic logic, such as modifying response data, or handling [custom actions](#custom-actions). +- Add [custom event handlers in Node.js](#custom-handlers-in-nodejs) or [in Java](#custom-handlers-in-java) to CAP services for more complex programmatic logic, such as modifying response data, or handling [custom actions](#custom-actions). > [!note] Choosing between Node.js and Java > The latter is the first time in this guide where you need to choose between Node.js and Java as your CAP runtime. diff --git a/get-started/concepts.md b/get-started/concepts.md index 7d46dc852d..6f87b036b5 100644 --- a/get-started/concepts.md +++ b/get-started/concepts.md @@ -44,7 +44,7 @@ The major building blocks are as follows: - [**Core Data Services** (CDS)](../cds/) — CAP's universal modeling language, and the very backbone of everything; used to capture domain knowledge, generating database schemas, translating to and from various API languages, and most important: fueling generic runtimes to automatically serve request out of the box. -- [**Service Runtimes**](../guides/services/providing-services) for [Node.js](../node.js/) and [Java](../java/) — providing the core frameworks for services, generic providers to serve requests automatically, database support for SAP HANA, SQLite, and PostgreSQL, and protocol adaptors for REST, OData, GraphQL, ... +- [**Service Runtimes**](../guides/services/providing-services) for [Node.js](../node.js/) and [Java](../java/) — providing the core frameworks for services, generic providers to serve requests automatically, database support for SAP HANA, SQLite, and PostgreSQL, and protocol adapters for REST, OData, GraphQL, ... - [**Platform Integrations**](../plugins/) — providing CAP-level service interfaces (*'[Calesi](#the-calesi-pattern)'*) to cloud platform services in platform-agnostic ways, as much as possible. Some of these are provided out of the box, others as plugins. @@ -150,7 +150,7 @@ INNER JOIN Countries as country ON country.code = author.country_code -- the actual filter condition: WHERE country.code = 'GB'; ``` -Path expressions in *infix filters* become *SEMI JOINs*, e.g.using `IN`: +Path expressions in *infix filters* become *SEMI JOINs*, for example, using `IN`: ```sql CREATE VIEW EnglishBooks AS SELECT * FROM Books -- for Association Books:author: @@ -702,16 +702,19 @@ Your application models are your services, also served automatically by generic ### Protocol Adapters -Behind the scenes, i.e., in the **outer hexagon** containing stuff, you as an application developer should not see, the CAP runtime employs Protocol Adapters, which translate requests from (and to) low-level protocols like HTTP, REST, OData, GraphQL, ... to protocol-agnostic CAP requests and queries. +Behind the scene - that is, in the **outer hexagon** containing stuff, you as an application developer should not see - the CAP runtime employs Protocol Adapters, which translate requests from (and to) low-level protocols like HTTP, REST, OData, GraphQL, ... to protocol-agnostic CAP requests and queries for inbound and outbound communication. -- for ***inbound*** communication → i.e., requests your application *receives*, as well as as... -- for ***outbound*** communication → i.e., requests your application *sends* to other services. +--> ***Inbound*** Communication +: Requests your application *receives*. -In effect your service implementations stay agnostic to (wire) protocols, which allows us to exchange protocols, replace targets by mocks, do fast inner loop development in airplane mode, ... even change topologies from a monolith to micro services and vice versa late in time. +--> ***Outbound*** Communication +: Requests your application *sends* to other services. + +In effect your service implementations stay agnostic to (wire) protocols, which allows you to exchange protocols, replace targets by mocks, do fast inner loop development in airplane mode, ... even change topologies from a monolith to micro services and vice versa late in time. ![protocol-adapters.drawio](./assets/concepts/protocol-adapters.drawio.svg) -The inbound and outbound adapters (and the framework services) effectively provide your inner core with the ***ports*** to the outside world, which always provide the same, hence *agnostic* style of API (indicated by the green arrows used in the previous graphic), as already introduced in [Local /Remote](#local-remote). +The inbound and outbound adapters (and the framework services) effectively provide your inner core with the ***ports*** to the outside world, which always provide the same, hence *agnostic* style of API (indicated by the green arrows used in the previous graphic), as already introduced in [Local /Remote](#local--remote). Inbound: diff --git a/get-started/feature-matrix.md b/get-started/feature-matrix.md index 66abfaf3e4..8ac01c90c2 100644 --- a/get-started/feature-matrix.md +++ b/get-started/feature-matrix.md @@ -168,7 +168,7 @@ Following is an index of the features currently covered by CAP, with status and | Outbound Protocol Support | CDS 1 | Node.js | Java | |------------------------------------------------------------------|:----------------:|:-------:|:----:| -| [REST/OpenAPI](../tools/apis/cds-import#cds-import-from-openapi) | | | | +| [REST/OpenAPI](../tools/apis/cds-import#cdsimportfromopenapi) | | | | | OData V2 | | | | | OData V4 | | | | | GraphQL2 | | | | diff --git a/get-started/get-help.md b/get-started/get-help.md index 60cd1b159e..e4542068e0 100644 --- a/get-started/get-help.md +++ b/get-started/get-help.md @@ -212,7 +212,7 @@ module.exports = cds.server | | Explanation | |--------------|--------------------------------------------------------------------------------------------------------------------------------------------------------| | _Root Cause_ | Most probably, the service name in the `requires` section does not match the served service definition. | -| _Solution_ | Set the `.service` property in the respective `requires` entry. See [cds.connect()](../node.js/cds-connect#cds-requires-srv-service) for more details. | +| _Solution_ | Set the `.service` property in the respective `requires` entry. See [cds.connect()](../node.js/cds-connect#cdsrequiressrvservice) for more details. | ### Why does my remote service call not work? diff --git a/get-started/learn-more.md b/get-started/learn-more.md index c9a6b4b25b..4c297e1dd7 100644 --- a/get-started/learn-more.md +++ b/get-started/learn-more.md @@ -164,6 +164,7 @@ SAP Developer Advocate [DJ Adams](https://qmacro.org) has compiled a vast number - [Shift left with CAP](https://qmacro.org/blog/posts/2026/02/09/shift-left-with-cap/) - [Five reasons to use CAP](https://qmacro.org/blog/posts/2024/11/07/five-reasons-to-use-cap/) +- [CAP service authentication at design time and in production](https://qmacro.org/blog/posts/2026/06/19/cap-service-authentication-at-design-time-and-in-production/) - [Flattening the hierarchy with mixins](https://qmacro.org/blog/posts/2024/11/08/flattening-the-hierarchy-with-mixins/) - [A reCAP intro to the cds REPL](https://qmacro.org/blog/posts/2025/07/21/a-recap-intro-to-the-cds-repl/) - [A deep dive into OData and CDS annotations](https://qmacro.org/blog/posts/2023/03/10/a-deep-dive-into-odata-and-cds-annotations/) @@ -174,6 +175,7 @@ SAP Developer Advocate [DJ Adams](https://qmacro.org) has compiled a vast number ### Workshop exercise content - [Service integration with SAP Cloud Application Programming Model](https://github.com/SAP-samples/cap-service-integration-codejam) +- [A hands-on tour of CAP](https://github.com/SAP-samples/cap-tour-hands-on/) - [Stay cool, stay local: CAP local development workshop](https://github.com/SAP-samples/cap-local-development-workshop) - [Hands-on with CAP CDS](https://github.com/SAP-samples/cap-cds-hands-on) diff --git a/guides/databases/cdl-to-ddl.md b/guides/databases/cdl-to-ddl.md index db3239c71f..6eec13feaa 100644 --- a/guides/databases/cdl-to-ddl.md +++ b/guides/databases/cdl-to-ddl.md @@ -465,7 +465,7 @@ CREATE TABLE BadNames ( However, even though CAP allows this, and handles all accesses correctly, it is strongly discouraged to use such names in your CDS models, as that may lead to unexpected issues in several scenarios, not in control of CAP, such as native SQL queries, third-party tools, or integration with non-CAP applications. > [!warning] DON'T use Database-Invalid Names! -> It's **strongly discouraged** to use names that contain non-ASCII characters, or conflict with database reserved words. Even more avoid [delimited names](../../cds/cdl#keywords-identifiers) in CDS models in the first place, as that impacts readability of your models. +> It's **strongly discouraged** to use names that contain non-ASCII characters, or conflict with database reserved words. Even more avoid [delimited names](../../cds/cdl#keywords--identifiers) in CDS models in the first place, as that impacts readability of your models. ###### reserved-words > [!tip] Lists of Reserved Words @@ -611,7 +611,7 @@ CREATE TABLE Books ( ... ::: > [!tip] Consider using @assert.target instead -> Database constraints are meant to protect against data corruption due to programming errors. Prefer using the [`@assert.target`](../services/constraints#assert-target) for application-level input validation, which is more tuned for typical application scenarios, with error messages tailored for end users. +> Database constraints are meant to protect against data corruption due to programming errors. Prefer using the [`@assert.target`](../services/constraints#asserttarget) for application-level input validation, which is more tuned for typical application scenarios, with error messages tailored for end users. #### `ON DELETE CASCADE` @@ -735,7 +735,7 @@ CREATE VIEW Bar AS SELECT ... FROM Foo; -- skipped [!code --] > All parts of the view definition not relevant for the signature, such as `where`, `group by`, `having`, `order by`, or `limit`, are ignored. > [!tip] Use Case: Replica Caching Tables -A common use case for this annotation is to create projections on entities from imported APIs, i.e., so-called _consumption views_, and at the same time use them as replica cache tables. +A common use case for this annotation is to create projections on entities from imported APIs, so-called _consumption views_, and at the same time use them as replica cache tables. diff --git a/guides/databases/h2.md b/guides/databases/h2.md index c72638a6ae..a97b56a7e3 100644 --- a/guides/databases/h2.md +++ b/guides/databases/h2.md @@ -8,4 +8,4 @@ For local development and testing, CAP Java supports the [H2](https://www.h2data Learn more about the [features and limitations of using CAP with H2.](../../java/cqn-services/persistence-services#h2) -There are various options of how to configure the [H2 database for local development and testing in CAP Java.](../../java/developing-applications/testing#setup-configuration) +There are various options of how to configure the [H2 database for local development and testing in CAP Java.](../../java/developing-applications/testing#setup--configuration) diff --git a/guides/databases/hana.md b/guides/databases/hana.md index ac78a36509..fe54e7eafc 100644 --- a/guides/databases/hana.md +++ b/guides/databases/hana.md @@ -51,7 +51,7 @@ The datasource for SAP HANA is then auto-configured based on available service b ::: tip Prefer `cds add` -... as documented in the [deployment guide](../deploy/to-cf#_1-sap-hana-database), which also does the equivalent of `npm add @cap-js/hana` but in addition cares for updating `mta.yaml` and other deployment resources. +... as documented in the [deployment guide](../deploy/to-cf#1-sap-hana-database), which also does the equivalent of `npm add @cap-js/hana` but in addition cares for updating `mta.yaml` and other deployment resources. ::: @@ -477,7 +477,7 @@ If this is not desired, annotate these generated entities with `@cds.persistence ### Native Database Clauses {#schema-evolution-native-db-clauses} -Not all clauses supported by SQL can directly be written in CDL syntax. To use native database clauses also in a CAP CDS model, you can provide arbitrary SQL snippets with the annotations [`@sql.prepend` and `@sql.append`](cdl-to-ddl#sql-prepend-append). In this section, we're focusing on schema evolution specific details. +Not all clauses supported by SQL can directly be written in CDL syntax. To use native database clauses also in a CAP CDS model, you can provide arbitrary SQL snippets with the annotations [`@sql.prepend` and `@sql.append`](cdl-to-ddl#sqlprepend--append). In this section, we're focusing on schema evolution specific details. Schema evolution requires that any changes are applied by corresponding ALTER statements. See [ALTER TABLE statement reference](https://help.sap.com/docs/hana-cloud-database/sap-hana-cloud-sap-hana-database-sql-reference-guide/alter-table-statement-data-definition?version=2024_1_QRC) for more information. A new migration version is generated whenever an `@sql.append` or `@sql.prepend` annotation is added, changed, or removed. ALTER statements define the individual changes that create the final database schema. This schema has to match the schema defined by the TABLE statement in the _.hdbmigrationtable_ artifact. Please note that the compiler doesn't evaluate or process these SQL snippets. Any snippet is taken as is and inserted into the TABLE statement and the corresponding ALTER statement. The deployment fails in case of syntax errors. diff --git a/guides/databases/initial-data.md b/guides/databases/initial-data.md index 86db8bc40b..8066d7d866 100644 --- a/guides/databases/initial-data.md +++ b/guides/databases/initial-data.md @@ -138,7 +138,7 @@ cds: ``` ::: -Use `cds env` to check which configuration is active in your current profile, e.g.: +Use `cds env` to check which configuration is active in your current profile, for example: ```shell cds env requires.db.data --profile development ``` diff --git a/guides/databases/new-dbs.md b/guides/databases/new-dbs.md index b1333b5785..2abd096e77 100644 --- a/guides/databases/new-dbs.md +++ b/guides/databases/new-dbs.md @@ -44,7 +44,7 @@ npm add @cap-js/hana ::: details Prefer `cds add hana` ... -... which also does the equivalent of `npm add @cap-js/hana` but in addition cares for updating `mta.yaml` and other deployment resources as documented in the [deployment guide](../deploy/to-cf#_1-sap-hana-database). +... which also does the equivalent of `npm add @cap-js/hana` but in addition cares for updating `mta.yaml` and other deployment resources as documented in the [deployment guide](../deploy/to-cf#1-sap-hana-database). ::: diff --git a/guides/databases/postgres.md b/guides/databases/postgres.md index 3d312a3eb2..cca4e6bc80 100644 --- a/guides/databases/postgres.md +++ b/guides/databases/postgres.md @@ -1,40 +1,20 @@ ---- -impl-variants: true ---- # Using PostgreSQL -
- This guide focuses on the new PostgreSQL Service provided through *[@cap-js/postgres](https://www.npmjs.com/package/@cap-js/postgres)*, which is based on the same new database services architecture as the new [SQLite Service](./sqlite). -*Learn about migrating from the former `cds-pg` in the [Migration](#migration) chapter.*{.learn-more} - -
- -
- CAP Java 3 is tested on [PostgreSQL](https://www.postgresql.org/) 16 and most CAP features are supported on PostgreSQL. -[Learn more about features and limitations of using CAP with PostgreSQL](../../java/cqn-services/persistence-services#postgresql){.learn-more} -
+*Learn about migrating from the former `cds-pg` in the [Migration](#migration-from-cds-pg-in-nodejs) chapter.*{.learn-more} +[Learn more about features and limitations of using CAP Java with PostgreSQL.](../../java/cqn-services/persistence-services#postgresql){.learn-more} - [[toc]] ## Setup & Configuration -
- -Run this to use [PostgreSQL](https://www.postgresql.org/) for production: - -
- -
- To run CAP Java on PostgreSQL, add a Maven dependency to the PostgreSQL feature in `srv/pom.xml`: ```xml @@ -45,21 +25,16 @@ To run CAP Java on PostgreSQL, add a Maven dependency to the PostgreSQL feature ``` -In order to use the CDS tooling with PostgreSQL, you also need to install the module `@cap-js/postgres`: - -
+For CAP Node.js projects, and for CAP Java in order to use the CDS tooling with PostgreSQL, you also need to install the module `@cap-js/postgres`: ```sh npm add @cap-js/postgres ``` -
- -After that, you can use the `cds deploy` command to [deploy](#using-cds-deploy) to a PostgreSQL database or to [create a DDL script](#using-liquibase-java) for PostgreSQL. +After that, you can use the `cds deploy` command to [deploy](#using-cds-deploy) to a PostgreSQL database or to [create a DDL script](#using-liquibase-in-cap-java) for PostgreSQL. -
-### Auto-Wired Configuration {.node} +### Auto-Wired Configuration in Node.js The `@cap-js/postgres` package uses `cds-plugin` technique to auto-configure your application and use a PostgreSQL database for production. @@ -84,7 +59,7 @@ Output: To connect to a PostgreSQL offering from the cloud provider in Production, leverage the [PostgreSQL on SAP BTP, hyperscaler option](https://discovery-center.cloud.sap/serviceCatalog/postgresql-hyperscaler-option). For local development and testing convenience, you can run PostgreSQL in a [docker container](#using-docker). -
+### CAP Java on SAP BTP To consume a PostgreSQL instance from a CAP Java application running on SAP BTP, consider the following: @@ -112,7 +87,6 @@ modules: ::: > `BOOKSHOP-PG-DB` is the real PostgreSQL service instance name in this example. -
### Using Docker @@ -141,27 +115,17 @@ You can use Docker to run a PostgreSQL database locally as follows: docker-compose -f pg.yml up -d ``` -
- -::: tip +::: tip Testcontainer in CAP Java using Spring Boot With the introduction of [Testcontainers support](https://spring.io/blog/2023/06/23/improved-testcontainers-support-in-spring-boot-3-1) in Spring Boot 3.1, you can create PostgreSQL containers on the fly for local development or testing purposes. ::: -
- ## Service Bindings You need a service binding to connect to the PostgreSQL database. In the cloud, use given techniques to bind a cloud-based instance of PostgreSQL to your application. -
- -For local development provide the credentials using a suitable [`cds env`](../../node.js/cds-env) technique, like one of the following. - -
- -### Configure Connection Data {.java} +### Configure Connection Data in CAP Java If a PostgreSQL service binding exists, the corresponding `DataSource` is auto-configured. @@ -181,11 +145,11 @@ spring: ::: To start the application with the new profile `postgres-docker`, the `spring-boot-maven-plugin` can be used: `mvn spring-boot:run -Dspring-boot.run.profiles=postgres-docker`. -[Learn more about the configuration of a PostgreSQL database](../../java/cqn-services/persistence-services#postgresql-1){ .learn-more} +[Learn more about the configuration of a PostgreSQL database.](../../java/cqn-services/persistence-services#postgresql-1){ .learn-more} -### Service Bindings for CDS Tooling {.java} +### Service Bindings for CDS Tooling in CAP Java -#### Using Defaults with `[pg]` Profile {.java} +#### Using Defaults with `[pg]` Profile `@cds-js/postgres` comes with a set of default credentials under the profile `[pg]` that matches the defaults used in the [docker setup](#using-docker). So, if you stick to these defaults you can skip to deploying your database with: @@ -193,7 +157,7 @@ To start the application with the new profile `postgres-docker`, the `spring-boo cds deploy --profile pg ``` -#### In Your Private `.cdsrc-private.json` {.java} +#### In Your Private `.cdsrc-private.json` If you don't use the default credentials and want to use just `cds deploy`, you need to configure the service bindings (connection data) for the CDS tooling. Add the connection data to your private `.cdsrc-private.json`: @@ -214,7 +178,9 @@ If you don't use the default credentials and want to use just `cds deploy`, you } ``` -### Configure Service Bindings {.node} +### Configure Service Bindings in Node.js + +For local development provide the credentials using a suitable [`cds env`](../../node.js/cds-env) technique, like one of the following. #### Using Defaults with `[pg]` Profile @@ -515,7 +481,7 @@ to your database! ::: -## Using Liquibase (Java) +## Using Liquibase in CAP Java In CAP Java projects you can also use [Liquibase](https://www.liquibase.org/) to control when, where, and how database changes are deployed. Liquibase lets you define database changes [in an SQL file](https://docs.liquibase.com/change-types/sql-file.html), use `cds deploy` to quickly generate DDL scripts which can be used by Liquibase. @@ -617,7 +583,7 @@ If the changes in the model could lead to data loss, an error is raised. ::: -## Migration { .node } +## Migration from cds-pg in Node.js Thanks to CAP's database-agnostic cds.ql API, we're confident that the new PostgreSQL service comes without breaking changes. diff --git a/guides/databases/schema-evolution.md b/guides/databases/schema-evolution.md index 78c21f27bb..2efe19db66 100644 --- a/guides/databases/schema-evolution.md +++ b/guides/databases/schema-evolution.md @@ -115,7 +115,7 @@ Let's simulate the workflow with the [@capire/bookshop](https://github.com/capir Some changes to the CDS model are considered disallowed in the context of schema evolution, as they could lead to data loss or inconsistencies. The following list shows examples of such changes: - Renaming entities or fields (instead, add new ones and migrate data) -- Changing data types in incompatible ways (e.g., from String to Integer) +- Changing data types in incompatible ways (for example, from String to Integer) - Removing entities or fields (instead, consider deprecating them first) - Reducing the length of strings or binary fields - Reducing the precision of numeric fields @@ -198,4 +198,4 @@ For Java-based CAP projects, you can also use [Liquibase](https://www.liquibase. Please be aware that Liquibase [changed its license to Functional Source License (FSL)](https://www.liquibase.com/blog/liquibase-community-for-the-future-fsl) with release 5.0. You need to check if this license is compatible with your application. ::: -Learn more about that in the [PostgreSQL](postgres.md) guide, section [Using Liquibase (Java)](postgres#using-liquibase-java). +Learn more about that in the [PostgreSQL](postgres.md) guide, section [Using Liquibase (Java)](postgres#using-liquibase-in-cap-java). diff --git a/guides/deploy/cicd.md b/guides/deploy/cicd.md index 3a0f940f75..08d039af1e 100644 --- a/guides/deploy/cicd.md +++ b/guides/deploy/cicd.md @@ -103,13 +103,13 @@ For the actual release we want to override org-wide sandbox variables to deploy Go to **Settings** → **Environments** → **New environment** → enter "Production". -Now override org-wide variables (e.g. `CF_ORG` and `CF_SPACE` in Cloud Foundry) to use a dedicated subaccount you created for the release deployment. +Now override org-wide variables (for example `CF_ORG` and `CF_SPACE` in Cloud Foundry) to use a dedicated subaccount you created for the release deployment. #### Publish the release On your repository front page go to `Releases` → `Draft a new release` → `Select tag`. -Now enter a tag name, e.g. `v1.0.0` and select `Create new tag: v1.0.0 on publish`. +Now enter a tag name, for example, `v1.0.0` and select `Create new tag: v1.0.0 on publish`. You can optionally add a release title and release notes. Hit **Publish release** once you're ready. diff --git a/guides/domain/index.md b/guides/domain/index.md index 71658e7a7c..935bceb6ef 100644 --- a/guides/domain/index.md +++ b/guides/domain/index.md @@ -251,7 +251,7 @@ entity name { } ``` -[Learn more about entity definitions.](../../cds/cdl#entities-type-definitions){.learn-more} +[Learn more about entity definitions.](../../cds/cdl#entities--type-definitions){.learn-more} @@ -265,7 +265,7 @@ entity ProjectedEntity as select from BaseEntity { }; ``` -[Learn more about views and projections.](../../cds/cdl#views-projections){.learn-more} +[Learn more about views and projections.](../../cds/cdl#views--projections){.learn-more} @@ -289,7 +289,7 @@ entity Books { ##### Don't: {.bad} - Don't use binary data as keys! -- [Don't interpret UUIDs!](#don-t-interpret-uuids) +- [Don't interpret UUIDs!](#dont-interpret-uuids) #### Prefer Simple, Technical Keys diff --git a/guides/integration/calesi.md b/guides/integration/calesi.md index 1a6669341f..59fd5d5441 100644 --- a/guides/integration/calesi.md +++ b/guides/integration/calesi.md @@ -55,7 +55,7 @@ The graphic below illustrates what happened here: ![Diagram illustrating CAP-level service integration showing two scenarios: Local services where Consumer connects to Service via CQL, and Remote services where Consumer connects to Proxy via CQL, Proxy connects to Protocol Adapter via OData, and Protocol Adapter connects to Service via CQL. ](assets/remoting.drawio.svg) -Remote CAP services can be consumed using the same high-level, uniform APIs as for local services – i.e., **_as if they were local_**. `cds.connect` automatically constructs remote proxies, which translate all local requests into protocol-specific ones, sent to remote services. Thereby also taking care of all connectivity, remote communication, principal propagation, as well as generic resilience. +Remote CAP services can be consumed using the same high-level, uniform APIs as for local services – that is, **_as if they were local_**. `cds.connect` automatically constructs remote proxies, which translate all local requests into protocol-specific ones, sent to remote services. Thereby also taking care of all connectivity, remote communication, principal propagation, as well as generic resilience. > [!note] Model Free > @@ -123,7 +123,7 @@ Open the _cap/samples/xflights_ folder in Visual Studio Code, and have a look at ::: code-group ```cds :line-numbers [cap/samples/xflights/srv/data-service.cds] using sap.capire.flights as x from '../db/schema'; -@odata @hcql service sap.capire.flights.data { +@odata @hcql service FlightsService { @readonly entity Flights as projection on x.Flights {flights.*,*}; @readonly entity Airlines as projection on x.Airlines; @readonly entity Airports as projection on x.Airports; @@ -131,7 +131,7 @@ using sap.capire.flights as x from '../db/schema'; ``` ::: -This declares a CAP service named `sap.capire.flights.data`, served over _OData_ and _HCQL_ protocols, which exposes _Flights_, _Airlines_, and _Airports_ as readonly projections on underlying domain model entities, with _Flights_ as a denormalized view. +This declares a CAP service named `FlightsService`, served over _OData_ and _HCQL_ protocols, which exposes _Flights_, _Airlines_, and _Airports_ as readonly projections on underlying domain model entities, with _Flights_ as a denormalized view. #### Using Denormalized Views @@ -189,12 +189,12 @@ cds export srv/data-service.cds --dry ```zsh Kept: 6 - • sap.capire.flights.data - • sap.capire.flights.data.Flights - • sap.capire.flights.data.Airlines - • sap.capire.flights.data.Airports - • sap.capire.flights.data.Supplements - • sap.capire.flights.data.SupplementTypes + • FlightsService + • FlightsService.Flights + • FlightsService.Airlines + • FlightsService.Airports + • FlightsService.Supplements + • FlightsService.SupplementTypes Skipped: 31 @@ -227,8 +227,8 @@ cds export srv/data-service.cds --dry - sap.common.Countries.texts - sap.common.Currencies.texts - sap.common.Timezones.texts - - sap.capire.flights.data.Supplements.texts - - sap.capire.flights.data.SupplementTypes.texts + - FlightsService.Supplements.texts + - FlightsService.SupplementTypes.texts Total: 37 ``` @@ -247,12 +247,12 @@ This opens a diff view in VSCode, which would display these differences: Kept: 26 # [!code --] Kept: 6 # [!code ++] - • sap.capire.flights.data - • sap.capire.flights.data.Flights - •• sap.capire.flights.data.Airlines - •• sap.capire.flights.data.Airports - • sap.capire.flights.data.Supplements - •• sap.capire.flights.data.SupplementTypes + • FlightsService + • FlightsService.Flights + •• FlightsService.Airlines + •• FlightsService.Airports + • FlightsService.Supplements + •• FlightsService.SupplementTypes •• sap.capire.flights.Flights # [!code --] ••• sap.capire.flights.FlightConnections # [!code --] •••• sap.capire.flights.Airlines # [!code --] @@ -271,8 +271,8 @@ Kept: 6 # [!code ++] ••• sap.capire.flights.SupplementTypes # [!code --] •••• sap.capire.flights.SupplementTypes.texts # [!code --] ••• sap.capire.flights.Supplements.texts # [!code --] - ••• sap.capire.flights.data.SupplementTypes.texts # [!code --] - •• sap.capire.flights.data.Supplements.texts # [!code --] + ••• FlightsService.SupplementTypes.texts # [!code --] + •• FlightsService.Supplements.texts # [!code --] Skipped: 11 # [!code --] Skipped: 31 # [!code ++] @@ -332,10 +332,10 @@ cds export srv/data-service.cds --data ``` ```log - > apis/data-service/data/sap.capire.flights.data.Flights.csv - > apis/data-service/data/sap.capire.flights.data.Airlines.csv - > apis/data-service/data/sap.capire.flights.data.Airports.csv - > apis/data-service/data/sap.capire.flights.data.Supplements.csv + > apis/data-service/data/FlightsService.Flights.csv + > apis/data-service/data/FlightsService.Airlines.csv + > apis/data-service/data/FlightsService.Airports.csv + > apis/data-service/data/FlightsService.Supplements.csv ``` The `.csv` data comes from the source application's initial data, filtered and transformed for the exposed entities, including denormalizations and calculated fields. The application actually reads it via an instance of that service. @@ -364,7 +364,7 @@ This would add this to the generated output: "version": "0.1.13", "cds": { // [!code focus] "requires": { // [!code focus] - "sap.capire.flights.data": true // [!code focus] + "FlightsService": true // [!code focus] } // [!code focus] } // [!code focus] } @@ -459,7 +459,7 @@ This allows us to update imported APIs later on using standard commands like `np You can also `cds import` APIs from other sources, such as OData APIs for customer data from SAP S/4 HANA systems: -1. Get an [_OData EDMX_](https://api.sap.com/api/API_BUSINESS_PARTNER/overview) source, e.g., from [*SAP Business Accelerator Hub*](https://api.sap.com): +1. Get an [_OData EDMX_](https://api.sap.com/api/API_BUSINESS_PARTNER/overview) source, for example, from [*SAP Business Accelerator Hub*](https://api.sap.com): ::: details Detailed steps through SAP Business Accelerator Hub ... - Open https://api.sap.com in your browser @@ -599,7 +599,7 @@ Create two new files `apis/capire/xflights.cds` and `apis/capire/s4.cds`: ::: code-group ```cds :line-numbers [apis/capire/xflights.cds] -using { sap.capire.flights.data as x } from '@capire/xflights-data'; +using { FlightsService as x } from '@capire/xflights-data'; namespace sap.capire.xflights; @federated entity Flights as projection on x.Flights { @@ -630,7 +630,7 @@ namespace sap.capire.s4; The noteworthy aspects here are: -- We map names to match our domain, e.g., `A_Business_Partner` -> `Customers`, and choose simpler names for the elements we want to use. +- We map names to match our domain, for example, `A_Business_Partner` -> `Customers`, and choose simpler names for the elements we want to use. - For entity `Flights` we flatten data from associations directly into the consumption view. This is another [denormalization](#using-denormalized-views) to make life easier for us in the xtravels app. @@ -782,7 +782,7 @@ With mashed up models in place, we can run applications in _'airplane mode'_ wit } ``` ```zsh - [cds] - mocking sap.capire.flights.data { + [cds] - mocking FlightsService { at: [ '/odata/v4/data', '/rest/data', '/hcql/data' ], decl: 'xflights/apis/data-service/services.csn:3' } @@ -825,7 +825,7 @@ While everything just works nicely when mocked in-process and with a shared in-m } ``` ```zsh - [cds] - connect to sap.capire.flights.data > hcql { + [cds] - connect to FlightsService > hcql { url: 'http://localhost:54475/hcql/data' } ``` @@ -853,16 +853,16 @@ It all starts with connecting to remote services, which we do like that in the x ```js :line-numbers=21 [srv/travel-service.js] const s4 = await cds.connect.to ('sap.capire.s4.business-partner') -const xflights = await cds.connect.to ('sap.capire.flights.data') +const xflights = await cds.connect.to ('FlightsService') ``` ::: The `cds.connect.to()` function used here is the single common way to address service instances. It's used for and works the same way for both, local as well as remote services: -- for **local** services, it returns the local service providers – i.e., instances of [`cds.ApplicationService`](../../node.js/app-services), or your application-specific subclases thereof. +- for **local** services, it returns the local service providers – that is, instances of [`cds.ApplicationService`](../../node.js/app-services), or your application-specific subclasses thereof. -- for **remote** services, it returns a remote service proxy – i.e., instances of [`cds.RemoteService`](../../node.js/remote-services), generically constructed by the client libs. +- for **remote** services, it returns a remote service proxy – that is, instances of [`cds.RemoteService`](../../node.js/remote-services), generically constructed by the client libs. ![Diagram illustrating CAP-level service integration showing two scenarios: Local services where Consumer connects to Service via CQL, and Remote services where Consumer connects to Proxy via CQL, Proxy connects to Protocol Adapter via OData, and Protocol Adapter connects to Service via CQL. ](assets/remoting.drawio.svg) @@ -926,7 +926,7 @@ Within the REPL, connect to local and remote services: ```js const TravelService = await cds.connect.to ('TravelService') -const xflights = await cds.connect.to ('sap.capire.flights.data') +const xflights = await cds.connect.to ('FlightsService') const s4 = await cds.connect.to ('sap.capire.s4.business-partner') ``` @@ -1080,7 +1080,7 @@ await s4.run (q2) > Modifying queries prior to forwarding them to remote services is a powerful technique to implement advanced integration scenarios. For example, you can adapt queries to the capabilities of target services, implement custom filtering, paging, or sorting logic, or even split and merge queries across multiple services. ::: details First-Class Query Objects -On a side note: We leverage key principles of [_first-class objects_](https://google.com/search?q=first+class+objects+programming) here, as known from functional programming and dynamic languages: As queries are represented as first-class CQN objects, we can construct and manipulate them programmatically at runtime, pass them as arguments, and return them from functions. And, not the least, this opens the doors for things like higher-order queries, query delegation – e.g. push down to databases –, and late materialization. +On a side note: We leverage key principles of [_first-class objects_](https://google.com/search?q=first+class+objects+programming) here, as known from functional programming and dynamic languages: As queries are represented as first-class CQN objects, we can construct and manipulate them programmatically at runtime, pass them as arguments, and return them from functions. And, not the least, this opens the doors for things like higher-order queries, query delegation – for example push down to databases –, and late materialization. ::: > [!warning] Always Clone Before Modifying @@ -1110,7 +1110,7 @@ annotate x.Flights with @cds.persistence.table; 2. Implement logic to replicate updated data, for example like that: ```js [srv/data-replication.js] -const xflight = await cds.connect.to ('sap.capire.flights.data') +const xflight = await cds.connect.to ('FlightsService') const {Flights} = cds.entities ('sap.capire.xflights') let {latest} = await SELECT.one`max(modifiedAt) as latest`.from (Flights) let touched = await xflight.read (Flights).where`modifiedAt > ${latest||0}` @@ -1200,7 +1200,7 @@ Note that for the handler above, incoming requests always refer to: In effect, we are delegating a query to the S/4 service, which refers to an entity actually not known to that remote service. How could that work at all? -It works because we fuelled the CAP runtime with CDS models, so the generic handlers detect such situations, and automatically translate delegated queries into valid queries targeted to underlying remote entities – i.e. `A_BusinessPartner` in our example. When doing so, all column references in select clauses, where clauses, etc., are translated and delegated as well, and the results' structure transformed back to that of the original target – i.e., `TravelService.Customers` above. +It works because we fuelled the CAP runtime with CDS models, so the generic handlers detect such situations, and automatically translate delegated queries into valid queries targeted to underlying remote entities – that is, `A_BusinessPartner` in our example. When doing so, all column references in select clauses, where clauses, etc., are translated and delegated as well, and the results' structure transformed back to that of the original target – that is, `TravelService.Customers` above. @@ -1238,7 +1238,7 @@ await SELECT.from (Bookings) .where`Flight.ID in ${flightIDs}` ::: > [!tip] What is 'Navigation'? -> The term 'navigation' commonly refers to traversing associations between entities in queries. In CAP, this is typically expressed using [path expressions](../../cds/cql#path-expressions) along (chains of) associations – e.g., `flight.origin.name` –, which can show up in all query clauses (_select_, _from_, _where_, _order by_, and _group by_). +> The term 'navigation' commonly refers to traversing associations between entities in queries. In CAP, this is typically expressed using [path expressions](../../cds/cql#path-expressions) along (chains of) associations – for example, `flight.origin.name` –, which can show up in all query clauses (_select_, _from_, _where_, _order by_, and _group by_). ### Expands diff --git a/guides/integration/data-federation.md b/guides/integration/data-federation.md index 5dda8407ed..6578c08ba8 100644 --- a/guides/integration/data-federation.md +++ b/guides/integration/data-federation.md @@ -12,12 +12,12 @@ CAP applications can integrate and federate data from multiple external data sou You should be familiar with the content in the [_CAP-level Service Integration_](calesi.md) guide, as we build upon that foundation here. In particular, you should have read and understood these sections: -- [*Overview* of *CAP-level Service Integration*](calesi.md#overview) +- [*Overview* of *CAP-level Service Integration*](calesi.md#overview) - [_Providing & Exporting APIs_](calesi.md#providing-cap-level-apis) - [_Importing APIs_](calesi.md#importing-apis) - [_Consumption Views_](calesi.md#consumption-views) -In addition, you should have read the introduction to [the _XTravels_ sample](calesi.md#the-xtravels-sample) application, which we continue to use as our running example. +In addition, you should have read the introduction to [the _XTravels_ sample](calesi.md#the-xtravels-sample) application, which we continue to use as our running example. @@ -80,7 +80,7 @@ We'll use the same [XTravels sample](calesi.md#the-xtravels-sample) and setup as ```sh :line-numbers mkdir -p cap/samples cd cap/samples -git clone https://github.com/capire/xtravels +git clone https://github.com/capire/xtravels git clone https://github.com/capire/xflights git clone https://github.com/capire/s4 ``` @@ -95,14 +95,14 @@ npm install ``` > [!note] -> Line 6 above turns the `cap/samples` folder into a root for `npm workspaces` to optimize the `npm install` locally. -> We'll learn more about that in the [_Inner Loop Development guide_](inner-loops.md). +> Line 6 above turns the `cap/samples` folder into a root for `npm workspaces` to optimize the `npm install` locally. +> We'll learn more about that in the [_Inner Loop Development guide_](inner-loops.md). ## Federated Consumption Views -Tag [consumption views](calesi#consumption-views) with the `@federated` annotation, to express your intent to have that data federated, i.e. in close access locally. For example, we did so in our consumption view for entities imported from XFlights as well as for the S/4 Business Partners entity: +Tag [consumption views](calesi#consumption-views) with the `@federated` annotation, to express your intent to have that data federated, that is, in close access locally. For example, we did so in our consumption view for entities imported from XFlights as well as for the S/4 Business Partners entity: ::: code-group @@ -125,7 +125,7 @@ Tag [consumption views](calesi#consumption-views) with the `@federated` annotati ::: > [!tip] Stay Intentional -> What, not how! -> Minimal Assumptions -> +> > By tagging entities with `@federated` we stay _intentional_ about **_what_** we want to achieve, and avoid any premature assumptions about **_how_** things are actually implemented. => This allows CAP runtimes – or your own _generic_ solutions, as in this case – to choose the best possible implementation strategies for the given environment and use case, which may differ between development, testing, and production environments, or might need to evolve over time. @@ -151,7 +151,7 @@ PROD || cds.on ('loaded', csn => { } } }) - + // Setup and schedule replications for all collected entities PROD || cds.once ('served', () => Promise.all (feed.map (async each => { const srv = await cds.connect.to (each.remote) @@ -160,11 +160,11 @@ PROD || cds.once ('served', () => Promise.all (feed.map (async each => { }))) // Event handler for replicating single entities -async function replicate (req) { +async function replicate (req) { let { entity } = req.data, remote = this let { latest } = await SELECT.one `max(modifiedAt) as latest` .from (entity) let rows = await remote.run ( - SELECT.from (entity) .where `modifiedAt > ${latest}` + SELECT.from (entity) .where `modifiedAt > ${latest}` ) if (rows.length) await UPSERT (rows) .into (entity); else return console.log ('Replicated', rows.length, 'entries', { for: entity, via: this.kind }) @@ -180,10 +180,10 @@ Let's have a closer look at this code, which handles these main tasks: 1. **Prepare Persistence** – When the model is `loaded`, before it's deployed to the database, we collect all to be `@federated` entities, check whether their respective services are remote, and if so, turn them into tables for local replicas (line 11). 2. **Setup Replication** – Later when all services are `served`, we connect to each remote one (line 20), register a handler for replication (line 21), and schedule it to be invoked repeatedly (line 22). -3. **Replicate Data** – Finally, the `replicate` handler implements a simple polling-based data federation strategy, based on `modifiedAt` timestamps (lines 28-32), with the actual call to remote happening on line 29. +3. **Replicate Data** – Finally, the `replicate` handler implements a simple polling-based data federation strategy, based on `modifiedAt` timestamps (lines 28-32), with the actual call to remote happening on line 29. > [!tip] CAP-level Querying -> agnostic to databases & protocols -> We work with **database-agnostic** and **protocol-agnostic** [CQL queries](../../cds/cql) both for interacting with the local database as well as for querying remote services. In effect, we got a fully generic solution for replication, i.e., it works for **_any_** remote service that supports OData, or HCQL. +> We work with **database-agnostic** and **protocol-agnostic** [CQL queries](../../cds/cql) both for interacting with the local database as well as for querying remote services. In effect, we got a fully generic solution for replication, that is, it works for **_any_** remote service that supports OData, or HCQL. ## Test Drive Locally @@ -230,7 +230,7 @@ While the xflights service in terminal 2 shows its incoming HCQL requests like t ```zsh [hcql] - GET /hcql/data/ { SELECT: { - from: { ref: [ 'sap.capire.flights.data.Flights' ] }, + from: { ref: [ 'FlightsService.Flights' ] }, columns: [ { ref: [ 'ID' ], as: 'ID' }, { ref: [ 'date' ], as: 'date' }, @@ -260,12 +260,12 @@ Finally, open the Fiori UI in the browser again, and see that customer data from diff --git a/guides/integration/inner-loops.md b/guides/integration/inner-loops.md index bb5bce38c3..bca75d1c0b 100644 --- a/guides/integration/inner-loops.md +++ b/guides/integration/inner-loops.md @@ -12,11 +12,11 @@ CAP promotes fast inner-loop development by allowing us to easily swap productio ### What is Inner Loop? -![inner-loop-turntable](assets/inner-loop-turntable.png){.ignore-dark style="width:50%; border-radius: 22px; float: right; margin-top: -3em"} +![inner-loop-turntable](assets/inner-loop-turntable.png){.ignore-dark style="width:50%; border-radius: 22px; float: right; margin-top: -3em"} -Many of us likely remember that turntable thing in the playgrounds: stay close to the center – the inner loop –, and it rotates at ultimate speed, lean out and it slows down. +Many of us likely remember that turntable thing in the playgrounds: stay close to the center – the inner loop –, and it rotates at ultimate speed, lean out and it slows down. -We see similar effects when running through full *code - build - deploy - start* cycles to see the effects of incremental changes in overly cloud-based development models. And it's not only the turnaround times for individual developers, it's also the runtime for tests, the operating costs induced by both, the impact on support (local setups allow to reproduce things, complex setups don't), up to severe resilience issues (whenever a cloud service isn't available development stops for whole teams). +We see similar effects when running through full *code - build - deploy - start* cycles to see the effects of incremental changes in overly cloud-based development models. And it's not only the turnaround times for individual developers, it's also the runtime for tests, the operating costs induced by both, the impact on support (local setups allow to reproduce things, complex setups don't), up to severe resilience issues (whenever a cloud service isn't available development stops for whole teams). Here's a very rough comparison from a real world example: @@ -37,7 +37,7 @@ We'll use the same [XTravels sample](calesi.md#the-xtravels-sample) and setup as ```sh :line-numbers mkdir -p cap/samples cd cap/samples -git clone https://github.com/capire/xtravels +git clone https://github.com/capire/xtravels git clone https://github.com/capire/xflights git clone https://github.com/capire/s4 ``` @@ -53,7 +53,7 @@ npm install > [!note] > -> Line 6 above turns the `cap/samples` folder into a root for `npm workspaces`. For the time being this simply optimizes the `npm install`. We'll revisit that in chapter [*Using `npm` Workspaces*](#using-npm-workspaces) below. +> Line 6 above turns the `cap/samples` folder into a root for `npm workspaces`. For the time being this simply optimizes the `npm install`. We'll revisit that in chapter [*Using `npm` Workspaces*](#using-npm-workspaces) below. #### Activate Generic Data Federation @@ -69,7 +69,7 @@ process.env.NODE_ENV || require ('./data-federation') ## Mocked Out of the Box -Within the context of application service integration and microservice architecture, we'd need to mock remote services in a consuming app to reach an inner loop. CAP handles this automatically for us, based on: +Within the context of application service integration and microservice architecture, we'd need to mock remote services in a consuming app to reach an inner loop. CAP handles this automatically for us, based on: - A CDS service definition is all we need to serve a fully functional OData service - APIs imported via `cds export` and `cds import` are CDS service definition @@ -97,7 +97,7 @@ With mashed up models in place, we can run applications in _'airplane mode'_ wit ``` ```zsh - [cds] - mocking sap.capire.flights.data { + [cds] - mocking FlightsService { at: [ '/odata/v4/data', '/rest/data', '/hcql/data' ], decl: 'xflights/apis/data-service/services.csn:3' } @@ -133,7 +133,7 @@ We can also use `cds mock` to mock remote services in separate processes, which } ``` ```zsh - [cds] - connect to sap.capire.flights.data > hcql { + [cds] - connect to FlightsService > hcql { url: 'http://localhost:54475/hcql/data' } ``` @@ -159,15 +159,15 @@ We can also use `cds mock` to mock remote services in separate processes, which -### Providing Mock Data +### Providing Mock Data There are different options to provide initial data, test data, and mock data: -- In case of `@capire/xflights-data`, we generated the package content using `cds export --data` option, which added `.csv` files next to the `.cds` files. -- In case of `@capire/s4`, we explicitly added `.csv` files next to the `.cds` files. +- In case of `@capire/xflights-data`, we generated the package content using `cds export --data` option, which added `.csv` files next to the `.cds` files. +- In case of `@capire/s4`, we explicitly added `.csv` files next to the `.cds` files. - In addition, we could add `.csv` files for imported entities in the consuming apps `db/data` or `test/data` folders. -In all cases, the `.csv` files are placed next to the `.cds` files, and hence they are automatically detected and loaded into the in-memory database. +In all cases, the `.csv` files are placed next to the `.cds` files, and hence they are automatically detected and loaded into the in-memory database. For Java, make sure to add the `--with-mocks` option to the `cds deploy` command used to generate the `schema.sql` in `srv/pom.xml`. This ensures that tables for the mocked remote entities are created in the database. @@ -177,9 +177,9 @@ For Java, make sure to add the `--with-mocks` option to the `cds deploy` command -## Run with Real Services +## Run with Real Services -Instead of mocking required services by the imported APIs [using `cds mock` as shown above](#cds-mock), we can also run the real *xflights* and *s4* services from their respective home folders which we [cloned already in the beginning](#the-xtravels-sample). +Instead of mocking required services by the imported APIs [using `cds mock` as shown above](#cds-mock), we can also run the real *xflights* and *s4* services from their respective home folders which we [cloned already in the beginning](#the-xtravels-sample). Do so by running the following commands from within the `cap/samples` root folder in separate terminals, and in that order: @@ -200,7 +200,7 @@ In the log output of the xtravels server we should see that it _connects_ to the } ``` ```zsh -[cds] - connect to sap.capire.flights.data > hcql { +[cds] - connect to FlightsService > hcql { url: 'http://localhost:54475/hcql/data' } ``` @@ -231,7 +231,7 @@ Within the REPL, connect to local and remote services: ```js const TravelService = await cds.connect.to ('TravelService') -const xflights = await cds.connect.to ('sap.capire.flights.data') +const xflights = await cds.connect.to ('FlightsService') const s4 = await cds.connect.to ('sap.capire.s4.business-partner') ``` @@ -323,7 +323,7 @@ await s4.delete (Customers,'123') await s4.delete (Customers) .where`ID = ${'456'}` ``` -Go on like that and try out similar requests with the other services, that is, `TravelService` and `xflights`. For the latter you might run into `401` errors, in that case run the following once in the REPL to run in privileged mode: +Go on like that and try out similar requests with the other services, that is, `TravelService` and `xflights`. For the latter you might run into `401` errors, in that case run the following once in the REPL to run in privileged mode: ```js cds.User.default = cds.User.privileged @@ -336,7 +336,7 @@ cds.User.default = cds.User.privileged ## Using `npm` Workspaces -So far we assumed we mainly worked within the *xtravels* project, and we consumed the API from xflights via `npm publish` / `npm install`. There might be situations where we would want to shortcut this process. For example, we might want to consume a very latest version of the xflights API, which is not yet published to the *npm* registry. Or we might even want to work on both projects simultaneously, and test our latest changes to *xflights* in *xtravels* in close loops. +So far we assumed we mainly worked within the *xtravels* project, and we consumed the API from xflights via `npm publish` / `npm install`. There might be situations where we would want to shortcut this process. For example, we might want to consume a very latest version of the xflights API, which is not yet published to the *npm* registry. Or we might even want to work on both projects simultaneously, and test our latest changes to *xflights* in *xtravels* in close loops. So, in essence, instead of exercising a workflow like that again and again: @@ -347,7 +347,7 @@ So, in essence, instead of exercising a workflow like that again and again: ```sh :line-numbers mkdir -p cap/samples cd cap/samples -git clone https://github.com/capire/xtravels +git clone https://github.com/capire/xtravels git clone https://github.com/capire/xflights git clone https://github.com/capire/s4 ``` @@ -375,7 +375,7 @@ samples@ ~/cap/samples └── @capire/xflights-data@0.1.11 deduped -> ./xflights/apis/data-service ``` -Start the xtravels application → and note the sources loaded from *./xflights/apis/data-service*, and the information further below about the `sap.capire.flights.data` service mocked automatically: +Start the xtravels application → and note the sources loaded from *./xflights/apis/data-service*, and the information further below about the `FlightsService` service mocked automatically: ```shell cds watch xtravels @@ -393,7 +393,7 @@ cds watch xtravels ``` ```zsh -[cds] - mocking sap.capire.flights.data { +[cds] - mocking FlightsService { at: [ '/odata/v4/data', '/rest/data', '/hcql/data' ], decl: 'xflights/apis/data-service/services.csn:3', } @@ -480,7 +480,7 @@ samples@ ~/cap/samples └── @capire/xflights-data@ deduped -> ./xflights-api-shortcut≤ ``` -Start the *xtravels* application → and note the sources loaded from *./xflights-api-shortcut*, and the information further below about the `sap.capire.flights.data` service now being _served_, not _mocked_ anymore: +Start the *xtravels* application → and note the sources loaded from *./xflights-api-shortcut*, and the information further below about the `FlightsService` service now being _served_, not _mocked_ anymore: ```shell cds watch xtravels @@ -494,12 +494,12 @@ cds watch xtravels xtravels/db/xflights.cds xflights-api-shortcut/index.cds xflights/srv/data-service.cds - xflights/db/schema.cds + xflights/db/schema.cds ... ``` ```zsh -[cds] - serving sap.capire.flights.data { +[cds] - serving FlightsService { at: [ '/odata/v4/data', '/rest/data', '/hcql/data' ], decl: 'xflights/apis/data-service/services.csn:3', } diff --git a/guides/integration/platform/attachments.md b/guides/integration/platform/attachments.md index b40c250afe..2a4a16a5d4 100644 --- a/guides/integration/platform/attachments.md +++ b/guides/integration/platform/attachments.md @@ -41,7 +41,7 @@ } ``` - 5. **Implement Attachment Logic**: In your service implementation file (e.g., `srv/your-service.js`), implement the logic to handle attachment operations such as upload, download, and delete. + 5. **Implement Attachment Logic**: In your service implementation file (for example, `srv/your-service.js`), implement the logic to handle attachment operations such as upload, download, and delete. 6. **Test Your Application**: Run your CAP application and test the attachment functionality to ensure everything is working as expected. diff --git a/guides/integration/platform/ias-xsuaa.md b/guides/integration/platform/ias-xsuaa.md index 0b736e0cc3..19ffc4fc25 100644 --- a/guides/integration/platform/ias-xsuaa.md +++ b/guides/integration/platform/ias-xsuaa.md @@ -11,7 +11,7 @@ It allows users to authenticate using various methods, including social logins, When integrated with CAP applications, IAS can serve as the primary identity provider, managing user identities and authentication flows. -To integrate IAS with a CAP application, you typically need to configure the application to trust IAS as an identity provider and set up the necessary SSO protocols (e.g., SAML, OAuth2). +To integrate IAS with a CAP application, you typically need to configure the application to trust IAS as an identity provider and set up the necessary SSO protocols (for example, SAML, OAuth2). For more details on configuring IAS with CAP applications, refer to the [SAP IAS documentation](https://help.sap.com/viewer/product/SAP_IDENTITY_AUTHENTICATION/). ## SAP Authorization and Trust Management Service (XSUAA) XSUAA is a service that provides authentication and authorization capabilities for applications running on SAP Business Technology Platform (BTP). diff --git a/guides/integration/service-bindings.md b/guides/integration/service-bindings.md index f6eac5bdcc..649469273f 100644 --- a/guides/integration/service-bindings.md +++ b/guides/integration/service-bindings.md @@ -28,7 +28,7 @@ The key names under `cds.requires` (here: `ReviewsService` and `OrdersService`) const ReviewsService = await cds.connect.to ('ReviewsService') ``` -Configurations for required services can also be provided in a plug & play manner by CAP plugins. For example, the [_@capire/xtravels_](https://github.com/capire/xtravels) sample application automatically requires two services, provided by the CAP plugins [_@capire/s4_](https://github.com/capire/s4) and [_@capire/xflights_](https://github.com/capire/xflights/blob/main/apis/data-service). +Configurations for required services can also be provided in a plug & play manner by CAP plugins. For example, the [_@capire/xtravels_](https://github.com/capire/xtravels) sample application automatically requires two services, provided by the CAP plugins [_@capire/s4_](https://github.com/capire/s4) and [_@capire/xflights_](https://github.com/capire/xflights/blob/main/apis/data-service). [Learn more about that in the _CAP-level Service Integration_ guide.](calesi#packaged-apis) {.learn-more} @@ -55,8 +55,8 @@ You can inspect the effective configurations of declared required services using cds env requires ``` ```zsh -'sap.capire.flights.data': { - kind: '*' +'FlightsService': { + kind: '*' }, ``` ```zsh @@ -79,7 +79,7 @@ cds mock apis/capire/s4.cds cds env requires -b ``` ```zsh -'sap.capire.flights.data': { +'FlightsService': { kind: 'hcql', impl: '@sap/cds/srv/remote-service.js', credentials: { url: 'http://localhost:51441/hcql/data' } diff --git a/guides/multitenancy/index.md b/guides/multitenancy/index.md index 173d8eed70..043093c2f5 100644 --- a/guides/multitenancy/index.md +++ b/guides/multitenancy/index.md @@ -676,7 +676,7 @@ There are several ways to update the database schema of a multitenant applicatio * For **CAP Java** applications, schema updates should be done as described in the respective [Java Guide](../../java/multitenancy#database-update). * For **CAP Node.js** applications, you can use either of the following as shown in the examples below: - the `cds-mtx upgrade` command from a terminal - - the [MTX Sidecar API](mtxs#upgrade-tenants-→-jobs) + - the [MTX Sidecar API](mtxs#upgrade-tenants--jobs) - via a [CloudFoundry hook](https://help.sap.com/docs/btp/sap-business-technology-platform/module-hooks) - via a [CloudFoundry task](https://tutorials.cloudfoundry.org/cf4devs/advanced-concepts/tasks/) - via a [Kubernetes job](https://kubernetes.io/docs/concepts/workloads/controllers/job/) @@ -894,13 +894,19 @@ To help ensure that the generated SAP HANA tenant UUID is unique within a region like `prefix-${org}-${space}` in Cloud Foundry. :::warning Prefix is mandatory -The cds/requires/cds.xt.DeploymentService/hdi/create/hana_tenant_prefix configuration is mandatory to ensure that the internal tenant `t0` is created with its own SAP HANA tenant. +The cds/requires/cds.xt.DeploymentService/hdi/create/hana_tenant_prefix configuration is mandatory to ensure that the internal tenant `t0` is created with its own SAP HANA tenant. ::: :::warning Length restriction The prefix and subscriber tenant name are each limited to 63 characters maximum. ::: +:::warning Do not use `hana_tenant_prefix` in queries +The `hana_tenant_prefix` is only used to generate the SAP HANA tenant UUID to avoid duplicates when creating an +SAP HANA tenant. Although it is added as `prefix` label to the SAP HANA tenant, it is not reliable and must not +be used for any query. +::: + ##### Mandatory for CAP Java Applications For **CAP Java** applications you need to set the same prefix in the cds.multitenancy.hanaMtService.hanaTenantPrefix property. We recommend doing this using the environment variable in the _mta.yaml_: @@ -913,44 +919,55 @@ For **CAP Java** applications you need to set the same prefix in the ")); -} -``` +: If you choose this options, the following preconditions need to be met +- The subscriber tenant ID (BTP tenant ID) needs to be the same for all applications or microservices. +- All applications or microservices need to use the [same database](#configure-mtxs-for-tenant-management-service). + +**Option 2: Pass the SAP HANA Tenant ID with a Subscription** + +: **... in CAP Node.js** + + If you want to control the ID of the SAP HANA tenant ID on your own, you can pass it as subscription payload as parameters, for example, using a handler for the [`SaasRegistryService`](./mtxs#put-tenant): + ```jsonc + { + "subscribedTenantId": "t1", + "subscribedSubdomain": "subdomain1", + "eventType": "CREATE" + ... + "_": { + "hdi": { + "create": { + "hana_tenant_id": "5b3c0699-1c65-4ec1-9a8e-b7cfc3cc15bc" + } + } + } + } + ``` + The `hana_tenant_id` must be a valid UUID and must be unique per subscriber tenant. Specifying `hana_tenant_id` overrides the prefix settings mentioned earlier, + except for the internal tenant `t0`. Also ensure that the ID is unique within a region. + +: **... in CAP Java** + + To specify the ID of the SAP HANA tenant in **CAP Java** applications you can register a custom handler for the `before` phase of the `SUBSCRIBE` event that sets the `hana_tenant_id` within the provisioning parameters: + + ```java + @Before + public void beforeSubscription(SubscribeEventContext context) { + context.getOptions().put("provisioningParameters", + Collections.singletonMap("hana_tenant_id", "")); + } + ``` -This will affect every new [tenant subscription](../../java/multitenancy.md#subscribe-tenant) and will set the specified SAP HANA tenant ID. + This will affect every new [tenant subscription](../../java/multitenancy.md#subscribe-tenant) and will set the specified SAP HANA tenant ID.
@@ -965,8 +982,6 @@ There are still some limitations with the current client implementation. - **Database ID is Mandatory** As mentioned, you need to specify a database ID that's to be used, either for all tenants or per subscription request, see [Deployment configuration](./mtxs#deployment-config). -- [`clusterSize` configuration](./mtxs#saas-provisioning-config) needs to be set to `1` (default is `3`). HANA TMS v2 does not provide a performant way to determine all database IDs, -so clustering the upgrade by database does not work properly. ## SaaS Dependencies {#saas-dependencies} diff --git a/guides/multitenancy/mtxs.md b/guides/multitenancy/mtxs.md index 1e617a84f6..f7c9ce0434 100644 --- a/guides/multitenancy/mtxs.md +++ b/guides/multitenancy/mtxs.md @@ -1308,6 +1308,11 @@ The _SaasProvisioningService_ is a façade for the _DeploymentService_ to adapt - `clusterSize` — max number of database clusters, running `workerSize` jobs each - `queueSize` — max number of jobs waiting to run in the job queue +:::warning clusterSize configuration is not available with HANA TMS v2 +When using [HANA TMS v2](../multitenancy/index.md#sap-hana-tms-v2), the cds/requires/cds.xt.SaasProvisioningService/jobs/clusterSize is automatically set to `1`. HANA TMS v2 currently does not + provide a performant way to determine all database IDs, so clustering the upgrade by database does not work properly. +::: + #### HTTP Request Options | Request Header | Example Value | Description | diff --git a/guides/protocols/mcp.md b/guides/protocols/mcp.md index 71ea6da4c6..96268437ea 100644 --- a/guides/protocols/mcp.md +++ b/guides/protocols/mcp.md @@ -13,7 +13,7 @@ The [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) is an open- > [!note] > -> This guide is about the *MCP Adapter* – e.g. as provided through the [*@cap-js/mcp*](https://github.com/cap-js/mcp) plugin – which powers domain-specific application use cases, for example, to respond to questions for CAP-based applications like *"List all overstocked books"*. +> This guide is about the *MCP Adapter* – for example, as provided through the [*@cap-js/mcp*](https://github.com/cap-js/mcp) plugin – which powers domain-specific application use cases, for example, to respond to questions for CAP-based applications like *"List all overstocked books"*. > > In parallel, there's also the *MCP Server* plugin ([*@cap-js/mcp-server*](https://github.com/cap-js/mcp-server)), which serves a different purpose, though, that is: AI-assisted *development* of CAP projects. @@ -106,7 +106,7 @@ INFO com.sap.cds.adapter.mcp.McpServlet : MCP Server initialized at endpoint '/m ``` ::: -You can also specify an alternative path under which the MCP server should be served, e.g. like that: +You can also specify an alternative path under which the MCP server should be served as follows: ```cds annotate CatalogService with @mcp:'books' @@ -184,17 +184,14 @@ To consume them, you need an MCP client. For local testing, you can use tools li brew install claude-code ``` -2. Configure it to use the Hyperspace proxy: - ```sh - hai configure claude-code - ``` - -3. Optionally add [Claude Code for VSCode](https://marketplace.visualstudio.com/items?itemName=anthropic.claude-code): +2. Optionally add [Claude Code for VSCode](https://marketplace.visualstudio.com/items?itemName=anthropic.claude-code): ```shell code --install-extension anthropic.claude-code ``` +
+ ### Using OpenCode 1. Install [OpenCode](https://opencode.ai/), for example via npm: @@ -203,8 +200,7 @@ To consume them, you need an MCP client. For local testing, you can use tools li npm i -g opencode-ai ``` -2. Configure it to use LLMs through the Hyperspace proxy, or an existing GitHub Copilot setup, by following the instructions in the [Hyperspace documentation](https://ai-docs.portal.hyperspace.tools.sap/llm-proxy/recipes/opencode/) - +
### Run your CAP server @@ -378,7 +374,7 @@ During startup, the generated MCP servers and their URL are added to the client- When the application stops, the added configuration is removed. This is only intended for local development. > [!warning] For Development Only -> The autowiring is only enabled during development (i.e., when running `cds watch` or `mvn cds:watch`) and not meant for production use cases. +> The autowiring is only enabled during development (that is, when running `cds watch` or `mvn cds:watch`) and not meant for production use cases. #### Mock Authentication @@ -462,7 +458,7 @@ npx @modelcontextprotocol/inspector The inspector should automatically open in your browser. 1. Select _Streamable HTTP_ as Transport Type. -2. Enter the URL of your service (e.g., `http://localhost:4004/mcp/browse`). +2. Enter the URL of your service - for example, `http://localhost:4004/mcp/browse`. 3. Select `Via Proxy` as connection type and select _Connect_. 4. Go to the _Tools_ tab and select _List Tools_. 5. To get data, select the _query_ tool. @@ -496,4 +492,4 @@ Future versions of the adapter may add support for data changes using CREATE, UP > [!caution] > The MCP adapter does not perform any input validation or output validation regarding prompt injections. -> Hence, for production use cases ensure you use infrastructure and practices that mitigate prompt injection risks and connect only to trusted MCP agents (e.g., Joule). +> Hence, for production use cases ensure you use infrastructure and practices that mitigate prompt injection risks and connect only to trusted MCP agents (for example, Joule). diff --git a/guides/security/authentication.md b/guides/security/authentication.md index cf79bd2edb..33c2fddd40 100644 --- a/guides/security/authentication.md +++ b/guides/security/authentication.md @@ -336,7 +336,7 @@ Integration tests running in production profile should verify that unauthenticat
[Learn more about testing with authenticated endpoints.](../../node.js/cds-test#authentication){.learn-more} -[Learn more about testing.](../../node.js/cds-test#testing-with-cds-test){.learn-more} +[Learn more about testing.](../../node.js/cds-test#testing-with-cdstest){.learn-more}
@@ -1252,7 +1252,7 @@ For these scenarios, the CAP Node.js runtime allows to specify an implementation :::warning Be **very** careful when creating your own `auth` implementation. -This should be a last resort for when every other possible solution (e.g. through [modelling](./authorization.md#restrictions) or by [configuration](#pluggable-authentication)) has been investigated and dismissed. +This should be a last resort for when every other possible solution (for example through [modelling](./authorization.md#restrictions) or by [configuration](#pluggable-authentication)) has been investigated and dismissed. ::: Like any other [custom middleware](../../node.js/cds-serve.md#custom-middlewares), the auth middleware you create needs to accept express's `req`, `res` and `next` and end up by sending a response, throwing an error or calling `next()`. @@ -1290,7 +1290,7 @@ module.exports = function custom_auth (req, res, next) { --> :::tip -In case you want to customize the `cds.context.user`, check out [this example](../../node.js/cds-serve#customization-of-cds-context-user). +In case you want to customize the `cds.context.user`, check out [this example](../../node.js/cds-serve#customization-of-cdscontextuser). :::
diff --git a/guides/security/authorization.md b/guides/security/authorization.md index 6b6d468cd4..3b64d13311 100644 --- a/guides/security/authorization.md +++ b/guides/security/authorization.md @@ -180,7 +180,7 @@ annotate ShopService.ReplicationAction with @(requires: 'system-user'); In this example, the `BrowseBooksService` service is open for authenticated but not for anonymous users. A user who has the `Vendor` _or_ `ProcurementManager` role is allowed to access the `ShopService.Books` entity. Unbound action `ShopService.ReplicationAction` can only be triggered by a technical user. ::: tip -When restricting service access through `@requires`, the service's metadata endpoints (that is, `/$metadata` as well as the service root `/`) are restricted by default as well. If you require public metadata, you can disable the check with [a custom express middleware](../../node.js/cds-serve#add-mw-pos) using the [privileged user](../../node.js/authentication#privileged-user) (Node.js) or through config cds.security.authentication.authenticateMetadataEndpoints = false (Java), respectively. Please be aware that the `/$metadata` endpoint is *not* checking for authorizations implied by `@restrict` annotation. +When restricting service access through `@requires`, the service's metadata endpoints (that is, `/$metadata` as well as the service root `/`) are restricted by default as well. If you require public metadata, you can disable the check with [a custom express middleware](../../node.js/cds-serve#addmw-pos) using the [privileged user](../../node.js/authentication#privileged-user) (Node.js) or through config cds.security.authentication.authenticateMetadataEndpoints = false (Java), respectively. Please be aware that the `/$metadata` endpoint is *not* checking for authorizations implied by `@restrict` annotation. ::: diff --git a/guides/security/cap-users.md b/guides/security/cap-users.md index a82fcf1347..4114c6dc4a 100644 --- a/guides/security/cap-users.md +++ b/guides/security/cap-users.md @@ -762,7 +762,7 @@ If not done yet, prepare your project Cloud deployment as [explained before](./a Policies can be automatically deployed to the AMS server during deployment of the application by means of AMS deployer provided by module `@sap/ams`. -Enhancing the project by `cds add ams` automatically adds task e.g. in the MTA for AMS policy deployment. +Enhancing the project by `cds add ams` automatically adds task for example in the MTA for AMS policy deployment.
@@ -1206,7 +1206,7 @@ In addition, there are getters to retrieve information about [pseudo-roles](#pse | `isAuthenticated()` | _True if the current user has been authenticated._ | `authenticated-user` | | `isSystemUser()` | _Indicates whether the current user has pseudo-role `system-user`._ | `system-user` | | `isInternalUser()` | _Indicates whether the current user has pseudo-role `internal-user`._ | `internal-user` | -| `isPrivileged()` | _Returns `true` if the current user runs in [privileged mode](#switching-to-privileged-user), i.e. is unrestricted._ | - | +| `isPrivileged()` | _Returns `true` if the current user runs in [privileged mode](#switching-to-privileged-user), that is is unrestricted._ | - |
diff --git a/guides/security/data-protection.md b/guides/security/data-protection.md index 5a35349c16..f32cf79ba9 100644 --- a/guides/security/data-protection.md +++ b/guides/security/data-protection.md @@ -637,7 +637,7 @@ The adapters also transform the HTTP requests into a corresponding CQN statement Access control is performed on basis of CQN level according to the CDS model and hence HTTP Verb Tampering attacks are avoided. Also HTTP method override, using `X-Http-Method-Override` or `X-Http-Method` header, is not accepted by the runtime. The OData protocol allows to encode field values in query parameters of the request URL or in the response headers. This is, for example, used to specify: -- [Pagination (implicit sort order)](../services/served-ootb#pagination-sorting) +- [Pagination (implicit sort order)](../services/served-ootb#pagination--sorting) - [Searching Data](../services/served-ootb#searching-data) - Filtering diff --git a/guides/security/remote-authentication.md b/guides/security/remote-authentication.md index b6fecac6da..d244c34db4 100644 --- a/guides/security/remote-authentication.md +++ b/guides/security/remote-authentication.md @@ -62,10 +62,10 @@ You can test CAP's built-in support for co-located services in practice by modif - **Java**: [`xflights-java`](https://github.com/capire/xflights-java/tree/main) and [`xtravels-java`](https://github.com/capire/xtravels-java/tree/main) - **Node.js**: [`xflights`](https://github.com/capire/xflights/tree/main) and [`xtravels`](https://github.com/capire/xtravels/tree/main) -`xflights` acts as a master data provider exposing basic flight data in service `sap.capire.flights.data` via different protocols. +`xflights` acts as a master data provider exposing basic flight data in service `FlightsService` via different protocols. On the client side, `xtravels` imports this service as a CAP remote service and fetches data for federation. -::: tip CAP offers +::: tip CAP offers A simplified co-located service setup by leveraging remote services that require: - Shared identity instance - URL for the destination @@ -85,7 +85,7 @@ Make sure that you've prepared a [local environment for CF deployments](../deplo #### 2. Prepare and Deploy the Consumer Application { #co-located-consumer } -As client, `xtravels` first needs a valid configuration for the remote service `sap.capire.flights.data`: +As client, `xtravels` first needs a valid configuration for the remote service `FlightsService`: ::: code-group ```yaml [Java: application.yaml] @@ -96,7 +96,7 @@ cds: remote.services: xflights: type: hcql - model: sap.capire.flights.data + model: FlightsService http: suffix: /hcql binding: @@ -109,7 +109,7 @@ cds: { "cds": { "requires": { - "sap.capire.flights.data": { + "FlightsService": { "kind": "hcql", "[production]": { "credentials": { @@ -128,7 +128,7 @@ cds: The `type` property activates the protocol for exchanging business data and must be offered by the provider [CDS service](https://github.com/capire/xflights-java/blob/6fc7c665c63bb6d73e28c11b391b1ba965b8772c/srv/data-service.cds#L24). The `model` property needs to match the fully qualified name of the CDS service from the imported model. -You can find CDS service definition of `sap.capire.flights.data` in file `target/cds/capire/xflight-data/service.cds` resolved during CDS build step. +You can find CDS service definition of `FlightsService` in file `target/cds/capire/xflight-data/service.cds` resolved during CDS build step. The `binding.name` needs to point to the shared identity instance and `options.url` together with `http.suffix` provides the required location of the remote service endpoint. Finally, `onBehalfOf: systemUser` specifies that the remote call is invoked on behalf of a technical user in context of the tenant. @@ -171,12 +171,12 @@ For production deployment, we recommend combining both services with the shared #### 3. Prepare and Deploy the Provider Application { #co-located-provider } -As server, `xflights` needs to restrict service `sap.capire.flights.data` to the technical client calling from the same application. +As server, `xflights` needs to restrict service `FlightsService` to the technical client calling from the same application. This can be done by adding pseudo-role [`internal-user`](./cap-users#pseudo-roles) to the service: ::: code-group ```cds [xflights/srv/authorization.cds] -using { sap.capire.flights.data as data } from './data-service'; +using { FlightsService as data } from './data-service'; annotate data with @(requires: 'internal-user'); ``` @@ -297,7 +297,7 @@ CAP offers App-2-App setup by leveraging remote services that require: Assuming the same local CF environment setup as [here](#prepare), clone the sample application ([`xflights-java`](https://github.com/capire/xflights-java/tree/main) or [`xflights`](https://github.com/capire/xflights/tree/main) for Node.js), or if already cloned and modified locally, reset to the remote branch. -Similar to the [co-located](#co-located-provider) variant, `xflights` needs to expose service `sap.capire.flights.data` to technical clients. +Similar to the [co-located](#co-located-provider) variant, `xflights` needs to expose service `FlightsService` to technical clients. The difference is that the consumers are not known a priori and are not part of the same application deployment. To expose service APIs for consumption, you can enhance the identity instance of the provider by defining API identifiers that are listed in property `provided-apis`: @@ -321,7 +321,7 @@ resources: ``` ::: -The entry with name `data-consumer` represents the consumption of service `sap.capire.flights.data` and is exposed as IAS API. +The entry with name `data-consumer` represents the consumption of service `FlightsService` and is exposed as IAS API. The description helps administrators to configure the consumer application with the proper provider API if done on UI level. [Detailed description about identity instance parameters for `provided-apis`](https://github.wdf.sap.corp/pages/CPSecurity/sci-dev-guide/docs/BTP/identity-broker#service-instance-parameters){.learn-more} @@ -333,7 +333,7 @@ Therefore, you can protect the corresponding CDS service by CAP role `data-consu ::: code-group ```cds [/srv/authorization.cds] -using { sap.capire.flights.data as data } from './data-service'; +using { FlightsService as data } from './data-service'; annotate data with @(requires: 'data-consumer'); ``` @@ -398,7 +398,7 @@ cds: remote.services: xflights: type: hcql - model: sap.capire.flights.data + model: FlightsService http: suffix: /hcql binding: @@ -418,7 +418,7 @@ cds: "kind": "ias" } }, - "sap.capire.flights.data": { + "FlightsService": { "kind": "hcql", "[production]": { "credentials": { @@ -596,5 +596,3 @@ Instead, rely on the shared connectivity component, which ensures centralized an - **Don't treat co-located services as external services**. This introduces unnecessary communication overhead and increases total cost of ownership. - - diff --git a/guides/services/constraints.md b/guides/services/constraints.md index 03b62e5cf0..5e86675e23 100644 --- a/guides/services/constraints.md +++ b/guides/services/constraints.md @@ -73,7 +73,7 @@ annotate TravelService.Travels with { The constraints are enforced automatically by the CAP runtimes on any input, and if failures occur, the request is ultimately rejected and the transaction rolled back. -Some of the checks, e.g. the static `@mandatory` checks, are validated directly on the input data, while the ones specified with `@assert:(\)` are collected into a query and **pushed down to the database** for execution. This in turn means, that first the respective `INSERT`s and `UPDATE`s are sent to the database, followed by the validation query. +Some of the checks, for example, the static `@mandatory` checks, are validated directly on the input data, while the ones specified with `@assert:(\)` are collected into a query and **pushed down to the database** for execution. This in turn means, that first the respective `INSERT`s and `UPDATE`s are sent to the database, followed by the validation query. @@ -454,7 +454,7 @@ entity Person : cuid { ### Localized Messages -Whenever you specify an error message with the annotations above, i.e., in the `then` part of an `@assert: ()` or in `@mandatory.message`, `@assert.format.message`, or `@assert.range.message`, you can either specify a plain text, or a [I18n text bundle key](../uis/i18n#externalizing-texts-bundles). +Whenever you specify an error message with the annotations above, that is, in the `then` part of an `@assert: ()` or in `@mandatory.message`, `@assert.format.message`, or `@assert.range.message`, you can either specify a plain text, or a [I18n text bundle key](../uis/i18n#externalizing-texts-bundles). Actually, we saw this already in the [sample in the introduction](#introduction): @@ -499,7 +499,7 @@ If you use a message key, the message is automatically looked up in the message ## Field Control -Declarative constraints can also be used to do field control in Fiori UIs, i.e. to add visual indicators to mandatory or readonly fields, or to hide fields. In particular, CAP automatically adds respective OData annotations to generated EDMX $metadata documents for the CDS listed below. +Declarative constraints can also be used to do field control in Fiori UIs, that is, to add visual indicators to mandatory or readonly fields, or to hide fields. In particular, CAP automatically adds respective OData annotations to generated EDMX $metadata documents for the CDS listed below. ### `@mandatory` diff --git a/guides/services/consuming-services.md b/guides/services/consuming-services.md index 39872356bf..305f73e8e3 100644 --- a/guides/services/consuming-services.md +++ b/guides/services/consuming-services.md @@ -146,7 +146,7 @@ This adds the API in CDS format to the _srv/external_ folder and also copies the
-Further, it adds the API as an external service to your _package.json_. You use this declaration later to connect to the remote service [using a destination](#use-destinations-with-node-js). +Further, it adds the API as an external service to your _package.json_. You use this declaration later to connect to the remote service [using a destination](#use-destinations-with-nodejs). ```json "cds": { @@ -1501,7 +1501,7 @@ You can change the destination lookup behavior as follows: ``` -Setting the [`selectionStrategy`](https://sap.github.io/cloud-sdk/docs/js/features/connectivity/destination#multi-tenancy) property for the [destination options](#use-destinations-with-node-js) to `alwaysProvider`, you can ensure that the destination is always read from your provider subaccount. With that you ensure that a subscriber cannot overwrite your destination. +Setting the [`selectionStrategy`](https://sap.github.io/cloud-sdk/docs/js/features/connectivity/destination#multi-tenancy) property for the [destination options](#use-destinations-with-nodejs) to `alwaysProvider`, you can ensure that the destination is always read from your provider subaccount. With that you ensure that a subscriber cannot overwrite your destination. Set the destination option `jwt` to `null`, if you don't want to pass the request's JWT to SAP Cloud SDK. Passing the request's JWT to SAP Cloud SDK has implications on, amongst others, the effective defaults for selection strategy and isolation level. In rare cases, these defaults are not suitable, for example when the request to the upstream server does not depend on the current user. Please see [Authentication and JSON Web Token (JWT) Retrieval](https://sap.github.io/cloud-sdk/docs/js/features/connectivity/destinations#authentication-and-json-web-token-jwt-retrieval) for more details. diff --git a/guides/uis/fiori.md b/guides/uis/fiori.md index 6d6513c8d2..78236f9d36 100644 --- a/guides/uis/fiori.md +++ b/guides/uis/fiori.md @@ -266,7 +266,7 @@ entity TravelService.Travels.drafts : TravelService.Travels { ... } ``` ::: -You can access this entity definition from the model at runtime, for example, to add custom handlers to draft events or to access draft data. In a CAP Node.js service implementation, use the [`.drafts`](../../node.js/cds-reflect#drafts) reference as a shortcut to access the draft entity: +You can access this entity definition from the model at runtime, for example, to add custom handlers to draft events or to access draft data. In a CAP Node.js service implementation, use the [`.drafts`](../../node.js/cds-reflect#-drafts) reference as a shortcut to access the draft entity: ```js const { Travels } = this.entities @@ -401,7 +401,7 @@ Select.from(FOO).where(o -> o.ID().eq(201) .and( //> reads draft data ); ``` -In CAP Node.js, use the [`Foo.drafts`](../../node.js/cds-reflect#drafts) references to access draft data: +In CAP Node.js, use the [`Foo.drafts`](../../node.js/cds-reflect#-drafts) references to access draft data: ```js {3} const { Foo } = this.entities @@ -409,7 +409,7 @@ SELECT.from (Foo, 201) // reads active data only SELECT.from (Foo.drafts, 201) // reads draft data, if exists ``` -Even better, use [`req.subject`](../../node.js/events#subject), which automatically resolves to the correct entity instance - active or draft - based on the current request context. For example, in custom action handlers triggered for both active and draft data: +Even better, use [`req.subject`](../../node.js/events#-subject), which automatically resolves to the correct entity instance - active or draft - based on the current request context. For example, in custom action handlers triggered for both active and draft data: ```js this.on ('approveTravel', req => UPDATE (req.subject) .with ({ status: 'A' })) @@ -634,7 +634,7 @@ The Cache Control feature is currently supported only on the Java runtime. In addition to adding [restrictions on services, entities, and actions/functions](../security/authorization#restrictions), there are cases where you want to hide certain UI elements for specific users. You can do this using annotations such as `@UI.Hidden` or `@UI.CreateHidden` together with `$edmJson` pointing to a singleton. -First, define the [singleton](../protocols/odata#singletons) in your service and annotate it with [`@cds.persistence.skip`](../databases/cdl-to-ddl#cds-persistence-skip) so that no database artifact is created: +First, define the [singleton](../protocols/odata#singletons) in your service and annotate it with [`@cds.persistence.skip`](../databases/cdl-to-ddl#cdspersistenceskip) so that no database artifact is created: ```cds @odata.singleton @cds.persistence.skip diff --git a/guides/uis/i18n.md b/guides/uis/i18n.md index 840afa2747..8bc2066df9 100644 --- a/guides/uis/i18n.md +++ b/guides/uis/i18n.md @@ -73,7 +73,7 @@ bookshop/ -By default, text bundles are fetched from folders named *_i18n* or *i18n* in the neighborhood of models, i.e. all folders that contain `.cds` sources or parent folders thereof. For example, given the following project layout and sources: +By default, text bundles are fetched from folders named *_i18n* or *i18n* in the neighborhood of models, that is, all folders that contain `.cds` sources or parent folders thereof. For example, given the following project layout and sources: ```zsh bookshop/ @@ -241,7 +241,7 @@ In effect, this means: - We support incoming locales as [language tags](https://www.ietf.org/rfc/bcp/bcp47.txt) using hyphens to separate sub tags 1, for example `en-GB`. - We always normalize these to underscores, which is `en_GB`. - Always use underscores in filenames, for example, `i18n_en_GB.properties` -- Always use underscores when filling `LOCALE` columns of localized text tables (e.g. in CSV files). +- Always use underscores when filling `LOCALE` columns of localized text tables (for example, in CSV files). 1 CAP Node.js also supports underscore separated tags, for example `en_GB`. diff --git a/java/building-plugins.md b/java/building-plugins.md index e14f749da2..d974205382 100644 --- a/java/building-plugins.md +++ b/java/building-plugins.md @@ -87,7 +87,7 @@ Additionally, the `resolve` goal from the CDS Maven Plugin needs to be added, to ::: ::: details Scope of the Reuse Package -Usually you would declare the dependency to the reuse package in the `srv` module of your CAP Java project. Since CAP Java 4.4.0 this makes the reuse models available to all CDS files in the CAP project. The models are extracted to the root `target/cds` folder. In case you want to make the reuse models only available within the Maven module that declared the dependency (e.g. `srv`) set the configuration `to` of the `resolve` goal to `${project.build.directory}`. In earlier versions of CAP Java reuse models where only available within CDS files placed in the Maven module that declared the dependency by default. +Usually you would declare the dependency to the reuse package in the `srv` module of your CAP Java project. Since CAP Java 4.4.0 this makes the reuse models available to all CDS files in the CAP project. The models are extracted to the root `target/cds` folder. In case you want to make the reuse models only available within the Maven module that declared the dependency (for example, `srv`) set the configuration `to` of the `resolve` goal to `${project.build.directory}`. In earlier versions of CAP Java reuse models where only available within CDS files placed in the Maven module that declared the dependency by default. ::: When your Maven build is set up correctly, you can use the reuse models in your CDS files using the standard `using` directive: diff --git a/java/change-tracking.md b/java/change-tracking.md index 680adc74d5..6a20ad44dd 100644 --- a/java/change-tracking.md +++ b/java/change-tracking.md @@ -213,7 +213,7 @@ Elements from the `@changelog` annotation value must always be prefixed by the a :::warning Validation required If the target of the association is missing, for example, when an entity is updated with the ID for a customer that does not exist, the changelog entry is not created. You need to validate -such cases in the custom code or use annotations, for example, [`@assert.target`](../guides/services/constraints#assert-target). +such cases in the custom code or use annotations, for example, [`@assert.target`](../guides/services/constraints#asserttarget). ::: ### Caveats of Identifiers diff --git a/java/cqn-services/persistence-services.md b/java/cqn-services/persistence-services.md index 734e82fec1..f087445657 100644 --- a/java/cqn-services/persistence-services.md +++ b/java/cqn-services/persistence-services.md @@ -184,7 +184,7 @@ cds add postgres ``` ::: warning -Automatic schema deployment isn't suitable for productive use. Consider using production-ready tools like Flyway or Liquibase. See more on that in the [Database guide for PostgreSQL](../../guides/databases/postgres#using-liquibase-java) +Automatic schema deployment isn't suitable for productive use. Consider using production-ready tools like Flyway or Liquibase. See more on that in the [Database guide for PostgreSQL](../../guides/databases/postgres#using-liquibase-in-cap-java) ::: #### Configure the Connection Data Explicitly { #postgres-connection } diff --git a/java/event-handlers/indicating-errors.md b/java/event-handlers/indicating-errors.md index a129cd1863..2ba23ab4c3 100644 --- a/java/event-handlers/indicating-errors.md +++ b/java/event-handlers/indicating-errors.md @@ -167,7 +167,7 @@ In case of actions and functions SAP Fiori also requires the message target to b When creating a message target, the correct parameter needs to be selected to specify what the relative message target path refers to. By default a message target always refers to the CQN statement of the event. In case of CRUD events this is the targeted entity. In case of bound actions and functions this is the entity that the action or function was bound to. -As CRUD event handlers are often called from within bound actions or functions (e.g. `draftActivate`), CAP's OData adapter adds a parameter prefix to a message target referring to the `cqn` parameter only when required. +As CRUD event handlers are often called from within bound actions or functions (for example, `draftActivate`), CAP's OData adapter adds a parameter prefix to a message target referring to the `cqn` parameter only when required. ::: info When using the `target(String)` API, which specifies the full target as a `String`, no additional parameter prefixes are added by CAP's OData adapter. When using this API, [draft state messages](../../guides/uis/fiori#validating-drafts) can't be invalidated automatically on `PATCH`. diff --git a/java/event-queues.md b/java/event-queues.md index 3211862e6f..a36e634dd1 100644 --- a/java/event-queues.md +++ b/java/event-queues.md @@ -524,7 +524,7 @@ A startup log entry shows the configured version: 2024-12-19T11:21:33.253+01:00 INFO 3420 --- [main] cds.services.impl.utils.BuildInfo : application.deployment.version: 1.0.0-SNAPSHOT ``` -To bypass the version check for a specific custom outbox, set [`cds.outbox.services.MyCustomOutbox.checkVersion: false`](./developing-applications/properties#cds-outbox-services--checkVersion). +To bypass the version check for a specific custom outbox, set [`cds.outbox.services.MyCustomOutbox.checkVersion: false`](./developing-applications/properties#cds-outbox-services-key-checkversion). ## Troubleshooting diff --git a/java/messaging.md b/java/messaging.md index 7cc48c9f38..db959e79db 100644 --- a/java/messaging.md +++ b/java/messaging.md @@ -470,7 +470,7 @@ Example: } ``` -[Learn more about _default-env.json_.](../node.js/cds-env#in-default-env-json){.learn-more} +[Learn more about _default-env.json_.](../node.js/cds-env#in-default-envjson){.learn-more} #### VCAP_SERVICES Template for SAP Event Mesh diff --git a/java/migration.md b/java/migration.md index e2597ed296..fb08289ef6 100644 --- a/java/migration.md +++ b/java/migration.md @@ -69,6 +69,14 @@ CAP Java 5 uses Spring Boot 4 and Spring Security 7 as the underlying framework. You must also upgrade the [BTP Security Library](https://github.com/SAP/cloud-security-services-integration-library) to version 4.0.0 or later. +#### Test Code Changes { #spring-boot-4-test-code } + +While regular CAP Java application code rarely touches Spring Boot APIs directly, test code typically does. Spring Boot 4 introduces a new module structure for its testing support, which results in package name changes that can break existing test classes. Common examples are the test slice annotations (such as `@AutoConfigureMockMvc`, `@WebMvcTest`, or `@DataJpaTest`) that were relocated as part of this restructuring. + +Refer to the [Test Code section of the Spring Boot 4.0 Migration Guide](https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-4.0-Migration-Guide#test-code) for the complete list of affected packages and classes. + +These package renames are already covered by the [OpenRewrite community recipes](https://docs.openrewrite.org/recipes/java/spring/boot4) for Spring Boot 4, so you can apply them automatically together with the [CAP Java OpenRewrite migrations](#automatic-cap-java-migrations-with-openrewrite) above. The same applies to the commercial recipes offered by the [Moderne platform](https://www.moderne.ai/). + ### Minimum Versions CAP Java 5.0 increased some minimum required versions: diff --git a/java/security.md b/java/security.md index 6e12c42f7d..f76fdc3fdd 100644 --- a/java/security.md +++ b/java/security.md @@ -157,7 +157,7 @@ public class ActuatorSecurityConfig { In case you want to write your own custom security configuration that acts as a last line of defense and handles any request you need to disable the CAP security configurations by setting cds.security.authentication.authConfig.enabled: false, as Spring Security forbids registering multiple security configurations with an any request security matcher. -If you even want to deactivate OAuth token validation for XSUAA or IAS, e.g. to establish an own authentication strategy, +If you even want to deactivate OAuth token validation for XSUAA or IAS, for example, to establish an own authentication strategy, the following properties can be used: | Configuration Property | Description | Default diff --git a/java/versions.md b/java/versions.md index 6a58fb6766..20079ba528 100644 --- a/java/versions.md +++ b/java/versions.md @@ -61,35 +61,35 @@ CAP Java uses various dependencies that are also used by the applications themse If the applications decide to manage the versions of these dependencies, it's helpful to know the minimum versions of these dependencies that CAP Java requires. The following table lists these minimum versions for various common dependencies, based on the latest release: -#### Active Version 4.x { #dependencies-version-4 } +#### Active Version 5.x { #dependencies-version-5 } | Dependency | Minimum Version | Recommended Version | | --- | --- | --- | -| CAP Java | n/a | [latest 4.x](https://central.sonatype.com/artifact/com.sap.cds/cds-services-bom/versions) | -| JDK | 17 | 21 | -| Maven | 3.8.8 | 3.9.9 | -| @sap/cds-dk | 8 | [latest](https://www.npmjs.com/package/@sap/cds-dk) | -| @sap/cds-compiler | 5 | [latest](https://www.npmjs.com/package/@sap/cds-compiler) | -| Spring Boot | 3.0 | [latest 3.x](https://central.sonatype.com/artifact/org.springframework.boot/spring-boot/versions) | -| XSUAA | 3.1 | [latest](https://central.sonatype.com/artifact/com.sap.cloud.security/java-bom/versions) | +| CAP Java | n/a | [latest 5.x](https://central.sonatype.com/artifact/com.sap.cds/cds-services-bom/versions) | +| JDK | 21 | 25 | +| Maven | 3.9.14 | [latest 3.9.x](https://maven.apache.org/download.cgi) (not 4.x) | +| @sap/cds-dk | 9 | [latest 10.x](https://www.npmjs.com/package/@sap/cds-dk) | +| @sap/cds-compiler | 6 | [latest 7.x](https://www.npmjs.com/package/@sap/cds-compiler) | +| Spring Boot | 4.0 | [latest 4.x](https://central.sonatype.com/artifact/org.springframework.boot/spring-boot/versions) | +| XSUAA | 4.0 | [latest 4.x](https://central.sonatype.com/artifact/com.sap.cloud.security/java-bom/versions) | | SAP Cloud SDK | 5.9 | [latest](https://central.sonatype.com/artifact/com.sap.cloud.sdk/sdk-modules-bom/versions) | -| Java Logging | 3.7 | [latest](https://central.sonatype.com/artifact/com.sap.hcp.cf.logging/cf-java-logging-support-servlet-jakarta/versions) | -| Node.js | 20 | 22 | +| Java Logging | 3.7 | [latest 4.x](https://central.sonatype.com/artifact/com.sap.hcp.cf.logging/cf-java-logging-support-servlet-jakarta/versions) | +| Node.js | 22 | 24 | -#### Maintenance Version 3.10.x { #dependencies-version-3 } +#### Maintenance Version 4.9.x { #dependencies-version-4 } | Dependency | Minimum Version | Recommended Version | | --- | --- | --- | -| CAP Java | n/a | [latest 3.10.x](https://central.sonatype.com/artifact/com.sap.cds/cds-services-bom/versions) | +| CAP Java | n/a | [latest 4.9.x](https://central.sonatype.com/artifact/com.sap.cds/cds-services-bom/versions) | | JDK | 17 | 21 | | Maven | 3.8.8 | 3.9.9 | -| @sap/cds-dk | 7 | [latest 8.x](https://www.npmjs.com/package/@sap/cds-dk) | -| @sap/cds-compiler | 4 | [latest 5.x](https://www.npmjs.com/package/@sap/cds-compiler) | +| @sap/cds-dk | 9 | [latest 9.x](https://www.npmjs.com/package/@sap/cds-dk) | +| @sap/cds-compiler | 6 | [latest 6.x](https://www.npmjs.com/package/@sap/cds-compiler) | | Spring Boot | 3.0 | [latest 3.x](https://central.sonatype.com/artifact/org.springframework.boot/spring-boot/versions) | | XSUAA | 3.0 | [latest](https://central.sonatype.com/artifact/com.sap.cloud.security/java-bom/versions) | | SAP Cloud SDK | 5.9 | [latest](https://central.sonatype.com/artifact/com.sap.cloud.sdk/sdk-modules-bom/versions) | | Java Logging | 3.7 | [latest](https://central.sonatype.com/artifact/com.sap.hcp.cf.logging/cf-java-logging-support-servlet-jakarta/versions) | -| Node.js | 18 | 20 | +| Node.js | 22 | 22 | ### Consistent Versions diff --git a/java/working-with-cql/query-api.md b/java/working-with-cql/query-api.md index 34af880760..9c03f4dbec 100644 --- a/java/working-with-cql/query-api.md +++ b/java/working-with-cql/query-api.md @@ -188,7 +188,7 @@ Select.from(BOOKS).byParams("title", "author.name"); #### Filtering Map Data -You can also filter by _content_ of [map data](../cds-data.md#cds-map) (i.e. elements of type `cds.Map`). +You can also filter by _content_ of [map data](../cds-data.md#cds-map) (that is, elements of type `cds.Map`). Let's use this model as an example: @@ -899,7 +899,7 @@ The pagination isn't stateful. If rows are inserted or removed before a subseque #### Sorting by Map Data -You can also sort by _content_ of [map data](../cds-data.md#cds-map) (i.e. elements of type `cds.Map`). Considering this model +You can also sort by _content_ of [map data](../cds-data.md#cds-map) (that is, elements of type `cds.Map`). Considering this model ```cds entity Product : cuid { diff --git a/java/working-with-cql/query-execution.md b/java/working-with-cql/query-execution.md index b2f7a32a68..d6501d7929 100644 --- a/java/working-with-cql/query-execution.md +++ b/java/working-with-cql/query-execution.md @@ -223,7 +223,7 @@ long deleteCount = service.run(delete).rowCount(); ## Views and Projections { #views } -With CDS [views](../../cds/cdl#views-projections) you can derive new entities from existing ones, for example to rename or exclude certain elements, or to add [virtual elements](../../cds/cdl#virtual-elements-in-views) for specific use cases. +With CDS [views](../../cds/cdl#views--projections) you can derive new entities from existing ones, for example to rename or exclude certain elements, or to add [virtual elements](../../cds/cdl#virtual-elements-in-views) for specific use cases. From the CDS model the CDS compiler generates [DDL](../../guides/databases/cdl-to-ddl) files, which include SQL views for the CDS views. These views are deployed to the [database](../cqn-services/persistence-services#database-support) and used by the CAP runtime to read data. diff --git a/node.js/_menu.md b/node.js/_menu.md index b433647b63..a55ba84d4e 100644 --- a/node.js/_menu.md +++ b/node.js/_menu.md @@ -2,8 +2,8 @@ # [cds. compile()](cds-compile) - ## [cds. compile()](cds-compile#cds-compile) - ## [cds. compile.to ...](cds-compile#cds-compile-to) + ## [cds. compile()](cds-compile#cds-compile-) + ## [cds. compile.to ...](cds-compile#cds-compile-to-) ## [cds. load()](cds-compile#cds-load) ## [cds. parse()](cds-compile#cds-parse) ## [cds. minify()](cds-compile#cds-minify) diff --git a/node.js/app-services.md b/node.js/app-services.md index bcfdce896f..da2f5c6c30 100644 --- a/node.js/app-services.md +++ b/node.js/app-services.md @@ -113,7 +113,7 @@ This method is adding request handlers for handling managed data, as documented ### _static_ handle_paging() {.method} -This method is adding request handlers for paging & implicit sorting, as documented in the [Providing Services guide](../guides/services/served-ootb#pagination-sorting). +This method is adding request handlers for paging & implicit sorting, as documented in the [Providing Services guide](../guides/services/served-ootb#pagination--sorting). @@ -164,20 +164,23 @@ When CAP's generic handlers run a CRUD operation, the result follows a consisten | Operation | Return value | |-----------------------|-----------------------------------------------------------------------------------------------| | `READ` | Array of matching records, or a single record / `null` when read by key | -| `INSERT` / `CREATE` | Array with `.affected` (rows written); iterate to access the inserted rows' primary keys | -| `UPDATE` / `UPSERT` | Array with `.affected` (rows changed); populated with rows from a `RETURNING` clause | -| `DELETE` | Array with `.affected` (rows deleted); populated with rows from a `RETURNING` clause | +| `CREATE` | Array with `.affected` (rows created); iterate to access the created rows' generated keys | +| `UPDATE` | Array with `.affected` (rows changed); reserved for rows from a `RETURNING` clause | +| `UPSERT` | Array with `.affected` (rows written); reserved for rows from a `RETURNING` clause | +| `DELETE` | Array with `.affected` (rows deleted); reserved for rows from a `RETURNING` clause | -For `INSERT`s, the result is a lazy array: iterating it (`[...result]`, `for…of`, `JSON.stringify`) materializes the generated primary keys of the inserted rows. Direct index access works after the first iteration. +For `CREATE`, the array will be populated with rows from an SQL `RETURNING` clause once that is supported. Until then, the result is a lazy array that computes the created rows' generated primary keys on demand: iterating it (`[...result]`, `for…of`, `JSON.stringify`) populates those keys, avoiding the cost when you don't need them. + +> [!warning] Iterate before indexing +> Direct index access (`result[0]`) returns `undefined` until the array has been iterated at least once. Spread or loop over the result first. ```js -const inserted = await srv.create(Books).entries({title:'Catweazle'}) -inserted.affected // 1 -const [row] = [...inserted] // materializes — row holds the generated key -inserted[0] // same row (materialized above) +const created = await srv.create(Books).entries({title:'Catweazle'}) +created.affected // 1 +const [row] = [...created] // iterate first — row holds the generated key ``` -For `UPDATE`, `UPSERT`, and `DELETE`, the array is reserved for rows returned by a SQL `RETURNING` clause. But `RETURNING` is not yet supported, so the array currently is always empty: +For `UPDATE`, `UPSERT`, and `DELETE`, the array is reserved for rows returned by an SQL `RETURNING` clause. Unlike `CREATE`, there are no generated keys to synthesize client-side, so — with `RETURNING` not yet supported — the array is currently always empty: ```js const updated = await srv.update(Books).set({discount:'10%'}).where({stock:{'>':111}}) diff --git a/node.js/authentication.md b/node.js/authentication.md index 4b8cc3db00..7ccf58eaf4 100644 --- a/node.js/authentication.md +++ b/node.js/authentication.md @@ -17,7 +17,7 @@ uacp: This page is linked from the Help Portal at https://help.sap.com/products/ [user]: #cds-user [`cds.context.user`]: #cds-user -Represents the currently logged-in user as filled into [`cds.context.user`](events#user) by authentication middlewares. +Represents the currently logged-in user as filled into [`cds.context.user`](events#-user) by authentication middlewares. Simply create instances of `cds.User` or of subclasses thereof in custom middlewares. For example: @@ -429,7 +429,7 @@ export default function custom_auth(req: Req, res: Response, next: NextFunction) } ``` -[Learn more about customizing the user ID in this example.](cds-serve#customization-of-cds-context-user){.learn-more} +[Learn more about customizing the user ID in this example.](cds-serve#customization-of-cdscontextuser){.learn-more} ## Authentication in Production diff --git a/node.js/best-practices.md b/node.js/best-practices.md index 02d0e135b0..792a06e42c 100644 --- a/node.js/best-practices.md +++ b/node.js/best-practices.md @@ -251,7 +251,7 @@ Handling CSRF at the _App Router_ level ensures consistency across instances. Th With _Cross-Origin Resource Sharing_ (CORS) the server that hosts the UI can tell the browser about servers it trusts to provide resources. In addition, so-called "preflight" requests tell the browser if the cross-origin server will process a request with a specific method and a specific origin. -If not running in production, CAP's [built-in server.js](cds-server#built-in-server-js) allows all origins. +If not running in production, CAP's [built-in server.js](cds-server#built-in-serverjs) allows all origins. #### Custom CORS Implementation @@ -368,7 +368,7 @@ The following articles might be of interest: ## Timestamps -When using [timestamps](events#timestamp) (for example for managed dates) the Node.js runtime offers a way to easily deal with that without knowing the format of the time string. The `req` object contains a property `timestamp` that holds the current time (specifically `new Date()`, which is comparable to `CURRENT_TIMESTAMP` in SQL). It also stays the same until the request finished, so if it is used in multiple places in the same transaction or request it will always be the same. +When using [timestamps](events#-timestamp) (for example for managed dates) the Node.js runtime offers a way to easily deal with that without knowing the format of the time string. The `req` object contains a property `timestamp` that holds the current time (specifically `new Date()`, which is comparable to `CURRENT_TIMESTAMP` in SQL). It also stays the same until the request finished, so if it is used in multiple places in the same transaction or request it will always be the same. Example: @@ -379,7 +379,7 @@ srv.before("UPDATE", "EntityName", (req) => { }); ``` -Internally the [timestamp](events#timestamp) is a JavaScript `Date` object, that is converted to the right format, when sent to the database. So if in any case a date string is needed, the best solution would be to initialize a Date object, that is then translated to the correct UTC String for the database. +Internally the [timestamp](events#-timestamp) is a JavaScript `Date` object, that is converted to the right format, when sent to the database. So if in any case a date string is needed, the best solution would be to initialize a Date object, that is then translated to the correct UTC String for the database. ## Decimals and Int64 as Strings { #decimals-int64 } diff --git a/node.js/cds-compile.md b/node.js/cds-compile.md index a70e9c1763..8a36471650 100644 --- a/node.js/cds-compile.md +++ b/node.js/cds-compile.md @@ -248,7 +248,7 @@ const doc = cds.compile.to.asyncapi(csn_file) ## cds. load (files) {.method #cds-load } Loads and parses a model from one or more files into a single effective model. -It's essentially a [shortcut to `cds.compile ([...])`](#cds-compile). In addition emits event `cds 'loaded'`. +It's essentially a [shortcut to `cds.compile ([...])`](#cds-compile-). In addition emits event `cds 'loaded'`. Declaration: @@ -287,7 +287,7 @@ The three main methods are offered as classic functions, as well as [tagged temp ### cds. parse. cdl() {.method #parse-cdl } Parses a source string in _[CDL](../cds/cdl)_ syntax and returns it as a parsed model according to the [_CSN spec_](../cds/csn). Supports tagged template strings as well as plain string arguments. -It's essentially a [shortcut to `cds.compile (..., {flavor:'parsed'})`](#cds-compile). +It's essentially a [shortcut to `cds.compile (..., {flavor:'parsed'})`](#cds-compile-). Examples: ```js @@ -462,7 +462,7 @@ This is the right place to, for example, add custom elements required at runtime ### compile.to.dbx {.event} -Emitted every time before database-specific artifacts, i.e. SQL DDL scripts, are generated from the model. +Emitted every time before database-specific artifacts, that is, SQL DDL scripts, are generated from the model. This is the right place to, for example, add custom elements required in your persistence. diff --git a/node.js/cds-connect.md b/node.js/cds-connect.md index 812b292275..a6ae411bf0 100644 --- a/node.js/cds-connect.md +++ b/node.js/cds-connect.md @@ -25,7 +25,7 @@ Use `cds.connect.to()` to connect to services configured in a project's `cds.req const ReviewsService = await cds.connect.to('ReviewsService') ``` -The method returns a _Promise_ resolving to a _[Service](../cds/cdl#services)_ instance which acts as a client proxy to the service's API, allowing you to call its methods and access its data using common [`cds.Service`](core-services#consuming-services) methods, e.g.: +The method returns a _Promise_ resolving to a _[Service](../cds/cdl#services)_ instance which acts as a client proxy to the service's API, allowing you to call its methods and access its data using common [`cds.Service`](core-services#consuming-services) methods, for example: ```js let reviews = await ReviewsService.read ('Reviews') @@ -58,7 +58,7 @@ const db2 = await cds.connect.to ({ ### cds. services {#cds-connect-caching .property} -When connecting to a service using `cds.connect.to()`, the service instance is cached in [`cds.services`](cds-facade#cds-services) under the service name. This means that subsequent calls to `cds.connect.to()` with the same service name will all return the same instance. As services constructed by [`cds.serve`](cds-serve#cds-serve) are registered with [`cds.services`](cds-facade#cds-services) as well, a connect finds and returns them as local service connections. +When connecting to a service using `cds.connect.to()`, the service instance is cached in [`cds.services`](cds-facade#cds-services) under the service name. This means that subsequent calls to `cds.connect.to()` with the same service name will all return the same instance. As services constructed by [`cds.serve`](cds-serve#cds-serve-) are registered with [`cds.services`](cds-facade#cds-services) as well, a connect finds and returns them as local service connections. You can also access cached service instance like this: diff --git a/node.js/cds-facade.md b/node.js/cds-facade.md index 0372114874..f5959ebfd6 100644 --- a/node.js/cds-facade.md +++ b/node.js/cds-facade.md @@ -183,7 +183,7 @@ Known values for `cds.cli.command` are `add`, `build`, `compile`, `deploy`, `imp ### cds. entities {.property} -Convenience shortcut to [`cds.model.entities`](cds-reflect#entities). +Convenience shortcut to [`cds.model.entities`](cds-reflect#-entities). Returns an iterable dictionary of entity definitions in the model, which can be used like this: - Accessing named entities directly: diff --git a/node.js/cds-i18n.md b/node.js/cds-i18n.md index 619e3f1cce..36ca56818a 100644 --- a/node.js/cds-i18n.md +++ b/node.js/cds-i18n.md @@ -60,7 +60,7 @@ ORDER_EXCEEDS_STOCK = The order of {quantity} books exceeds available stock {sto ### Direct Usage -In addition, you can use both standard bundles directly in your code, with [`.at(key)`](#at-key) the central method to obtain localized texts: +In addition, you can use both standard bundles directly in your code, with [`.at(key)`](#at-key-) the central method to obtain localized texts: ```js [dev] cds repl @@ -137,7 +137,7 @@ function cds.i18n.bundle4 (file : string, options?) function cds.i18n.bundle4 (model : CSN, options?) ``` -Factory method to create instances of [`I18nBundle`](#i18nbundle). The first argument is either a string used as the bundle's [`file`/`basename`](#–-file-basename), or a CDS model. +Factory method to create instances of [`I18nBundle`](#i18nbundle). The first argument is either a string used as the bundle's [`file`/`basename`](#-file--basename), or a CDS model. ```js const b1 = cds.i18n.bundle4('foo') @@ -278,7 +278,7 @@ You can alternatively pass in a CSN definition instead of an i18n key to look up ### `key4 (csn)` {.method} -This method is used by [`bundle.at()`](#at-key) to determine an i18n key for a CSN definition. In essence, the implementation works like that: +This method is used by [`bundle.at()`](#at-key-) to determine an i18n key for a CSN definition. In essence, the implementation works like that: ```js const a = csn['@title'] @@ -299,7 +299,7 @@ return a.match(/{i18n>(.+)}/)[1] function texts4 (locale: string) => Texts ``` -This method is used by [`bundle.at()`](#at-key) to obtain the set of translated texts for a specific locale. +This method is used by [`bundle.at()`](#at-key-) to obtain the set of translated texts for a specific locale. For example, try this in `cds repl`: {.indent} ```js @@ -417,8 +417,8 @@ An array of root directories up to which to recurse up the filesystem hierarchy ### – `leafs` {.property} -The leafs of the filesystem hierarchy to start fetch i18n folders recursively. Determined by `model?.$sources.map(path.dirname)` if a [`model`](#–-model) (or [`cds.model`](cds-facade#cds-model)) is given.
-*Default*: [`roots`](#–-roots). {.indent} +The leafs of the filesystem hierarchy to start fetch i18n folders recursively. Determined by `model?.$sources.map(path.dirname)` if a [`model`](#-model) (or [`cds.model`](cds-facade#cds-model)) is given.
+*Default*: [`roots`](#-roots). {.indent} ### – `folders` {.property} diff --git a/node.js/cds-ql.md b/node.js/cds-ql.md index 29b2f0899c..a3ea81e72f 100644 --- a/node.js/cds-ql.md +++ b/node.js/cds-ql.md @@ -82,7 +82,7 @@ const { Books } = cds.entities let q1 = SELECT.from (Books) .where `ID=${201}` ``` -[Learn more about using reflected definitions from a service's model](core-services#entities){.learn-more} +[Learn more about using reflected definitions from a service's model](core-services#-entities){.learn-more} #### Not Locked in to SQL @@ -114,7 +114,7 @@ let books = await cats.run (query) > `CatalogService` might be a remote service connected via OData. In this case, the query would be translated to an OData request sent via HTTP. -The APIs are also available through [`cds.Service`'s CRUD-style Convenience API](core-services#crud-style-api), e.g.: +The APIs are also available through [`cds.Service`'s CRUD-style Convenience API](core-services#crud-style-api), for example: ```js const db = cds.db @@ -205,7 +205,7 @@ let input = 201 //> might be entered by end users let books = await SELECT.from `Books` .where ('ID='+input) let bookz = await SELECT.from `Books` .where (`ID=${input}`) ``` -> **Note** also that tagged template strings never have surrounding parentheses! I.e., the third line above does the very same string concatenation as the second line. +> **Note** also that tagged template strings never have surrounding parentheses! That means, the third line above does the very same string concatenation as the second line. A malicious user might enter some SQL code fragment like that: diff --git a/node.js/cds-reflect.md b/node.js/cds-reflect.md index 330aa4e0ad..1b2731998e 100644 --- a/node.js/cds-reflect.md +++ b/node.js/cds-reflect.md @@ -137,8 +137,8 @@ for (let each of m.entities('my.bookshop')) console.log (each.name) ``` > [!info] -> Note: In the dictionaries returned by `.entities` there are no entries for [`.texts`](#texts) entities, as these are generated, and hence living in a shadow world. They did show up in former releases, which caused a lot of confusion, and was fixed since cds 9.6. -> They are always accessible via the main entity's [`.texts`](#texts) property, e.g. `Books.texts`. +> Note: In the dictionaries returned by `.entities` there are no entries for [`.texts`](#-texts) entities, as these are generated, and hence living in a shadow world. They did show up in former releases, which caused a lot of confusion, and was fixed since cds 9.6. +> They are always accessible via the main entity's [`.texts`](#-texts) property, for example `Books.texts`. ### each() {#each .method } @@ -254,11 +254,11 @@ All objects of a linked model containing CSN definitions are instances of this c For example, that applies to: -- *`cds.model` [.definitions](#definitions), [.services](#services), [.entities](#entities)* -- *`cds.service` [.entities](#entities-1), [.events](#events), [.actions](#actions-1)* -- *`cds.entity` [.keys](#keys), [.associations](#associations), [.compositions](#compositions), [.actions](#actions)* -- *`cds.struct` [.elements](#elements)* (hence also *`cds.entity` .elements*) -- *`cds.Association` [.foreignKeys](#foreignkeys)* +- *`cds.model` [.definitions](#-definitions), [.services](#-services), [.entities](#-entities)* +- *`cds.service` [.entities](#-entities-1), [.events](#-events), [.actions](#-actions-1)* +- *`cds.entity` [.keys](#-keys), [.associations](#-associations), [.compositions](#-compositions), [.actions](#-actions)* +- *`cds.struct` [.elements](#-elements)* (hence also *`cds.entity` .elements*) +- *`cds.Association` [.foreignKeys](#-foreignkeys)* Instances of `LinkedDefinitions` allow both, object-style access, as well as array-like access. For example: diff --git a/node.js/cds-server.md b/node.js/cds-server.md index e0124088e7..3d0bb0eae6 100644 --- a/node.js/cds-server.md +++ b/node.js/cds-server.md @@ -10,7 +10,7 @@ status: released -CAP Node.js servers are bootstrapped through a [built-in `server.js` module](#built-in-server-js), which can be accessed through [`cds.server`](#cds-server). You can plug-in custom logic to the default bootstrapping choreography using a [custom `server.js`](#custom-server-js) in your project. +CAP Node.js servers are bootstrapped through a [built-in `server.js` module](#built-in-serverjs), which can be accessed through [`cds.server`](#cds-server). You can plug-in custom logic to the default bootstrapping choreography using a [custom `server.js`](#custom-server-js) in your project. @@ -85,8 +85,8 @@ module.exports = async function cds_server(options) { ### cds. server() {.method} This is essentially a shortcut getter to `require('@sap/cds/server')`, that is, it loads and returns -the [built-in `server.js`](#built-in-server-js) implementation. -You'd mainly use this in [custom `server.js`](#custom-server-js) to delegate to the default implementation, [as shown below](#override-cds-server). +the [built-in `server.js`](#built-in-serverjs) implementation. +You'd mainly use this in [custom `server.js`](#custom-server-js) to delegate to the default implementation, [as shown below](#override-cdsserver). @@ -265,7 +265,7 @@ The built-in CORS middleware can be enabled explicitly with cds.server.c The default generic _index.html_ page is not served if `NODE_ENV` is set to `production`. Set cds.server.index: true to activate explicitly also in production-like test environments, for example for deployed PoCs. You must not do this in real production environments! -[See the **Generic *index.html*** page in action.](../get-started/bookshop#generic-index-html) {.learn-more} +[See the **Generic *index.html*** page in action.](../get-started/bookshop#generic-indexhtml) {.learn-more} diff --git a/node.js/cds-test.md b/node.js/cds-test.md index e01f222cdb..7ecf182e86 100644 --- a/node.js/cds-test.md +++ b/node.js/cds-test.md @@ -75,13 +75,13 @@ Let's analyze the highlighted the code above line by line: ```js :line-numbers=2 const ... cds.test... // > loads the cds-test module ``` -- By accessing [`cds.test`](#class-cds-test-test) the `cds-test` module is loaded, which ensures that... +- By accessing [`cds.test`](#class-cdstesttest) the `cds-test` module is loaded, which ensures that... - Functions like [`describe`](https://vitest.dev/api/describe.html), [`test`](https://vitest.dev/api/test.html), [`it`](https://vitest.dev/api/test.html), etc. are made available in test scope. ```js :line-numbers=2 const { GET, ... } = cds.test ('@capire/bookshop') ``` -- Calling the [`cds.test()`](#cds-test) function launches a CAP server for the given CAP project. +- Calling the [`cds.test()`](#cdstest) function launches a CAP server for the given CAP project. ```js :line-numbers=3 defaults.auth = { username: 'alice' } @@ -171,7 +171,7 @@ To keep your tests portable across different test runners, it's recommended to a ### Dos and Don'ts ::: danger Don't load `cds.env` before `cds.test()` -To ensure [`cds.env`](cds-env), and hence all plugins, are loaded from the test's target folder, the call to [`cds.test()`](#cds-test) is the first thing you do in your tests. Any references to [`cds`](cds-facade) sub modules or any imports of which have to go after. → See also: [`CDS_TEST_ENV_CHECK`.](#cds-test-env-check) +To ensure [`cds.env`](cds-env), and hence all plugins, are loaded from the test's target folder, the call to [`cds.test()`](#cdstest) is the first thing you do in your tests. Any references to [`cds`](cds-facade) sub modules or any imports of which have to go after. → See also: [`CDS_TEST_ENV_CHECK`.](#cds_test_env_check) ::: ::: warning Keep it simple, stupid! @@ -181,14 +181,14 @@ Using these bells and whistles might also cause conflicts with generic features ::: ::: tip Avoid `process.chdir()` -> prefer `cds.test.in()` -CAP servers need to be launched from a specific project home directory. Don't use `process.chdir()` for this, as that may leave test containers in failed state, leading to failing subsequent tests. -> Specify the target folder in the call to [`cds.test()`](#cds-test), or use [`cds.test.in()`](#test-in-folder) instead. +CAP servers need to be launched from a specific project home directory. Don't use `process.chdir()` for this, as that may leave test containers in failed state, leading to failing subsequent tests. -> Specify the target folder in the call to [`cds.test()`](#cdstest), or use [`cds.test.in()`](#test-in-folder-) instead. ::: ## Class `cds.test.Test` -Instances of this class are returned by [`cds.test()`](#cds-test), for example: +Instances of this class are returned by [`cds.test()`](#cdstest), for example: ```js const test = cds.test() @@ -207,7 +207,7 @@ test.run().in(_dirname) ### cds.test() {.method} -This method is the most convenient way to start a test server. It's actually just a convenient shortcut to construct a new instance of class `Test` and call [`test.run()`](#test-run), defined as follows: +This method is the most convenient way to start a test server. It's actually just a convenient shortcut to construct a new instance of class `Test` and call [`test.run()`](#test-run-), defined as follows: ```js const { Test } = cds.test @@ -391,7 +391,7 @@ The implementation redirects any console operations in a `beforeAll()` hook, cle ### test. run (...) {.method} -This is the method behind [`cds.test()`](#cds-test) to start a CDS server, that is the following are equivalent: +This is the method behind [`cds.test()`](#cdstest) to start a CDS server, that is the following are equivalent: ```js cds.test(...) @@ -412,7 +412,7 @@ cds.test('serve','srv/cat-service.cds') cds.test('serve','CatalogService') ``` -You can optionally add [`test.in(folder)`](#test-in-folder) in fluent style to run the test in a specific folder: +You can optionally add [`test.in(folder)`](#test-in-folder-) in fluent style to run the test in a specific folder: ```js cds.test('serve','srv/cat-service.cds').in('/cap/samples/bookshop') @@ -435,7 +435,7 @@ cds.test().in('/cap/samples/bookshop') //> equivalent ### test. in (folder, ...) {.method} -Safely switches [`cds.root`](cds-facade#cds-root) to the specified target folder. Most frequently you'd use it in combination with starting a server with [`cds.test()`](#cds-test) in fluent style like that: +Safely switches [`cds.root`](cds-facade#cds-root) to the specified target folder. Most frequently you'd use it in combination with starting a server with [`cds.test()`](#cdstest) in fluent style like that: ```js let test = cds.test(...).in(__dirname) @@ -460,7 +460,7 @@ cds.test(__dirname) //> target folder: __dirname This would result in the test server started from `__dirname`, but erroneously using `cds.env` loaded from `./`. -As these mistakes end up in hard-to-resolve follow up errors, [`test.in()`](#test-in-folder) can detect this if environment variable `CDS_TEST_ENV_CHECK` is set. The previous code will then result into an error like that: +As these mistakes end up in hard-to-resolve follow up errors, [`test.in()`](#test-in-folder-) can detect this if environment variable `CDS_TEST_ENV_CHECK` is set. The previous code will then result into an error like that: ```sh CDS_TEST_ENV_CHECK=y jest cds.test.test.js diff --git a/node.js/core-services.md b/node.js/core-services.md index 8ea29f4681..6a77ea334b 100644 --- a/node.js/core-services.md +++ b/node.js/core-services.md @@ -258,7 +258,7 @@ await srv.read ('GET','/Books/206') await srv.send ('submitOrder', { book:206, quantity:1 }) ``` -[Using typed APIs for actions and functions](../guides/services/custom-actions#calling-actions-functions): +[Using typed APIs for actions and functions](../guides/services/custom-actions#calling-actions--functions): ```js await srv.submitOrder({ book:206, quantity:1 }) @@ -353,7 +353,7 @@ function constructor ( ) ``` -> *Arguments fill in equally named properties [`name`](#name), [`model`](#model), [`options`](#options).* +> *Arguments fill in equally named properties [`name`](#-name), [`model`](#-model), [`options`](#-options).* **Don't override the constructor** in subclasses, rather override [`srv.init()`](#srv-init). @@ -420,7 +420,7 @@ for (let d of this.entities) //... d is a CSN definition #### Similarity _and_ difference to `cds.entities` -These properties are very similar in nature and behavior to [`cds.entities`](cds-facade#cds-entities), which is a sortcut to [`cds.model.entities`](cds-reflect#entities). However, note this difference: +These properties are very similar in nature and behavior to [`cds.entities`](cds-facade#cds-entities), which is a sortcut to [`cds.model.entities`](cds-reflect#-entities). However, note this difference: While both of these work with [`cds.entities`](cds-facade#cds-entities): ```js @@ -520,9 +520,11 @@ class BooksService extends cds.ApplicationService { **Methods `.on`, `.before`, `.after`** refer to corresponding *phases* during request processing: -- **`.on`** handlers _fulfill_ requests, for example, by reading/writing data from/to databases -- **`.before`** handlers run before the `.on` handlers, e.g., for validating inbound data -- **`.after`** handlers run after the `.on` handlers, e.g., to enrich outbound data +|Method | Description | Example +| --- | --- | --- | +| `.on`| handlers _fulfill_ requests | reading/writing data from/to databases | +| `.before` | handlers run before the `.on` handlers | validating inbound data | +| `.after` | handlers run after the `.on` handlers | enrich outbound data | **Argument `event`** can be one of: @@ -535,9 +537,9 @@ class BooksService extends cds.ApplicationService { **Argument `entity`** can be one of: -- A `CSN definition` of an entity served by this service → i.e., from [`this.entities`](#entities) +- A `CSN definition` of an entity served by this service → from [`this.entities`](#-entities) - A `string` corresponding to the _name_ of an entity served by this service -- A `path` navigating from a served entity to associated ones → e.g., `Books/author` +- A `path` navigating from a served entity to associated ones, e.g., `Books/author` **Multiple `events` or `entities`** – for both parameters, you can also specify: @@ -691,9 +693,9 @@ Books.data = { ::: details Noteworthy in these examples... -- The `READ` handler is using the [`req.target`](./events.md#target) property which points to the CSN definition of the entity addressed by the incoming request → matching one of `Books` or `Authors` we obtained from [`this.entities`](#entities) above. +- The `READ` handler is using the [`req.target`](./events.md#-target) property which points to the CSN definition of the entity addressed by the incoming request → matching one of `Books` or `Authors` we obtained from [`this.entities`](#-entities) above. -- The `UPDATE` handler is using the [`req.params`](./events.md#params) property which provides access to passed in entity keys. +- The `UPDATE` handler is using the [`req.params`](./events.md#-params) property which provides access to passed in entity keys. ::: @@ -820,7 +822,7 @@ Use this method to send synchronous requests to a service for execution. - `method` is an HTTP method - `path` can be an arbitrary URL, starting with a leading `'/'`, it is passed to a service without any modification as a string -To call bound / unbound actions and functions from the service, further variants of `srv.send` are additionally supported, as described in the section [Calling Actions / Functions](../guides/services/custom-actions#calling-actions-functions). Basically, use the action or function name instead of the HTTP method. +To call bound / unbound actions and functions from the service, further variants of `srv.send` are additionally supported, as described in the section [Calling Actions / Functions](../guides/services/custom-actions#calling-actions--functions). Basically, use the action or function name instead of the HTTP method. Examples: diff --git a/node.js/events.md b/node.js/events.md index c9f325a6f1..bebf5b5509 100644 --- a/node.js/events.md +++ b/node.js/events.md @@ -88,7 +88,7 @@ this.on ('*', req => { ``` Keep in mind that multiple requests (that is, instances of `cds.Request`) may share the same incoming HTTP request and outgoing HTTP response (for example, in case of an OData batch request). -See sections [`req`](#req) and [`res`](#res) of `cds.Request` to learn more about accessing the request and response objects of individual requests within an incoming batch request. +See sections [`req`](#-req) and [`res`](#-res) of `cds.Request` to learn more about accessing the request and response objects of individual requests within an incoming batch request. @@ -155,7 +155,7 @@ On the other hand, setting `req.user` in a custom authentication middleware is d -Class [`cds.Event`] represents event messages in [asynchronous messaging](messaging), providing access to the [event](#event) name, payload [data](#data), and optional [headers](#headers). It also serves as **the base class for [`cds.Request`](#cds-request)** and hence for all synchronous interactions. +Class [`cds.Event`] represents event messages in [asynchronous messaging](messaging), providing access to the [event](#-event) name, payload [data](#-data), and optional [headers](#-headers). It also serves as **the base class for [`cds.Request`](#cds-request)** and hence for all synchronous interactions. @@ -242,7 +242,7 @@ Additional note about OData: For requests that are part of a changeset, the even -Class `cds.Request` extends [`cds.Event`] with additional features to represent and deal with synchronous requests to services in [event handlers](./core-services#srv-handle-event), such as the [query](#query), additional [request parameters](#params), the [authenticated user](#user), and [methods to send responses](#req-reply-results). +Class `cds.Request` extends [`cds.Event`] with additional features to represent and deal with synchronous requests to services in [event handlers](./core-services#srv-handle-event), such as the [query](#-query), additional [request parameters](#-params), the [authenticated user](#-user), and [methods to send responses](#req-reply-results). [Router]: https://expressjs.com/en/4x/api.html#router @@ -254,13 +254,13 @@ Class `cds.Request` extends [`cds.Event`] with additional features to represent ### . req {.property} -Provides access to the express request object of individual requests within an incoming batch request. For convenience, in the case of non-batch requests, it points to the same request object as [`req.http.req`](#http). +Provides access to the express request object of individual requests within an incoming batch request. For convenience, in the case of non-batch requests, it points to the same request object as [`req.http.req`](#-http). ### . res {.property} -Provides access to the express response object of individual requests within an incoming batch request. For convenience, in the case of non-batch requests, it points to the same response object as [`req.http.res`](#http). +Provides access to the express response object of individual requests within an incoming batch request. For convenience, in the case of non-batch requests, it points to the same response object as [`req.http.res`](#-http). @@ -294,7 +294,7 @@ For example: {style="font-style:italic;width:80%;"} -[See also `req.path` to learn how to access full navigation paths.](#path){.learn-more} +[See also `req.path` to learn how to access full navigation paths.](#-path){.learn-more} [See _Entity Definitions_ in the CSN reference.](../cds/csn#entity-definitions){.learn-more} [Learn more about linked models and definitions.](cds-reflect){.learn-more} @@ -303,7 +303,7 @@ For example: ### . path {.property} Captures the full canonicalized path information of incoming requests with navigation. -For requests without navigation, `req.path` is identical to [`req.target.name`](#target) (or [`req.entity`](#entity), which is a shortcut for that). +For requests without navigation, `req.path` is identical to [`req.target.name`](#-target) (or [`req.entity`](#-entity), which is a shortcut for that). Examples based on [cap/samples/bookshop AdminService](https://github.com/capire/bookshop/blob/main/srv/admin-service.cds): @@ -314,14 +314,14 @@ Examples based on [cap/samples/bookshop AdminService](https://github.com/capire/ | Books(201)/author | AdminService.Books/author | AdminService.Authors | {style="font-style:italic"} -[See also `req.target`](#target){.learn-more} +[See also `req.target`](#-target){.learn-more} ### . entity {.property} -This is a convenience shortcut to [`msg.target.name`](#target). +This is a convenience shortcut to [`msg.target.name`](#-target). @@ -545,7 +545,7 @@ All errors are collected in property `req.errors`, which is initially `undefined if (req.errors) ... //> errors occurred ``` -After each phase of request processing, i.e. _before_ / _on_ / _after_, the framework checks whether errors got recorded in `req.errors`. If so, it automatically [rejects](#req-reject) the request with an aggregate error containing all recorded errors, and the request is not processed further. So, in essence, the above ends up in the equivalent of: +After each phase of request processing, that is, _before_ / _on_ / _after_, the framework checks whether errors got recorded in `req.errors`. If so, it automatically [rejects](#req-reject) the request with an aggregate error containing all recorded errors, and the request is not processed further. So, in essence, the above ends up in the equivalent of: ```js return req.reject ({ @@ -570,7 +570,7 @@ req.info ('Some information message') req.warn ('Some warning message') ``` -The methods are similar to [`req.error()`](#req-error), also accepting the [same arguments](#req-reject), but the messages are collected in `req.messages` instead of `req.errors`, not decorated with stack traces, and returned in a HTTP response header (e.g. `sap-messages`), instead of the response body. +The methods are similar to [`req.error()`](#req-error), also accepting the [same arguments](#req-reject), but the messages are collected in `req.messages` instead of `req.errors`, not decorated with stack traces, and returned in a HTTP response header (for example, `sap-messages`), instead of the response body. ::: warning User Input & Injection Vulnerabilities Ensure proper validation of the message text if it contains values ​​from user input. diff --git a/node.js/fiori.md b/node.js/fiori.md index fd09ec3efd..dce63a3a7d 100644 --- a/node.js/fiori.md +++ b/node.js/fiori.md @@ -79,7 +79,7 @@ srv.on('EDIT', MyEntity, /*...*/) The `EDIT` event is triggered when the user starts editing an active entity. As a result, a new entry to `MyEntity.drafts` is created. -For logical reasons handlers for the `EDIT` event are registered on the active entity, i.e. `MyEntity` in the code above, not on the `MyEntity.drafts` entity. +For logical reasons handlers for the `EDIT` event are registered on the active entity, that is, `MyEntity` in the code above, not on the `MyEntity.drafts` entity. ### `PATCH` @@ -105,7 +105,7 @@ srv.on('SAVE', MyEntity.drafts, /*...*/) The `SAVE` event is triggered when the user saves / activates a draft. This results in either a CREATE or an UPDATE on the active entity depending on whether the draft was created via `NEW` or `EDIT`. > [!note] -> The `SAVE` event is also available for non-draft, i.e. active entities. In that case it acts as an convenience shortcut for registering handlers for the combination of `CREATE` and `UPDATE` events. In contrast to that, the `SAVE` event on draft entities is a distinct event that is only triggered when **activating** a draft. +> The `SAVE` event is also available for non-draft, that is, active entities. In that case it acts as an convenience shortcut for registering handlers for the combination of `CREATE` and `UPDATE` events. In contrast to that, the `SAVE` event on draft entities is a distinct event that is only triggered when **activating** a draft. diff --git a/package-lock.json b/package-lock.json index 5d29b6c315..c088223cc1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -952,9 +952,9 @@ } }, "node_modules/@oxc-project/types": { - "version": "0.137.0", - "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.137.0.tgz", - "integrity": "sha512-WT+Gb24i8hmvo85AIv2oEYouEXkRlKAlT9WaCa3TfLgNCN+GhrJOGZuIlMouAh38Qe4QOx26eUOVsq70qXrywA==", + "version": "0.139.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.139.0.tgz", + "integrity": "sha512-r9gHphtCs+1M7J0pw6Sn/hh/Wpa/iQrOOkrNAlVLF/gHq+/CJmHIWKKUUhdWjcD6CIa8idarspCsASiXCXvFUw==", "dev": true, "license": "MIT", "funding": { @@ -1290,9 +1290,9 @@ } }, "node_modules/@rolldown/binding-android-arm64": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.1.3.tgz", - "integrity": "sha512-DT6Z3PhvioeHMvxo+xHc3KtqggrI7CCTXCmC2h/5zUlp5jVitv7XEy+9q5/7v8IolhlioawpMo8Kg0EEBy7J0g==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.1.5.tgz", + "integrity": "sha512-lZg8fqIv2v7FF237bwMgzGZEJvGL79/s5knJ/i6FmsGF4XXlzccZ4jb+TrFIxtSSxFtIpdsgrPZeMk1I9AFcyQ==", "cpu": [ "arm64" ], @@ -1307,9 +1307,9 @@ } }, "node_modules/@rolldown/binding-darwin-arm64": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.1.3.tgz", - "integrity": "sha512-0NwgwsjM7LrsuVnXMK3koTpagBNOhloc/BNjKqZjv4V5zI5r13qx69uVhRx+o5Z0yy4Hzq+lpy7TAgUG/ocvrw==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.1.5.tgz", + "integrity": "sha512-51Bnx9pNiMRKSUNtBfySkNJ9vMU9Hh3I1ozDd6gyPPYzaXCfnptUcEZxXGYFn+ul2dtcMUiqGR1Yai2K10uoTw==", "cpu": [ "arm64" ], @@ -1324,9 +1324,9 @@ } }, "node_modules/@rolldown/binding-darwin-x64": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.1.3.tgz", - "integrity": "sha512-YtiBp4disu6V560loT6PjMdiRaWmVvDNrUunAalbiFx2ggeJwxdAsgZMcoGP17uyAsTwAj5V1niksxlHnVQ1Sw==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.1.5.tgz", + "integrity": "sha512-Tm+gbfC0aHu1tBA/JvKQh32S0K6YgCHkiAF4/W6xX0K0RmNuc94VeK419dJoE65R5aRxmo+noZQSWrAMF6yb6g==", "cpu": [ "x64" ], @@ -1341,9 +1341,9 @@ } }, "node_modules/@rolldown/binding-freebsd-x64": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.1.3.tgz", - "integrity": "sha512-yD3EkEdXk2LypPxnf/kSZHirarsI8gcPzc62SukhR9VJTyvV+F9Q/GxWNuCojc7sXyuVC4DxRGhdDK4X8VSsbw==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.1.5.tgz", + "integrity": "sha512-JMzDKCCXq93YccG5gz3hvOs1oXRKAf0XYpfOS88e+wZrC8Iugj6j68867vrYZkvpDDpKn/KoKORThmchMpF6TA==", "cpu": [ "x64" ], @@ -1358,9 +1358,9 @@ } }, "node_modules/@rolldown/binding-linux-arm-gnueabihf": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.1.3.tgz", - "integrity": "sha512-c+8vieQbsD7HNAHKIA34w0GJ9FedFFuJGD+7E6vz7Q3uqAIugL5p45fhlsj4UaAsHpcmlqugBWMhA0/j7o0sIg==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.1.5.tgz", + "integrity": "sha512-uML21j2K5TfPGutKxub+M+nLjZIrWjXQ5Grx4lCe/nimTj9B4L63zHpjXLl4y0L3mcm2htEQIb06oCG/szerNw==", "cpu": [ "arm" ], @@ -1375,9 +1375,9 @@ } }, "node_modules/@rolldown/binding-linux-arm64-gnu": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.1.3.tgz", - "integrity": "sha512-50jD0uUwLvur7Zz9LHz17kaAdTPjn5wN93hEgjvmYFRZwiR7ZJYovTd5ipyWJDAnXKvZ+wgc+/Ika6dwSF5OcA==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.1.5.tgz", + "integrity": "sha512-navSiuTMogvnQoZoM/v+l3ZWo50/NTwSHSzheABx/RCnmUPaKwq9qSo4Br2OYRs21+Fz8uFqITZM3H4opOB0/Q==", "cpu": [ "arm64" ], @@ -1395,9 +1395,9 @@ } }, "node_modules/@rolldown/binding-linux-arm64-musl": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.1.3.tgz", - "integrity": "sha512-BO9+oPL8K9poZJBfYPsXNtYjPE5uM3qeehT3aFcW4LITOl+iSqhp0abzjR2nWBUNjIZeKXjAEWBZ64WjNoHd6w==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.1.5.tgz", + "integrity": "sha512-lAryqH7IteztmCXQXk0etKj4wBQ7Gx5S6LjKhsgp9zb8I5bsuvU/2llH1hDQcjsFeqIsovMVN339/8pUDDBXxA==", "cpu": [ "arm64" ], @@ -1415,9 +1415,9 @@ } }, "node_modules/@rolldown/binding-linux-ppc64-gnu": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.1.3.tgz", - "integrity": "sha512-f3VpLB1vQ0Eo6ecr/6cekLnvYMFF4YBFoVGkfkvPLq1bAkbAwHYQPZKoAmG6OJyTcxxoC+AvezGx/S1obNC0Mw==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.1.5.tgz", + "integrity": "sha512-fsK/sNBnxzBlL4O1JNrZakVQxPspqpED5dLtNsZS9oOKmtSpdNIzxH2kkol5HYTWJN47sE20ztMJPxfZ89qGOg==", "cpu": [ "ppc64" ], @@ -1435,9 +1435,9 @@ } }, "node_modules/@rolldown/binding-linux-s390x-gnu": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.1.3.tgz", - "integrity": "sha512-AmurZ26Pqx/RI9N1gzEOCklkKXl927yjfXWUUS0O7Puh8ARM/Ob8qfrD3qnWksScdw6cSrW5PSHE9DyLu7+PtA==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.1.5.tgz", + "integrity": "sha512-gLYb4BIadlfTOYT5gO503n8zQjXflgzpD0FcyKh0Mzx3rqCZKnHoJWV9xe1KXUJ5lx2JfcSHr/mhzS0PC/McAA==", "cpu": [ "s390x" ], @@ -1455,9 +1455,9 @@ } }, "node_modules/@rolldown/binding-linux-x64-gnu": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.1.3.tgz", - "integrity": "sha512-JJpqs8bRGITDOdbkNKnlojzBabbOHrqjSvDr0IVsZObE1lBcPjxItUEY9eWIDbxaJ3cGrXPWGfGkIxFijg/URg==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.1.5.tgz", + "integrity": "sha512-FjcpEKUyJygHgs1o50VYNvkt5+7Le/VEdYt0AkRpkL33MnyQfwr8l5mXwMmfmTbyMPr5vJLC+8/Gd9gXnwU1QQ==", "cpu": [ "x64" ], @@ -1475,9 +1475,9 @@ } }, "node_modules/@rolldown/binding-linux-x64-musl": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.1.3.tgz", - "integrity": "sha512-rSJcdjPxzA/by/6/rYs+v+bXU7UjvnbUWz8MJb6kh6+knqB1dCrtHg0uu7C/4haqJvqdkYHQ5IGn+tCH9GLW/g==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.1.5.tgz", + "integrity": "sha512-Me+PfPI2TMeOQk0gYWfLQZtTktrmzbr8cDboqX83XKc7UrgAi55gF+2dUkWdxd19n55Essp2yeca+O9N5rBxHg==", "cpu": [ "x64" ], @@ -1495,9 +1495,9 @@ } }, "node_modules/@rolldown/binding-openharmony-arm64": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.1.3.tgz", - "integrity": "sha512-hQ3/PYkDJICgevvyNcVrihVeqq7k1Pp3VZ9lY+dauAYUJKO+auqApvANhvR1An9BhmqYKvW2Mu1F9u4DXSMLxQ==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.1.5.tgz", + "integrity": "sha512-yc5WrLzXks6zCQfn9Oxr8pORKyl/pF+QjHmW/Qx3qu0oyrrNC+y2JLTU1E2rcWYAmzlnqngWXHQjy51VzW70Vw==", "cpu": [ "arm64" ], @@ -1512,9 +1512,9 @@ } }, "node_modules/@rolldown/binding-wasm32-wasi": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.1.3.tgz", - "integrity": "sha512-Elcv/BtML9lXrV6JuKITc/grN2kYV9gjsQpW8Jfw4ioK0TOkjBjye0nnyqQNy9STNaI20lXNaQBRrD5gSgR0Yg==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.1.5.tgz", + "integrity": "sha512-VbQGPX2b4r48TAMIM2cjgluIM1HYutm4pcTEJsle7iEP7sB1dFqtPLBVbdLAZCxy1txCcPxf4QFf4v8uvltPqA==", "cpu": [ "wasm32" ], @@ -1531,9 +1531,9 @@ } }, "node_modules/@rolldown/binding-win32-arm64-msvc": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.1.3.tgz", - "integrity": "sha512-2DrEfhluH9yhiaFApmsjsjwrSYbNcY1oFTzYSP1a535jDbV98zCFanA/96TBUd0iDFcxGmw9QRExwGCXz3U+/g==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.1.5.tgz", + "integrity": "sha512-gHv82k63z4qpV5+Q1y/12KrK0ltWBukVDI8nZcbT7Tt/ZlOIVwppazneq0F93oDxTo3IgAMEDIoQh3E2n6mVsw==", "cpu": [ "arm64" ], @@ -1548,9 +1548,9 @@ } }, "node_modules/@rolldown/binding-win32-x64-msvc": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.1.3.tgz", - "integrity": "sha512-OL4OMk7UPXOeVGGd3qo5zJyPIljf4AFgk5QAkPPS+OoLuOOozhuaQGC18MxVTnw/06q93gShAJzlwnSCY9YtqA==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.1.5.tgz", + "integrity": "sha512-tTZuDBPw85tEN5PQi1pnEBzDy0Z49HtScLAbD5t6hyeU92A95pRWaSMw1GZZi/RwgSgUIl0xrSlXIT/9QzvYSA==", "cpu": [ "x64" ], @@ -5808,9 +5808,9 @@ "license": "ISC" }, "node_modules/picomatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", - "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz", + "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==", "dev": true, "license": "MIT", "engines": { @@ -5990,13 +5990,13 @@ "license": "MIT" }, "node_modules/rolldown": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.1.3.tgz", - "integrity": "sha512-1F1eEtUBtFvcGm1HQ9TiUIUHPQG7mSAODrhIzjxoUEFuo8OcbrGLiVLkevNgj84TE4lnHvnumwFjhJO5Eu135g==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.1.5.tgz", + "integrity": "sha512-t9z29cJjXf/vxQ8dyhCSpt6H6aSwHTk8cT5I3iy6SMXuFpk5mB6PL6XfC8PCwrPTx93udwKUm9HRteAlTGBLiA==", "dev": true, "license": "MIT", "dependencies": { - "@oxc-project/types": "=0.137.0", + "@oxc-project/types": "=0.139.0", "@rolldown/pluginutils": "^1.0.0" }, "bin": { @@ -6006,21 +6006,21 @@ "node": "^20.19.0 || >=22.12.0" }, "optionalDependencies": { - "@rolldown/binding-android-arm64": "1.1.3", - "@rolldown/binding-darwin-arm64": "1.1.3", - "@rolldown/binding-darwin-x64": "1.1.3", - "@rolldown/binding-freebsd-x64": "1.1.3", - "@rolldown/binding-linux-arm-gnueabihf": "1.1.3", - "@rolldown/binding-linux-arm64-gnu": "1.1.3", - "@rolldown/binding-linux-arm64-musl": "1.1.3", - "@rolldown/binding-linux-ppc64-gnu": "1.1.3", - "@rolldown/binding-linux-s390x-gnu": "1.1.3", - "@rolldown/binding-linux-x64-gnu": "1.1.3", - "@rolldown/binding-linux-x64-musl": "1.1.3", - "@rolldown/binding-openharmony-arm64": "1.1.3", - "@rolldown/binding-wasm32-wasi": "1.1.3", - "@rolldown/binding-win32-arm64-msvc": "1.1.3", - "@rolldown/binding-win32-x64-msvc": "1.1.3" + "@rolldown/binding-android-arm64": "1.1.5", + "@rolldown/binding-darwin-arm64": "1.1.5", + "@rolldown/binding-darwin-x64": "1.1.5", + "@rolldown/binding-freebsd-x64": "1.1.5", + "@rolldown/binding-linux-arm-gnueabihf": "1.1.5", + "@rolldown/binding-linux-arm64-gnu": "1.1.5", + "@rolldown/binding-linux-arm64-musl": "1.1.5", + "@rolldown/binding-linux-ppc64-gnu": "1.1.5", + "@rolldown/binding-linux-s390x-gnu": "1.1.5", + "@rolldown/binding-linux-x64-gnu": "1.1.5", + "@rolldown/binding-linux-x64-musl": "1.1.5", + "@rolldown/binding-openharmony-arm64": "1.1.5", + "@rolldown/binding-wasm32-wasi": "1.1.5", + "@rolldown/binding-win32-arm64-msvc": "1.1.5", + "@rolldown/binding-win32-x64-msvc": "1.1.5" } }, "node_modules/rollup": { @@ -6663,16 +6663,16 @@ } }, "node_modules/vite": { - "version": "8.1.3", - "resolved": "https://registry.npmjs.org/vite/-/vite-8.1.3.tgz", - "integrity": "sha512-Ds+gBRbj0lwRO2Y5hwnUBdxSwlAve9LeRyU4sNnAr0ewW0gWF0n5bgXgUzbgZ49MV9BVUAQUFYVcDUcilUExMA==", + "version": "8.1.4", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.1.4.tgz", + "integrity": "sha512-bTT9PsdWO+MQMNG9ZXIP/qM9wGh37DFxTV/sPq9cFpHr3w4jkgef032PkAL9jAqhk3Nz8NQw3O8n6/xFkqO4QQ==", "dev": true, "license": "MIT", "dependencies": { "lightningcss": "^1.32.0", - "picomatch": "^4.0.4", + "picomatch": "^4.0.5", "postcss": "^8.5.16", - "rolldown": "~1.1.3", + "rolldown": "~1.1.4", "tinyglobby": "^0.2.17" }, "bin": { diff --git a/package.json b/package.json index 5616464e2e..033b51ba9e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@capire/docs", - "version": "2026.6.0", + "version": "2026.6.1", "description": "Capire on VitePress", "type": "module", "scripts": { diff --git a/redirects.md b/redirects.md index 2c7c01f270..82d4445c9d 100644 --- a/redirects.md +++ b/redirects.md @@ -42,6 +42,7 @@ - [guides/using-services](guides/integration/calesi) - [guides/databases-hana](guides/databases/hana) - [guides/databases-postgres](guides/databases/postgres) +- [guides/databases-sqlite](guides/databases/sqlite) - [security](guides/security/) - [security/aspects](guides/security/overview) diff --git a/tools/apis/cds-build.md b/tools/apis/cds-build.md index 0d9528617d..b41965ecf5 100644 --- a/tools/apis/cds-build.md +++ b/tools/apis/cds-build.md @@ -65,7 +65,7 @@ The CDS build system auto-detects all required build tasks by invoking the stati The compiled CSN model can be accessed using the asynchronous methods `model()` or `basemodel()`. - The method `model()` returns a CSN model for the scope defined by the `options.model` setting. If [feature toggles](../../guides/extensibility/feature-toggles) are enabled, this model also includes any toggled feature enhancements. -- To get a CSN model without features, use the method `baseModel()` instead. The model can be used as input for further [model processing](../../node.js/cds-compile#cds-compile-to), like `to.edmx`, `to.hdbtable`, `for.odata`, etc. +- To get a CSN model without features, use the method `baseModel()` instead. The model can be used as input for further [model processing](../../node.js/cds-compile#cds-compile-to-), like `to.edmx`, `to.hdbtable`, `for.odata`, etc. - Use [`cds.reflect`](../../node.js/cds-reflect) to access advanced query and filter functionality on the CDS model. ## Add build task type to cds schema diff --git a/tools/cds-cli.md b/tools/cds-cli.md index c71e8f1bde..fe8eb4d20d 100644 --- a/tools/cds-cli.md +++ b/tools/cds-cli.md @@ -258,7 +258,7 @@ The result could look like this for a typical _Books_ entity from the _Bookshop_ - `author.ID` refers to a key from the _...Authors.json_ file that is created at the same time. If the _Authors_ entity is excluded, though, no such foreign key would be created, which cuts the association off. - Data for _compositions_, like the `texts` composition to `Books.texts`, is always created. - A random unique number for each record, _29894036_ here, is added to each string property, to help you correlate properties more easily. -- Data for elements annotated with a regular expression using [`assert.format`](../guides/services/constraints#assert-format) can be generated using the NPM package [randexp](https://www.npmjs.com/package/randexp), which you need to installed manually. +- Data for elements annotated with a regular expression using [`assert.format`](../guides/services/constraints#assertformat) can be generated using the NPM package [randexp](https://www.npmjs.com/package/randexp), which you need to installed manually. - Other constraints like [type formats](../cds/types), [enums](../cds/cdl#enums), and [validation constraints](../guides/services/constraints) are respected as well, in a best effort way. ::: @@ -397,7 +397,7 @@ Compiles the specified models to [CSN](../cds/csn) or other formats. [See simple examples in the getting started page](../get-started/bookshop).{.learn-more} -[For the set of built-in compile 'formats', see the `cds.compile.to` API](../node.js/cds-compile#cds-compile-to).{.learn-more} +[For the set of built-in compile 'formats', see the `cds.compile.to` API](../node.js/cds-compile#cds-compile-to-).{.learn-more} In addition, the following formats are available: @@ -482,8 +482,8 @@ could look like this: ```cds [srv/data-service.cds] using { sap.capire.flights as my } from '../db/schema'; -@data.product @hcql @rest @odata -service sap.capire.flights.data { +@hcql @rest @odata +service FlightsService { @readonly entity Flights as projection on my.Flights; @readonly entity Airlines as projection on my.Airlines; @readonly entity Airports as projection on my.Airports; @@ -503,9 +503,11 @@ The command generates the API client package into a new folder _apis/data-servic ![The screenshot is described in the accompanying text.](assets/cds-export.png) {style="filter: drop-shadow(0 2px 5px rgba(0,0,0,.40));"} The `service.csn` contains only the interface defined in the service, removing the query part of the entities and all the underlying model. -In addition, there are i18n bundles with the localized metadata relevant -for the interface, and a _data_ folder with test data -that exactly matches the structure of the entities in the API. +In addition (and as shown in the screenshot): + +- with the `--texts` option, i18n bundles are included with the localized metadata relevant for the interface +- with the `--data` option, initial data that exactly matches the structure of the entities in the API is included +- with the `--plugin` option, a `cds-plugin.js` entrypoint file is included for plug & play `cds export` also adds a _package.json_. The package name combines the application name (from the main _package.json_) with the file name of the data service. In our example, this results in `@capire/xflights-data-service`. You can change this name as appropriate. diff --git a/tools/cds-typer.md b/tools/cds-typer.md index a2f5e45493..ffa0464628 100644 --- a/tools/cds-typer.md +++ b/tools/cds-typer.md @@ -455,7 +455,7 @@ const { Books } = require('../@cds-models/sap/capire/bookshop') const { Books } = require('#cds-models/sap/capire/bookshop') ``` -These imports will behave like [`cds.entities('sap.capire.bookshop')`](../node.js/cds-reflect#entities) during runtime, but offer you code completion and type hinting at design time: +These imports will behave like [`cds.entities('sap.capire.bookshop')`](../node.js/cds-reflect#-entities) during runtime, but offer you code completion and type hinting at design time: ```js twoslash // @noErrors