From 507cda4e7bee55cd6234d8a62d385eef90223c0b Mon Sep 17 00:00:00 2001 From: Nick Josipovic Date: Mon, 6 Nov 2023 14:21:21 +0100 Subject: [PATCH 01/43] Update performance-modeling.md (#513) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update performance-modeling.md We have updated the calculated fields section to now reflect the new CDS calculated element capabilities. @renejeglinsky we do not know how to properly link other Capire pages within the markup, so I pasted two absolute links in the text. Can you please replace them with the proper internal link syntax? * Apply suggestions from code review * Update performance-modeling.md Adapted feedback from Rene --------- Co-authored-by: René Jeglinsky --- advanced/performance-modeling.md | 34 +++++++------------------------- 1 file changed, 7 insertions(+), 27 deletions(-) diff --git a/advanced/performance-modeling.md b/advanced/performance-modeling.md index 4ad20ed4b..756896a30 100644 --- a/advanced/performance-modeling.md +++ b/advanced/performance-modeling.md @@ -277,8 +277,8 @@ Typical examples of calculated fields are: The following steps show you which option takes precedence over another. Use options one/two as the preferred way and three/four as fallback. 1. Do the calculation on the UI with help of field controls or dedicated custom controls. This applies to all kinds of **String concatenation** and **Formatting**. -2. Pre-calculate on *write* with help of an event handler. -3. Some Calculated Fields are dynamic in nature. Do those calculations on the database layer. For example _kanban_ (scheduling system for lean manufacturing), there you typically have dynamic live calculations. +2. Pre-calculate using CDS [on write](../cds/cdl#on-write) calculated fields. +3. Some calculations are dynamic in nature. If possible, use CDS [on read](../cds/cdl#on-read) calculated fields. 4. As a **very last resort**, use event handlers on *read*. Hints: @@ -312,35 +312,15 @@ entity OrdersItemsView as projection on OrdersItems { ::: code-group ```cds [schema.cds] extend my.OrdersItems with { - itemCategory: String enum{ Small; Medium; Large;}; - // fill itemCategory at runtime in service.js + category: String = case + when quantity > 500 then 'Large' + when quantity > 100 then 'Medium' + else 'Small' + end stored; } ``` ::: -::: code-group -```js [service.js] -... -// fill itemCategory at runtime - this.before (['CREATE', 'UPDATE'], 'my.OrdersItems', async req => { - if (req.data.quantity > 500) {req.data.itemCategory = 'Large'} - else if (req.data.quantity > 100) {req.data.itemCategory = 'Medium'} - else {req.data.itemCategory = 'Small'} - }) -... -``` -::: - -New `OrdersItemsView` without case statement: - -::: code-group -```cds [service.cds] -entity OrdersItemsView as projection on OrdersItems { - *, - itemCategory as category -}; -``` -::: ## Compositions vs Associations From the performance perspective there are some cases, where you have to check out carefully if the general semantic rules of compositions vs associations should be applied. From 9832eded1e53725927c3428c103f47927b01b607 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 6 Nov 2023 14:21:54 +0100 Subject: [PATCH 02/43] Update dependency com.sap.cds:cds4j-api to v2.3.1 (#517) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .vitepress/config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.vitepress/config.ts b/.vitepress/config.ts index 565ad3829..cecff5eee 100644 --- a/.vitepress/config.ts +++ b/.vitepress/config.ts @@ -22,7 +22,7 @@ const redirectLinks: Record = {} const latestVersions = { java_services: '2.3.0', - java_cds4j: '2.3.0' + java_cds4j: '2.3.1' } const localSearchOptions = { From a0cfda62adfe0ec83876dcaa735c8c7ebf3e573b Mon Sep 17 00:00:00 2001 From: Rene Jeglinsky Date: Mon, 6 Nov 2023 15:23:03 +0100 Subject: [PATCH 03/43] linking --- guides/security/aspects.md | 4 +++- java/development/index.md | 2 +- java/persistence-services.md | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/guides/security/aspects.md b/guides/security/aspects.md index 9259341bc..96856b59c 100644 --- a/guides/security/aspects.md +++ b/guides/security/aspects.md @@ -233,7 +233,9 @@ Based on configured features, the CAP runtime exposes additional callback endpoi | Platform service | URL | Authorization | |------------------------------|-----------------------------|--------------------------------------------------------------------------------------------------------| -| Multitenancy (SaaS Registry) | `/mt/v1.0/subscriptions/**` | [Technical role](../deployment/as-saas#xsuaa-mt-configuration) `mtcallback` | +| Multitenancy (SaaS Registry) | `/mt/v1.0/subscriptions/**` | Technical role `mtcallback` | + + diff --git a/java/development/index.md b/java/development/index.md index 78b1d9536..d854236a2 100644 --- a/java/development/index.md +++ b/java/development/index.md @@ -448,7 +448,7 @@ If the Spring Boot Devtools configuration of your CAP Java application defines a #### Local Development for Multitenant Applications -With the streamlined MTX, you can run your multitenant application locally along with the MTX sidecar and use SQLite as the database. See [the _Deploy as SaaS_ guide](../../guides/deployment/as-saas?impl-variant=java#local-mtx) for more information. +With the streamlined MTX, you can run your multitenant application locally along with the MTX sidecar and use SQLite as the database. See [the _Multitenancy_ guide](../../guides/multitenancy/#test-locally) for more information.
diff --git a/java/persistence-services.md b/java/persistence-services.md index 1022e87ea..363d04230 100644 --- a/java/persistence-services.md +++ b/java/persistence-services.md @@ -367,7 +367,7 @@ At runtime you need to ensure to access the tenant-dependent entities through th #### Local Development and Testing with MTX -In case you are testing your multitenant application locally with the setup described in [Local Development and Testing](../guides/deployment/as-saas?impl-variant=java#local-mtx) of the "Deploy as Multitenant SaaS Application" cookbook you need to perform additional steps to create an in-memory tenant-independent datasource. +In case you are testing your multitenant application locally with the setup described in [Local Development and Testing](../guides/multitenancy/#test-locally), you need to perform additional steps to create an in-memory tenant-independent datasource. To create an in-memory datasource, initialized with the SQL schema, add the following configuration to your Spring Boot application: From 86b0443f96c8f72174d22ba26027d1ae1fe7774e Mon Sep 17 00:00:00 2001 From: Rene Jeglinsky Date: Mon, 6 Nov 2023 15:45:52 +0100 Subject: [PATCH 04/43] linking --- java/observability.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/observability.md b/java/observability.md index 3e3539f29..1af103d00 100644 --- a/java/observability.md +++ b/java/observability.md @@ -214,7 +214,7 @@ In case you've configured `cf-java-logging-support` as described in [Logging Ser - Generation of IDs in non-HTTP contexts - Thread propagation through [Request Contexts](./request-contexts#threading-requestcontext) -- Propagation to remote services when called via CloudSDK (for instance [Remote Services](./remote-services) or [MTX sidecar](./multitenancy#mtx-sidecar-server)) +- Propagation to remote services when called via CloudSDK (for instance [Remote Services](./remote-services) or [MTX sidecar](./multitenancy/#enable-multitenancy)) By default, the ID is accepted and forwarded via HTTP header `X-CorrelationID`. If you want to accept `X-Correlation-Id` header in incoming requests alternatively, follow the instructions given in the guide [Instrumenting Servlets](https://github.com/SAP/cf-java-logging-support/wiki/Instrumenting-Servlets#correlation-id). From 014b04b3e4908ded32d5b44bf6fc13d5e39da90d Mon Sep 17 00:00:00 2001 From: Daniel Hutzel Date: Mon, 6 Nov 2023 15:59:24 +0100 Subject: [PATCH 05/43] Removed numbers --- get-started/-using-mock-servers.md | 272 ----------------------------- get-started/in-a-nutshell.md | 18 +- 2 files changed, 9 insertions(+), 281 deletions(-) delete mode 100644 get-started/-using-mock-servers.md diff --git a/get-started/-using-mock-servers.md b/get-started/-using-mock-servers.md deleted file mode 100644 index 8ac4a6353..000000000 --- a/get-started/-using-mock-servers.md +++ /dev/null @@ -1,272 +0,0 @@ ---- -shorty: Grow As You Go -synopsis: > - This section contains **best practices for speeding up development** by jump-starting projects - with zero setup, eliminating boilerplate code, and parallelizing work. -# status: released ---- - - -# Grow As You Go... - -{{$frontmatter?.synopsis}} - - - - -## Jump-Starting Projects {#jumpstart} - -Assuming that you've installed [_@sap/cds-dk_](jumpstart#setup), jump-starting a project in CAP is as simple as this: - -```sh -mkdir demo && cd demo # create a new project folder -cds watch # ask cds to watch for things to come -``` - - -###### Convention over Configuration - -Following the principles of *Convention over Configuration*, CAP provides defaults for many things that you'd have to configure in other frameworks. Stay within the confines of these defaults to benefit from things just working automatically. You can always override the defaults by adding your own configurations. - -###### Zero Setup - -The project folder is empty at the beginning, so there's really no setup required. No config files, no manifests whatsoever. The simple reason is that we don't need them in these early phases of a project. We may need some later on, for example, when we reach the step about [Deploying to the Cloud](../guides/deployment/), but there's no need to bother with this now. - -## Contracts First - -Most frequently, CAP projects are about creating services. Service definitions describe the API contract between a service provider and its consumers. To speed things up, you can quickly create an all-in-one service as follows: - -Copy and paste this into a file `srv/cat-service.cds`: - -```cds -@path:'/browse' -service CatalogService { - - entity Books { - key ID : UUID; - title : String; - descr : String; - author : Association to Authors; - } - - entity Authors { - key ID : UUID; - name : String; - books : Association to many Books on books.author=$self; - birth : Date; - death : Date; - } - -} -``` - - - -###### Prefer Top-Down Approaches - -Instead of following a bottom-up approach, starting from the data model, then putting services on top, then adding UIs, and so on, it's much better to apply a top-down approach as follows: - -1. Roughly sketch your application's usage and use-cases, for example, your UIs. -2. Capture required interfaces in [use case-oriented service definitions](#single-purposed-services). -3. Start building the actual UIs on top, ***and in parallel…*** -4. Have other teams working on the data models below. - -This ***(a)*** allows you to separate and parallelize work loads and ***(b)*** results in much better service interfaces than the one you'd get by using the bottom-up approach. - -
- -## Running Out of the Box - -When we save the file we created in the former step, the `cds` watcher in the terminal immediately reacts, showing this output: - -```sh -[cds](cds) - connect to datasource - sqlite::memory: -[cds](cds) - serving CatalogService at /browse -[cds](cds) - service definitions loaded from: - - srv/cat-service.cds - node_modules/@sap/cds/common.cds - -[cds](cds) - server listening on http://localhost:4004 ... (terminate with ^C) -[cds](cds) - launched in: 355.732ms -``` - -###### Full-Fledged OData Services - -Click the link [http://localhost:4004](http://localhost:4004), ... et voila, we are in contact with a full-fledged OData service (for example, see [$metadata](http://localhost:4004/browse/$metadata)​) in which we can even start [Fiori previews](http://localhost:4004/$fiori-preview/?service=CatalogService&entity=Books) to get idea of what a UI might look like. - -Let's do some **ad hoc tests**: - -* [Browse Books w/ author's name](http://localhost:4004/browse/Books?$select=ID,title&$expand=author($select=name)) -* [Browse Authors w/ written books](http://localhost:4004/browse/Authors?$expand=books) - -###### Served by Generic Providers - -What we see here are the effects of [Generic Providers](../guides/providing-services), which handle many things out of the box, such as compiling the CDS models into [OData](../advanced/odata) `$metadata` documents on the fly, as well automatically serving all CRUD requests, thereby handling all the OData protocol features such as `$batch`, up to complex choreographies such as [Fiori Draft](../advanced/fiori#draft-support). This saves us lots of work at this point and allows us to immediately go on with the next steps instead of implementing all of this in boilerplate code. - -[Learn more about generic providers.](../guides/providing-services){.learn-more} - -
- -## Mocking App Services {#with-mocks} - -Use `cds run --in-memory` to quickly start a lightweight Node.js server with *sqlite's* transient in-memory database instead of always deploying to and connecting to your target database. Do that not only in Node.js projects but also as a mock server in Java projects, for example, for frontend-related tasks, or as a mock up for remote services to integrate with. - -*Prerequisites* - -* API description of the mocked service -* CAP tools installed ([Create a Business Service with Node.js Using Visual Studio Code](https://developers.sap.com/tutorials/cp-apm-nodejs-create-service.html)) -* Node.js installed ([Official Node.js website](https://nodejs.org)) - -> The sample mock server created in the following steps is based on the mock server developed in the TechEd 2019 tutorial [Creating an SAP S/4HANA Extension with SAP Cloud Application Programming Model and SAP Cloud SDK](https://github.com/SAP-samples/cloud-cap-walkthroughs/tree/master/exercises-node/exercise04). - -###### Create a Project for the Mock Server - -1. Create an empty project for the mock server by executing `cds init mockserver` in the terminal. -2. Execute `code mockserver` to open the newly created project in VS Code. -3. Open the package.json file and add `"@sap/cds-dk": "^1.0.0"` as a dependency. Execute `npm i` to install all dependencies. - -###### Add Service API Definition - -1. Download the service API definition from the [SAP Business Accelerator Hub](https://api.sap.com/api/API_BUSINESS_PARTNER/overview) in EDMX format. -2. Import the downloaded API definition by running `cds import ~/Downloads/API_BUSINESS_PARTNER.edmx`. -This converts the EDMX service API definition to a Core Schema Notation (CSN) definition and places it into a local subfolder `srv/external`. - -###### Add a Dummy `services.cds` File - -1. In the `srv` folder, create a file named `services.cds`. -2. Add this line in the file: - -```cds -using { API_BUSINESS_PARTNER } from './external/API_BUSINESS_PARTNER'; -``` - -> Keep this file empty to serve imported APIs the default way. It can be used to tailor the mock server to your specific needs, for example, by adding or overriding certain definitions. - -###### Run the Mock Server - -1. Execute `cds run --with-mocks --in-memory` to start the mock server with in-memory database. - -Alternatively you can execute `cds watch`, which essentially is a shortcut to the same `cds run` command but also starts a monitor to restart the server automatically if sources are changed. -{.indent} - -###### Optionally Add Sample Data - -1. Create a new file `init.js` in the `srv` folder. -2. Paste the following code: - -```js -module.exports = (db)=>{ - const { A_BusinessPartnerAddress: Addresses } = db.entities( - 'API_BUSINESS_PARTNER' - ) - return cds.run ([ - INSERT.into(Addresses).columns( - 'BusinessPartner', - 'AddressID', - 'CityName', - 'StreetName' - ).rows( - [ '1003764', '28238', 'Walldorf', 'Dietmar-Hopp-Allee' ], - [ '1003765', '28241', 'Palo Alto', 'Hillview Avenue' ], - [ '1003766', '28244', 'Hallbergmoos', 'Zeppelinstraße' ], - [ '1003767', '28247', 'Potsdam', 'Konrad-Zuse-Ring' ] - ) - // add more INSERTs here, as appropriate - ]) -} -``` - -###### Mock Custom Responses - -To extend the mock server with custom logic, you can [create a custom handler](../guides/providing-services#custom-logic). To do so, create a `.js` file with the same name next to the imported service definition file, in our case `srv/external/API_BUSINESS_PARTNER.js`. Add the custom logic there: - -```js -module.exports = cds.service.impl (srv => { - // add your custom handlers here... -}) -``` - -###### Mock Error Cases - -To create error cases, explicitly return errors in a custom handler by using the [`req.error`](../node.js/events#req-error) or [`req.reject`](../node.js/events#req-reject) functions. For example, add the following code in the `API_BUSINESS_PARTNER.js` file: - -```js -module.exports = cds.service.impl(srv => { - srv.before('READ', 'A_BusinessPartnerAddress', req => { - const { BusinessPartner, AddressID:ID } = req.data - if (BusinessPartner === '1003764' && ID === '28238') - req.reject (500, 'Your error message.') - }) -}) -``` - -To trigger this error, use the following request: - -```http -GET http://localhost:4004/api-business-partner/A_BusinessPartnerAddress(BusinessPartner='1003764',AddressID='28238') -``` - -###### Reset Mock Data at Runtime - -To reset the mock data at runtime without restarting the mock server, define an [unbound action](../guides/providing-services#actions-functions). - -> When using `cds watch`, executing `rs` in the terminal with the running watch command will restart the mock server and reset the mock data without the need of an unbound action. - -Declare the action in the mock service definition. In `srv/services.cds` add the following code: - -```cds -extend service API_BUSINESS_PARTNER with { - action reset(); -} -``` - -In `srv/external/API_BUSINESS_PARTNER.js` add the implementation of the action: - -```js - srv.on('reset',async () => { - const db = await cds.connect.to('db') - await db.run(()=> require('../init')(db)) - }) -``` - -This will delete the data from the database and fill it with the initial data. - -Trigger the reset action with the following POST request: - -```http -GET http://localhost:4004/api-business-partner/reset -``` - -
- - -## Growing On... - -* [Domain Modeling](../guides/domain-modeling) -* [Providing](../guides/providing-services) -* [Events & Messaging](../guides/messaging/) -* [Using Generic Providers](../guides/providing-services#generic-providers) -* [Using Databases](../guides/databases) -* [Localization (i18n)](../guides/i18n) -* [Adding Localized Data](../guides/localized-data) -* [Adding Temporal Data](../guides/temporal-data) -* [Adding Authorization](../guides/authorization) -* [Adding Data Privacy](../guides/data-privacy/) -* [Using Multitenancy](../guides/deployment/as-saas) -* [Reuse & Compose](../guides/extensibility/composition) -* [SaaS Extensibility](../guides/extensibility/) -* [Serving OData APIs](../advanced/odata) -* [Serving SAP Fiori UIs](../advanced/fiori) -* [Deploying to the Cloud](../guides/deployment/) -* [Adding Audit Logging](../guides/data-privacy/audit-logging) -* [Using Monitoring](../advanced/monitoring) & Analytics -* Adding Tests -* [Using CI/CD](../guides/deployment/cicd) - -## Deploying to the Cloud - -CAP applications can be deployed to SAP BTP, Cloud Foundry environment. In the end, it's about deploying regular Node.js and/or Java applications, and about creating and binding appropriate service instances (see the [Cloud Foundry Developer Guide](https://docs.cloudfoundry.org/devguide/)). For more details, see [Deploying to the Cloud](../guides/deployment/). - - -
\ No newline at end of file diff --git a/get-started/in-a-nutshell.md b/get-started/in-a-nutshell.md index bd1364ff8..2bbef7a09 100644 --- a/get-started/in-a-nutshell.md +++ b/get-started/in-a-nutshell.md @@ -24,7 +24,7 @@ impl-variants: true background-size: 20px; line-height: 22px; border-radius: 50%; - font-weight: 400; + font-weight: 500; text-align: center; font-size: 12px; vertical-align: middle; @@ -80,7 +80,7 @@ git clone https://github.com/sap-samples/cloud-cap-samples-java bookshop -## 1. Jumpstart a Project { #start-a-project} +## Jumpstart a Project { #start-a-project} 1. Create a new project using `cds init` @@ -141,7 +141,7 @@ git clone https://github.com/sap-samples/cloud-cap-samples-java bookshop -## 2. Capture Domain Models { #domain-models } +## Capture Domain Models { #domain-models } Let's feed our project by adding a simple domain model. Start by creating a file named _db/schema.cds_ (also indicated in the code box's label) and copy the following definitions into it: @@ -186,7 +186,7 @@ _Find this source also in `cap/samples` [for Node.js](https://github.com/sap-sam [Learn more about **CDS Modeling Languages**.](../cds/){ .learn-more} -### Deployed to Databases Automatically {#deployed-in-memory} +### Automatically Deployed to Databases {#deployed-in-memory}
@@ -229,7 +229,7 @@ cds db/schema.cds -2 sql -## 3. Providing Services { #defining-services} +## Declaring Services {#services} @@ -351,7 +351,7 @@ cds srv/cat-service.cds -2 edmx Essentially, using a CLI, this invokes what happened automatically behind the scenes in the previous steps. While we don't really need such explicit compile steps, you can do this to test correctness on the model level, for example. -## 4. Using Databases {#databases} +## Using Databases {#databases} @@ -483,7 +483,7 @@ cds deploy --to hana [Learn more about deploying to SAP HANA.](../guides/databases){.learn-more .impl .node} -## 5. Adding/Serving UIs {#adding-serving-uis} +## Adding/Serving UIs {#uis} You can consume the provided services, for example, from UI frontends, using standard AJAX requests. Simply add an _index.html_ file into the _app/_ folder, to replace the generic index page. @@ -491,7 +491,7 @@ Simply add an _index.html_ file into the _app/_ folder, to replace the generic i ### Vue.js UIs {#vue .impl .node} -For example, you can [find a simple Vue.js app in **cap/samples**](https://github.com/sap-samples/cloud-cap-samples/tree/main/bookshop/app/vue), which demonstrates browsing and ordering books using OData requests to [the `CatalogService` API we defined above](#defining-services). {.impl .node} +For example, you can [find a simple Vue.js app in **cap/samples**](https://github.com/sap-samples/cloud-cap-samples/tree/main/bookshop/app/vue), which demonstrates browsing and ordering books using OData requests to [the `CatalogService` API we defined above](#services). {.impl .node} ![Shows the famous bookshop catalog service in a simple Vue.js UI.](assets/in-a-nutshell/vue-app.png){style="margin:0" .impl .node .adapt} @@ -513,7 +513,7 @@ query options, such as `$select`, `$expand`, `$search`, and many more. [Learn more about **Serving OData Protocol**.](../advanced/odata){.learn-more} -## 6. Adding Custom Logic {#adding-custom-logic} +## Adding Custom Logic While the generic providers serve most CRUD requests out of the box, you can add custom code to deal with the specific domain logic of your application. From e7f8a3fe43b1d497439c093f0609e4a0c92c4eda Mon Sep 17 00:00:00 2001 From: Rene Jeglinsky Date: Mon, 6 Nov 2023 19:46:15 +0100 Subject: [PATCH 06/43] fix links --- java/observability.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/observability.md b/java/observability.md index 1af103d00..3e3539f29 100644 --- a/java/observability.md +++ b/java/observability.md @@ -214,7 +214,7 @@ In case you've configured `cf-java-logging-support` as described in [Logging Ser - Generation of IDs in non-HTTP contexts - Thread propagation through [Request Contexts](./request-contexts#threading-requestcontext) -- Propagation to remote services when called via CloudSDK (for instance [Remote Services](./remote-services) or [MTX sidecar](./multitenancy/#enable-multitenancy)) +- Propagation to remote services when called via CloudSDK (for instance [Remote Services](./remote-services) or [MTX sidecar](./multitenancy#mtx-sidecar-server)) By default, the ID is accepted and forwarded via HTTP header `X-CorrelationID`. If you want to accept `X-Correlation-Id` header in incoming requests alternatively, follow the instructions given in the guide [Instrumenting Servlets](https://github.com/SAP/cf-java-logging-support/wiki/Instrumenting-Servlets#correlation-id). From bc556ddb274d764585ca8f450a6670590d9f4147 Mon Sep 17 00:00:00 2001 From: "Keckl, Matthias" <127967727+MatthiasAtSAP@users.noreply.github.com> Date: Tue, 7 Nov 2023 08:17:51 +0100 Subject: [PATCH 07/43] Fix: Fenced code block json to js (#503) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit json to js Co-authored-by: René Jeglinsky --- advanced/hybrid-testing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/advanced/hybrid-testing.md b/advanced/hybrid-testing.md index 749b0f7d8..8804c9888 100644 --- a/advanced/hybrid-testing.md +++ b/advanced/hybrid-testing.md @@ -222,7 +222,7 @@ cds env get requires.db.credentials --profile hybrid --resolve-bindings Example output: -```json +```js { url: 'jdbc:sap://BDB9AC0F20CB46B494E6742047C4F99A.hana.eu10.hanacloud.ondemand.com:443?encrypt=true&validateCertificate=true¤tschema=BDB9AC0F20CB46B494E6742047C4F99A', host: 'bdb9ac0f20cb46b494e6742047c4f99a.hana.eu10.hanacloud.ondemand.com', From c246fdedbfae017d786bcf6c101aade54a0f1980 Mon Sep 17 00:00:00 2001 From: hjboth <124381150+hjboth@users.noreply.github.com> Date: Tue, 7 Nov 2023 08:18:07 +0100 Subject: [PATCH 08/43] Correct default cardinality for a managed composition of aspect (#502) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Correct default cardinality for a managed composition of aspect If no cardinality is specified the default is 'to-one' and to be more precisely [0..1], as the header may have no items but an item must have exactly one header [1..1]. This is expressed with the `min`/`max` properties on the `up_` association. * of one * Update cds/cdl.md Co-authored-by: Adrian Görler --------- Co-authored-by: René Jeglinsky Co-authored-by: Adrian Görler --- cds/cdl.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cds/cdl.md b/cds/cdl.md index 96a4cfaf8..e477a4fd7 100644 --- a/cds/cdl.md +++ b/cds/cdl.md @@ -722,7 +722,7 @@ aspect OrderItems { #### Default Target Cardinality -If not otherwise specified, a managed composition of an aspect has the default target cardinality *to many*. +If not otherwise specified, a managed composition of an aspect has the default target cardinality *to-one*. #### For Many-to-many Relationships From 5a87bc09fabbf46e500b96ca49ae2d7ee23320d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Jeglinsky?= Date: Tue, 7 Nov 2023 15:37:46 +0100 Subject: [PATCH 09/43] switch to config.activate.on-profile: (#522) --- guides/databases-sqlite.md | 4 ++-- java/persistence-services.md | 6 +++--- java/security.md | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/guides/databases-sqlite.md b/guides/databases-sqlite.md index a2fc4ea1c..749e325a8 100644 --- a/guides/databases-sqlite.md +++ b/guides/databases-sqlite.md @@ -121,7 +121,7 @@ Finally, configure the DB connection in the non-productive `default` profile: ```yaml --- spring: - profiles: default + config.activate.on-profile: default sql: init: mode: always @@ -206,7 +206,7 @@ Finally, configure the DB connection - ideally in a dedicated `sqlite` profile: ```yaml --- spring: - profiles: sqlite + config.activate.on-profile: sqlite datasource: url: "jdbc:sqlite:sqlite.db" driver-class-name: org.sqlite.JDBC diff --git a/java/persistence-services.md b/java/persistence-services.md index 363d04230..8804c1a0b 100644 --- a/java/persistence-services.md +++ b/java/persistence-services.md @@ -160,7 +160,7 @@ If you don't have a compatible PostgreSQL service binding in your application en ```yaml --- spring: - profiles: postgres + config.activate.on-profile: postgres datasource: url: username: @@ -238,7 +238,7 @@ The database content is stored in a file, `sqlite.db` as in the following exampl ```yaml --- spring: - profiles: sqlite + config.activate.on-profile: sqlite sql: init: mode: never @@ -257,7 +257,7 @@ The database content is stored in-memory only. The schema initialization done by ```yaml --- spring: - profiles: default + config.activate.on-profile: default sql: init: mode: always diff --git a/java/security.md b/java/security.md index f5e5d046b..0dd442a86 100644 --- a/java/security.md +++ b/java/security.md @@ -248,7 +248,7 @@ Define the mock users in a Spring profile, which may be only active during testi ```yaml --- spring: - profiles: test + config.activate.on-profile: test cds: security: mock: @@ -316,7 +316,7 @@ A `tenants` section allows to specify additional configuration for the _mock ten ```yaml --- spring: - profiles: test + config.activate.on-profile: test cds: security: mock: From 8605c90de84f417789d70c024a84e81b7afd4aed Mon Sep 17 00:00:00 2001 From: Daniel Hutzel Date: Tue, 7 Nov 2023 16:33:27 +0100 Subject: [PATCH 10/43] Getting Started cosmetics --- about/features.md | 4 +- get-started/in-a-nutshell.md | 153 +++++++++++++++++++---------------- get-started/jumpstart.md | 10 +-- 3 files changed, 91 insertions(+), 76 deletions(-) diff --git a/about/features.md b/about/features.md index 27d54e70b..a848c28a0 100644 --- a/about/features.md +++ b/about/features.md @@ -39,12 +39,12 @@ Following is an index of the features currently covered by CAP, with status and | CLI commands | | |---------------------------------------------------------------------------------|----------------------------| -| [Jump-start cds-based projects](../get-started/in-a-nutshell#start-a-project) | `cds init ` | +| [Jump-start cds-based projects](../get-started/in-a-nutshell#jumpstart) | `cds init ` | | Add a feature to an existing project | `cds add ` | | [Add models from external sources](../guides/using-services#local-mocking) | `cds import ` | | [Compile cds models to different outputs](../node.js/cds-compile) | `cds compile ` | | [Run your services in local server](../node.js/cds-serve) | `cds serve ` | -| [Run and restart on file changes](../get-started/in-a-nutshell#start-a-project) | `cds watch` | +| [Run and restart on file changes](../get-started/in-a-nutshell#jumpstart) | `cds watch` | | [Read-eval-event loop](../node.js/cds-env#cli) | `cds repl` | | Inspect effective configuration | `cds env` | | Prepare for deployment | `cds build` | diff --git a/get-started/in-a-nutshell.md b/get-started/in-a-nutshell.md index 2bbef7a09..37da80be3 100644 --- a/get-started/in-a-nutshell.md +++ b/get-started/in-a-nutshell.md @@ -10,35 +10,36 @@ impl-variants: true # Getting Started in a Nutshell @@ -56,11 +57,7 @@ This guide is a step-by-step walkthrough to build a CAP application, using a min ## Preliminaries -1. **Prerequisite:** The following steps assume you've installed Node.js, Visual Studio Code, and `@sap/cds-dk` as described in the [_Setup_ section of the _Jumpstart_ guide](jumpstart#setup). - -2. **Hands-On Walkthrough:** The sections below describe a hands-on walkthrough, in which you'd create a new project and fill it with content step by step. - -3. **Option: Download from GitHub** – Instead of going for this hand-on step-by-step experience, you can get the final sample content from GitHub. If you choose to do so clone the repo as follows: +The sections below describe a hands-on walkthrough, in which you'd create a new project and fill it with content step by step. Alternatively, you can get the final sample content from GitHub as follows: ::: code-group @@ -76,14 +73,16 @@ git clone https://github.com/sap-samples/cloud-cap-samples-java bookshop ::: -> Just keep in mind that the sample code on GitHub is an already complete application showcasing a lot of features. So you might find more code in the app than in the code that is created in this step-by-step guide. +> When comparing the code from the *cap/samples* on GitHub to the snippets given in the sections below you will recognise additions showcasing enhanced features. So, what you find in there is a superset of what we describe in this getting started guide. -## Jumpstart a Project { #start-a-project} +## Jumpstart a Project {#jumpstart} -1. Create a new project using `cds init` +**Prerequisite:** Assumed you've installed Node.js, `@sap/cds-dk`, and Visual Studio Code as described in the [_Jumpstart_ guide](jumpstart).... + +2. Create a new project using `cds init` ::: code-group ```sh [Node.js] @@ -94,19 +93,17 @@ git clone https://github.com/sap-samples/cloud-cap-samples-java bookshop ``` ::: -2. Open the project in VS Code +3. Open the project in VS Code ```sh code bookshop ``` ::: details **Note:** VS Code CLI on macOS needs extra setup - - Users on macOS must first run a command (*Shell Command: Install 'code' command in PATH*) to add the VS Code executable to the `PATH` environment variable. Read VS Code's [macOS setup guide](https://code.visualstudio.com/docs/setup/mac) for help. - + In order to start VSCode via the `code` CLI, users on macOS must first run a command (*Shell Command: Install 'code' command in PATH*) to add the VS Code executable to the `PATH` environment variable. Read VS Code's [macOS setup guide](https://code.visualstudio.com/docs/setup/mac) for help. ::: -3. Run `cds watch` in an [*Integrated Terminal*](https://code.visualstudio.com/docs/terminal/basics) +4. Run `cds watch` in an [*Integrated Terminal*](https://code.visualstudio.com/docs/terminal/basics) ::: code-group @@ -124,15 +121,15 @@ git clone https://github.com/sap-samples/cloud-cap-samples-java bookshop ```log [dev] cds w - + cds serve all --with-mocks --in-memory? live reload enabled for browsers - + ___________________________ - + No models found in db/,srv/,app/,schema,services. // [!code focus] Waiting for some to arrive... // [!code focus] - + ``` So, let's go on adding some CDS model as follows... @@ -141,7 +138,7 @@ git clone https://github.com/sap-samples/cloud-cap-samples-java bookshop -## Capture Domain Models { #domain-models } +## Capture Domain Models {#domain-models} Let's feed our project by adding a simple domain model. Start by creating a file named _db/schema.cds_ (also indicated in the code box's label) and copy the following definitions into it: @@ -210,7 +207,7 @@ compilation and reload of the CAP Java application. The embedded database of the ### Compiling Models (Optional) {#cli} -We can also test-compile models individually to check for validity and produce a parsed output in [CSN format](../cds/csn). For example, run this command in a new terminal: +We can optionally test-compile models individually to check for validity and produce a parsed output in [CSN format](../cds/csn). For example, run this command in a new terminal: ```sh cds db/schema.cds @@ -229,7 +226,7 @@ cds db/schema.cds -2 sql -## Declaring Services {#services} +## Providing Services {#services} @@ -283,6 +280,7 @@ service CatalogService @(path:'/browse') { // [!code focus] [Learn more about **Defining Services**.](../guides/providing-services){ .learn-more} + ### Served to OData out of the box
@@ -298,13 +296,6 @@ This time `cds watch` reacted with additional output like this: As you can see, the two service definitions have been compiled and generic service providers have been constructed to serve requests on the listed endpoints _/admin_ and _/browse_. - -Open __ in your browser and see the generic _index.html_ page: - -![Generic welcome page generated by CAP that list all endpoints. Eases jumpstarting development and is not meant for productive use.](assets/in-a-nutshell/welcome.png){style="width:450px; box-shadow: 1px 1px 5px #888888"} {.impl .node} - -> User `alice` is a [default user with admin privileges](../node.js/authentication#mocked). Use it to access the _/admin_ service. You don't need to enter a password. -
@@ -330,16 +321,46 @@ Both services defined above contain security annotations that restrict access to ``` +
+ +::: tip + +CAP-based services are full-fledged OData services out of the box. Without adding any provider implementation code, they translate OData request into corresponding database requests, and return the results as OData responses. + +::: + +You can even use advanced query options, such as `$select`, `$expand`, `$search`, and many more. For example, try out this link: + +- http://localhost:4004/browse/Books?$search=Brontë&$select=title,author&$expand=currency($select=code,name,symbol)&$orderby=title + +[Learn more about **Serving OData Protocol**.](../advanced/odata){.learn-more} + + + +### Generic *index.html* Pages + -Open __ in your browser and see the generic _index.html_ page: +Open __ in your browser and see the generic _index.html_ page: + +
+ +![Generic welcome page generated by CAP that list all endpoints. Eases jumpstarting development and is not meant for productive use.](assets/in-a-nutshell/welcome.png){style="width:450px; box-shadow: 1px 1px 5px #888888"} -![Generic welcome page generated by CAP that list all endpoints. Eases jumpstarting development and is not meant for productive use.](assets/in-a-nutshell/welcome_java.png){style="width:450px; box-shadow: 1px 1px 5px #888888"} +> User `alice` is a [default user with admin privileges](../node.js/authentication#mocked). Use it to access the _/admin_ service. You don't need to enter a password. + +
+ +
+ +Generic welcome page generated by CAP that list all endpoints. Eases jumpstarting development and is not meant for productive use. > User `authenticated` is a [prepared mock user](../java/security#mock-users) which will be authenticated by default. Use it to access the _/admin_ service. You don't need to enter a password.
+ + ### Compiling APIs (Optional) { #repl} You can also compile service definitions explicitly, for example to an [OData model](https://docs.oasis-open.org/odata/odata/v4.0/odata-v4.0-part3-csdl.html): @@ -483,34 +504,28 @@ cds deploy --to hana [Learn more about deploying to SAP HANA.](../guides/databases){.learn-more .impl .node} -## Adding/Serving UIs {#uis} +## Serving UIs {#uis} You can consume the provided services, for example, from UI frontends, using standard AJAX requests. Simply add an _index.html_ file into the _app/_ folder, to replace the generic index page. -### Vue.js UIs {#vue .impl .node} - -For example, you can [find a simple Vue.js app in **cap/samples**](https://github.com/sap-samples/cloud-cap-samples/tree/main/bookshop/app/vue), which demonstrates browsing and ordering books using OData requests to [the `CatalogService` API we defined above](#services). {.impl .node} - -![Shows the famous bookshop catalog service in a simple Vue.js UI.](assets/in-a-nutshell/vue-app.png){style="margin:0" .impl .node .adapt} - - ### SAP Fiori UIs {#fiori} -Besides, being usable from any UI frontends using standard AJAX requests, CAP provides out-of-the-box support for SAP Fiori UIs, for example, with respect to SAP Fiori annotations and advanced features such as search, value helps and SAP Fiori draft. +CAP provides out-of-the-box support for SAP Fiori UIs, for example, with respect to SAP Fiori annotations and advanced features such as search, value helps and SAP Fiori Draft. ![Shows the famous bookshop catalog service in an SAP Fiori UI.](assets/in-a-nutshell/fiori-app.png){.mute-dark} [Learn more about **Serving Fiori UIs**.](../advanced/fiori){.learn-more} -### Using OData Protocol +### Vue.js UIs {#vue .impl .node} + +Besides Fiori UIs, CAP services can be consumed from any UI frontends using standard AJAX requests. +For example, you can [find a simple Vue.js app in **cap/samples**](https://github.com/sap-samples/cloud-cap-samples/tree/main/bookshop/app/vue), which demonstrates browsing and ordering books using OData requests to [the `CatalogService` API we defined above](#services). {.impl .node} -As CAP-based services are full-fledged OData services out of the box, you can use advanced -query options, such as `$select`, `$expand`, `$search`, and many more. +![Shows the famous bookshop catalog service in a simple Vue.js UI.](assets/in-a-nutshell/vue-app.png){style="margin:0" .impl .node .adapt} -[Learn more about **Serving OData Protocol**.](../advanced/odata){.learn-more} ## Adding Custom Logic diff --git a/get-started/jumpstart.md b/get-started/jumpstart.md index 8f0df6a7f..5c9de3f53 100644 --- a/get-started/jumpstart.md +++ b/get-started/jumpstart.md @@ -40,10 +40,12 @@ Choose the **LTS** version, via the left-hand side button: ```sh npm add -g @sap/cds-dk +cds #> run the installed CLI ``` [Running into problems? → See the troubleshooting guide.](troubleshooting#npm-installation){.learn-more} + ### 3. Install Git Run this in a terminal to check whether you already have Git installed: @@ -103,12 +105,10 @@ Then open it in Visual Studio Code: code bookshop ``` -::: details **Note:** VS Code CLI on macOS needs extra setup - -Users on macOS must first run a command (*Shell Command: Install 'code' command in PATH*) to add the VS Code executable to the `PATH` environment variable. Find detailed instructions in [VS Code's macOS setup guide](https://code.visualstudio.com/docs/setup/mac#_launching-from-the-command-line). - -::: + ::: details **Note:** VS Code CLI on macOS needs extra setup + In order to start VSCode via the `code` CLI, users on macOS must first run a command (*Shell Command: Install 'code' command in PATH*) to add the VS Code executable to the `PATH` environment variable. Read VS Code's [macOS setup guide](https://code.visualstudio.com/docs/setup/mac) for help. + ::: ### Project Structure From 13585d900fc92e975ca8249115758fe656bae3ad Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 7 Nov 2023 19:35:02 +0000 Subject: [PATCH 11/43] Update dependency com.sap.cds:cds-services-api to v2.3.1 (#519) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: René Jeglinsky --- .vitepress/config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.vitepress/config.ts b/.vitepress/config.ts index cecff5eee..d310fa46d 100644 --- a/.vitepress/config.ts +++ b/.vitepress/config.ts @@ -21,7 +21,7 @@ if (!siteURL.pathname.endsWith('/')) siteURL.pathname += '/' const redirectLinks: Record = {} const latestVersions = { - java_services: '2.3.0', + java_services: '2.3.1', java_cds4j: '2.3.1' } From aa580954b9f814e04f5b0a28a0d4eed1c75e131d Mon Sep 17 00:00:00 2001 From: Marc Becker Date: Wed, 8 Nov 2023 07:32:47 +0100 Subject: [PATCH 12/43] Explain how to handle /$count requests in CAP Java custom ON handlers (#495) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Explain how to handle /$count requests in CAP Java custom ON handlers Docs for this question on answers.sap.com: https://answers.sap.com/questions/13983437/cap-java-handle-count-query-in-custom-handler.html * Update java/application-services.md Co-authored-by: Markus Ofterdinger * Update java/application-services.md Co-authored-by: René Jeglinsky * Update application-services.md --------- Co-authored-by: Markus Ofterdinger Co-authored-by: René Jeglinsky --- java/application-services.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/java/application-services.md b/java/application-services.md index 87e0d1c9b..440b1fbe8 100644 --- a/java/application-services.md +++ b/java/application-services.md @@ -112,6 +112,19 @@ In case an order item is directly created (for example through a containment nav `READ` event handlers must return the data that was read, either as an `Iterable` or [Result](https://javadoc.io/doc/com.sap.cds/cds4j-api/latest/com/sap/cds/Result.html) object created via the [ResultBuilder](#result-builder-read). For queries with inline count, a `Result` object _must_ be used as the inline count is obtained from the `Result` interface. +`READ` event handlers are also called, for OData `/$count` requests. These requests determine the total amount of entity instances of a specific entity. When handling these requests in a custom `@On` event handler a `Map` with a single key `count` needs to be returned as a result: + +```java +@On(entity = MyEntity_.CDS_NAME) +List> readMyEntity(CdsReadEventContext context) { + if (CqnAnalyzer.isCountQuery(context.getCqn())) { + int count = 100; // determine correct count value + return List.of(Collections.singletonMap("count", count)); + } + // handle non /$count requests +} +``` + ### UPDATE and DELETE Results `UPDATE` and `DELETE` statements have an optional filter condition (where clause) which determines the entities to be updated/deleted. Handlers _must_ return a `Result` object with the number of entities that match this filter condition and have been updated/deleted. Use the [ResultBuilder](#result-builder) to create the `Result` object. From 8c53bcb80c384df90286cde725fc54d98379df6e Mon Sep 17 00:00:00 2001 From: sjvans <30337871+sjvans@users.noreply.github.com> Date: Wed, 8 Nov 2023 07:33:49 +0100 Subject: [PATCH 13/43] improve docs re tx in req listener (#505) * improve docs re tx in req listener * fix snippet type --- node.js/events.md | 11 ++++++++++- plugins/index.md | 2 +- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/node.js/events.md b/node.js/events.md index 76325b26d..ed0c3d13a 100644 --- a/node.js/events.md +++ b/node.js/events.md @@ -206,7 +206,16 @@ The events `succeeded` , `failed` and `done` are emitted *after* the current tra To veto requests, either use the `req.before('commit')` hook, or service-level `before` `COMMIT` handlers. -To do something which requires databases in `succeeded`/`failed` handlers, use `cds.spawn()`, or one of the other options of [manually-managed transactions](./cds-tx). +To do something which requires databases in `succeeded`/`failed` handlers, use `cds.spawn()`, or one of the other options of [manual transactions](./cds-tx#manual-transactions), preferably a variant with automatic commit/ rollback. + +Example: +```js +req.on('done', () => { + await cds.tx(() => { + await UPDATE `Stats` .set `views = views + 1` .where `book_ID = ${book.ID}` + }) +}) +``` Additional note about OData: For requests that are part of a changeset, the events are emitted once the entire changeset was completed. If at least one of the requests in the changeset fails, following the atomicity property ("all or nothing"), all requests fail. diff --git a/plugins/index.md b/plugins/index.md index f9282cde4..41c665dd9 100644 --- a/plugins/index.md +++ b/plugins/index.md @@ -191,4 +191,4 @@ Click on the icon to get detailed instructions. {.learn-more}
-
\ No newline at end of file +
From 7eb5530409fa0423439a3dac495db98eaa5f4e1e Mon Sep 17 00:00:00 2001 From: Christian Georgi Date: Wed, 8 Nov 2023 16:07:25 +0100 Subject: [PATCH 14/43] Anchor ID for complicated title --- get-started/troubleshooting.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/get-started/troubleshooting.md b/get-started/troubleshooting.md index 8f9ae6acc..a5e018145 100644 --- a/get-started/troubleshooting.md +++ b/get-started/troubleshooting.md @@ -411,7 +411,7 @@ You can disable the database clustering for the update. | _Root Cause_ | Your configuration isn't properly set. | | _Solution_ | Configure your project as described in [Using Databases](../guides/databases). -#### Deployment fails — _Connection failed (RTE:[89008] Socket closed by peer_ +#### Deployment fails — _Connection failed (RTE:[89008] Socket closed by peer_ {#connection-failed-89008} | | Explanation | | --- | ---- | @@ -420,7 +420,7 @@ You can disable the database clustering for the update.
-#### Deployment fails — _Connection failed (RTE:[89013] Socket closed by peer_ +#### Deployment fails — _Connection failed (RTE:[89013] Socket closed by peer_ {#connection-failed-89013} | | Explanation | | --- | ---- | From 18b7de5f820e279d265167fb74f5a923634cf00d Mon Sep 17 00:00:00 2001 From: Christian Georgi Date: Wed, 8 Nov 2023 16:16:07 +0100 Subject: [PATCH 15/43] Fix link --- advanced/hybrid-testing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/advanced/hybrid-testing.md b/advanced/hybrid-testing.md index 8804c9888..2f359dea3 100644 --- a/advanced/hybrid-testing.md +++ b/advanced/hybrid-testing.md @@ -28,7 +28,7 @@ cds bind -2 my-hana:my-hana-key Binds your local CAP application to the service key `my-hana-key` of the service instance `my-hana`, using your currently targeted Cloud Foundry space. The service instance `my-hana` is a _managed_ service. cds bind also supports Cloud Foundry _user-provided_ services. -[Got errors? See our troubleshooting for connection issues with SAP HANA Cloud.](../get-started/troubleshooting#deployment-fails-—-connection-failed-rte-89008-socket-closed-by-peer){.learn-more} +[Got errors? See our troubleshooting for connection issues with SAP HANA Cloud.](../get-started/troubleshooting#connection-failed-89008){.learn-more} [Learn how to bind to user-provided services on Cloud Foundry.](#binding-user-provided-services){.learn-more} Output: From 2fd74e87a02fb3a64ed15e7751a71e7319d69174 Mon Sep 17 00:00:00 2001 From: Christian Georgi Date: Wed, 8 Nov 2023 16:28:06 +0100 Subject: [PATCH 16/43] Bump to Vitepress RC 25 --- package-lock.json | 762 +++++++++++++++++++++++++--------------------- package.json | 4 +- 2 files changed, 410 insertions(+), 356 deletions(-) diff --git a/package-lock.json b/package-lock.json index 998c6a07d..c016b1c91 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@cap-js/docs", - "version": "0.20.0", + "version": "0.25.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@cap-js/docs", - "version": "0.20.0", + "version": "0.25.0", "license": "SEE LICENSE IN LICENSE", "devDependencies": { "@types/adm-zip": ">=0.5.0", @@ -19,7 +19,7 @@ "markdownlint-cli": ">=0.35.0", "markdownlint-rule-search-replace": "^1.1.1", "sass": "^1.62.1", - "vitepress": "^1.0.0-rc.20" + "vitepress": "^1.0.0-rc.25" } }, "node_modules/@aashutoshrathi/word-wrap": { @@ -324,54 +324,55 @@ } }, "node_modules/@cspell/cspell-bundled-dicts": { - "version": "7.3.7", - "resolved": "https://registry.npmjs.org/@cspell/cspell-bundled-dicts/-/cspell-bundled-dicts-7.3.7.tgz", - "integrity": "sha512-Mw7J0RAWGpEup/+eIePw3wi+OlMGNicrD1r9OhdgIgO6sHEi01ibS/RzNNbC7UziLaYEHi8+WfLyGzmp1ZISrQ==", + "version": "7.3.9", + "resolved": "https://registry.npmjs.org/@cspell/cspell-bundled-dicts/-/cspell-bundled-dicts-7.3.9.tgz", + "integrity": "sha512-ebfrf5Zaw33bcqT80Qrkv7IGT7GI/CDp15bSk2EUmdORzk1SCKZl6L4vUo3NLMmxVwYioS+OQmsW8E88sJNyGg==", "dev": true, "dependencies": { "@cspell/dict-ada": "^4.0.2", "@cspell/dict-aws": "^4.0.0", "@cspell/dict-bash": "^4.1.2", - "@cspell/dict-companies": "^3.0.24", - "@cspell/dict-cpp": "^5.0.5", + "@cspell/dict-companies": "^3.0.27", + "@cspell/dict-cpp": "^5.0.9", "@cspell/dict-cryptocurrencies": "^4.0.0", "@cspell/dict-csharp": "^4.0.2", - "@cspell/dict-css": "^4.0.10", + "@cspell/dict-css": "^4.0.12", "@cspell/dict-dart": "^2.0.3", "@cspell/dict-django": "^4.1.0", "@cspell/dict-docker": "^1.1.7", "@cspell/dict-dotnet": "^5.0.0", "@cspell/dict-elixir": "^4.0.3", - "@cspell/dict-en_us": "^4.3.8", + "@cspell/dict-en_us": "^4.3.11", "@cspell/dict-en-common-misspellings": "^1.0.2", "@cspell/dict-en-gb": "1.1.33", - "@cspell/dict-filetypes": "^3.0.1", + "@cspell/dict-filetypes": "^3.0.2", "@cspell/dict-fonts": "^4.0.0", - "@cspell/dict-fsharp": "^1.0.0", + "@cspell/dict-fsharp": "^1.0.1", "@cspell/dict-fullstack": "^3.1.5", "@cspell/dict-gaming-terms": "^1.0.4", "@cspell/dict-git": "^2.0.0", - "@cspell/dict-golang": "^6.0.3", + "@cspell/dict-golang": "^6.0.4", "@cspell/dict-haskell": "^4.0.1", "@cspell/dict-html": "^4.0.5", "@cspell/dict-html-symbol-entities": "^4.0.0", "@cspell/dict-java": "^5.0.6", - "@cspell/dict-k8s": "^1.0.1", + "@cspell/dict-k8s": "^1.0.2", "@cspell/dict-latex": "^4.0.0", "@cspell/dict-lorem-ipsum": "^4.0.0", - "@cspell/dict-lua": "^4.0.1", + "@cspell/dict-lua": "^4.0.2", + "@cspell/dict-makefile": "^1.0.0", "@cspell/dict-node": "^4.0.3", - "@cspell/dict-npm": "^5.0.10", - "@cspell/dict-php": "^4.0.3", + "@cspell/dict-npm": "^5.0.12", + "@cspell/dict-php": "^4.0.4", "@cspell/dict-powershell": "^5.0.2", - "@cspell/dict-public-licenses": "^2.0.4", - "@cspell/dict-python": "^4.1.9", + "@cspell/dict-public-licenses": "^2.0.5", + "@cspell/dict-python": "^4.1.10", "@cspell/dict-r": "^2.0.1", - "@cspell/dict-ruby": "^5.0.0", + "@cspell/dict-ruby": "^5.0.1", "@cspell/dict-rust": "^4.0.1", "@cspell/dict-scala": "^5.0.0", - "@cspell/dict-software-terms": "^3.3.2", - "@cspell/dict-sql": "^2.1.1", + "@cspell/dict-software-terms": "^3.3.9", + "@cspell/dict-sql": "^2.1.2", "@cspell/dict-svelte": "^1.0.2", "@cspell/dict-swift": "^2.0.1", "@cspell/dict-typescript": "^3.1.2", @@ -382,30 +383,30 @@ } }, "node_modules/@cspell/cspell-json-reporter": { - "version": "7.3.7", - "resolved": "https://registry.npmjs.org/@cspell/cspell-json-reporter/-/cspell-json-reporter-7.3.7.tgz", - "integrity": "sha512-bogUQKKZWLttZtxFKjpzHuliIha/ByV2km18gm8dA2uB3IrzD1UJy4sCE8lnaodm6n3VtjnViSkQ5XIVU3gAKQ==", + "version": "7.3.9", + "resolved": "https://registry.npmjs.org/@cspell/cspell-json-reporter/-/cspell-json-reporter-7.3.9.tgz", + "integrity": "sha512-QHsem5OZXshFX+Wdlx3VpdPi9WS7KgoBMGGJ4zQZ3lp81Rb1tRj0Ij/98whq882QOmAVQfr+uOHANHLnyPr0LQ==", "dev": true, "dependencies": { - "@cspell/cspell-types": "7.3.7" + "@cspell/cspell-types": "7.3.9" }, "engines": { "node": ">=16" } }, "node_modules/@cspell/cspell-pipe": { - "version": "7.3.7", - "resolved": "https://registry.npmjs.org/@cspell/cspell-pipe/-/cspell-pipe-7.3.7.tgz", - "integrity": "sha512-ZO8v3EwGhjUvhPo1S48+CKv7EPXMoYF7LGERB34K8EXFByb9+J74ojMYj9UgLRV68lFTrDFde3bHoZPPVS1FsA==", + "version": "7.3.9", + "resolved": "https://registry.npmjs.org/@cspell/cspell-pipe/-/cspell-pipe-7.3.9.tgz", + "integrity": "sha512-gKYTHcryKOaTmr6t+M5h1sZnQ42eHeumBJejovphipXfdivedUnuYyQrrQGFAlUKzfEOWcOPME1nm17xsaX5Ww==", "dev": true, "engines": { "node": ">=16" } }, "node_modules/@cspell/cspell-resolver": { - "version": "7.3.7", - "resolved": "https://registry.npmjs.org/@cspell/cspell-resolver/-/cspell-resolver-7.3.7.tgz", - "integrity": "sha512-WWZcTI5f2cCjr1yRDTMkcVg7Meil3s+0aaKcLCDTGQf9J2UWWjpqDJ6M6keYei3paAjxW2Pk03IRNNwdA3+igQ==", + "version": "7.3.9", + "resolved": "https://registry.npmjs.org/@cspell/cspell-resolver/-/cspell-resolver-7.3.9.tgz", + "integrity": "sha512-2slYAGvi7EFLKyJ5hrYBNaFT2iyOEQM1pEIzm+PDuhNJE/9wuBY5pBVqIgFSPz53vsQvW9GJThNY8h1/2EH3ZA==", "dev": true, "dependencies": { "global-dirs": "^3.0.1" @@ -415,18 +416,18 @@ } }, "node_modules/@cspell/cspell-service-bus": { - "version": "7.3.7", - "resolved": "https://registry.npmjs.org/@cspell/cspell-service-bus/-/cspell-service-bus-7.3.7.tgz", - "integrity": "sha512-pnDOFpjht7dZYydMygcf0brCSk5BGRvbeWRH6MaMhd+3CdyzyEvtZG3IbBQVNyVvDTA2c/K3rljOAo8y3/lpnw==", + "version": "7.3.9", + "resolved": "https://registry.npmjs.org/@cspell/cspell-service-bus/-/cspell-service-bus-7.3.9.tgz", + "integrity": "sha512-VyfK3qWtJZag4Fe/x1Oh/tqCNVGKGlQ2ArX1fVdmTVGQtZcbXuMKdZI80t4b8SGtzGINHufAdakpu3xucX/FrQ==", "dev": true, "engines": { "node": ">=16" } }, "node_modules/@cspell/cspell-types": { - "version": "7.3.7", - "resolved": "https://registry.npmjs.org/@cspell/cspell-types/-/cspell-types-7.3.7.tgz", - "integrity": "sha512-zM2BuZJ3UUgPwF78bssggi8X20nmW3a95EmbNJKfbO6Zf2ui7UMzeP3BwpCZk30A/EixGlFhLf6Xd+eBT/DQqw==", + "version": "7.3.9", + "resolved": "https://registry.npmjs.org/@cspell/cspell-types/-/cspell-types-7.3.9.tgz", + "integrity": "sha512-p7s8yEV6ASz0HjiArH11yjNj3vXzK2Ep94GrpdtYJxSxFC2w1mXAVUaJB/5+jC4+1YeYsmcBFTXmZ1rGMyTv3g==", "dev": true, "engines": { "node": ">=16" @@ -451,15 +452,15 @@ "dev": true }, "node_modules/@cspell/dict-companies": { - "version": "3.0.26", - "resolved": "https://registry.npmjs.org/@cspell/dict-companies/-/dict-companies-3.0.26.tgz", - "integrity": "sha512-BGRZ/Uykx+IgQoTGqvRqbBMQy7QSuY0pbTHgtmKtc1scgzZMJQKMDwyuE6LJzlhdlrV7TsVY0lyXREybnDpQPQ==", + "version": "3.0.27", + "resolved": "https://registry.npmjs.org/@cspell/dict-companies/-/dict-companies-3.0.27.tgz", + "integrity": "sha512-gaPR/luf+4oKGyxvW4GbxGGPdHiC5kj/QefnmQqrLFrLiCSXMZg5/NL+Lr4E5lcHsd35meX61svITQAvsT7lyQ==", "dev": true }, "node_modules/@cspell/dict-cpp": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/@cspell/dict-cpp/-/dict-cpp-5.0.6.tgz", - "integrity": "sha512-2z9JqWgsRYROnqeMj1k1L1taSQQHMhqfU6EqDNApsEQT3naznKntV8KKyybr2YSz0bIG9fMbzVv0GoQBbLgD9A==", + "version": "5.0.9", + "resolved": "https://registry.npmjs.org/@cspell/dict-cpp/-/dict-cpp-5.0.9.tgz", + "integrity": "sha512-ql9WPNp8c+fhdpVpjpZEUWmxBHJXs9CJuiVVfW/iwv5AX7VuMHyEwid+9/6nA8qnCxkUQ5pW83Ums1lLjn8ScA==", "dev": true }, "node_modules/@cspell/dict-cryptocurrencies": { @@ -517,9 +518,9 @@ "dev": true }, "node_modules/@cspell/dict-en_us": { - "version": "4.3.8", - "resolved": "https://registry.npmjs.org/@cspell/dict-en_us/-/dict-en_us-4.3.8.tgz", - "integrity": "sha512-rCPsbDHuRnFUbzWAY6O1H9+cLZt5FNQwjPVw2TdQZfipdb0lim984aLGY+nupi1iKC3lfjyd5SVUgmSZEG1QNA==", + "version": "4.3.11", + "resolved": "https://registry.npmjs.org/@cspell/dict-en_us/-/dict-en_us-4.3.11.tgz", + "integrity": "sha512-GhdavZFlS2YbUNcRtPbgJ9j6aUyq116LmDQ2/Q5SpQxJ5/6vVs8Yj5WxV1JD+Zh/Zim1NJDcneTOuLsUGi+Czw==", "dev": true }, "node_modules/@cspell/dict-en-common-misspellings": { @@ -535,9 +536,9 @@ "dev": true }, "node_modules/@cspell/dict-filetypes": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@cspell/dict-filetypes/-/dict-filetypes-3.0.1.tgz", - "integrity": "sha512-8z8mY1IbrTyTRumx2vvD9yzRhNMk9SajM/GtI5hdMM2pPpNSp25bnuauzjRf300eqlqPY2MNb5MmhBFO014DJw==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@cspell/dict-filetypes/-/dict-filetypes-3.0.2.tgz", + "integrity": "sha512-StoC0wPmFNav6F6P8/FYFN1BpZfPgOmktb8gQ9wTauelWofPeBW+A0t5ncZt9hXHtnbGDA98v4ukacV+ucbnUg==", "dev": true }, "node_modules/@cspell/dict-fonts": { @@ -547,9 +548,9 @@ "dev": true }, "node_modules/@cspell/dict-fsharp": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@cspell/dict-fsharp/-/dict-fsharp-1.0.0.tgz", - "integrity": "sha512-dHPkMHwW4dWv3Lv9VWxHuVm4IylqvcfRBSnZ7usJTRThraetSVrOPIJwr6UJh7F5un/lGJx2lxWVApf2WQaB/A==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@cspell/dict-fsharp/-/dict-fsharp-1.0.1.tgz", + "integrity": "sha512-23xyPcD+j+NnqOjRHgW3IU7Li912SX9wmeefcY0QxukbAxJ/vAN4rBpjSwwYZeQPAn3fxdfdNZs03fg+UM+4yQ==", "dev": true }, "node_modules/@cspell/dict-fullstack": { @@ -571,9 +572,9 @@ "dev": true }, "node_modules/@cspell/dict-golang": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/@cspell/dict-golang/-/dict-golang-6.0.3.tgz", - "integrity": "sha512-KiNnjAeqDBq6zH4s46hzBrKgqIrkSZ9bbHzQ54PbHfe+jurZkSZ4lXz6E+315RNh2TkRLcNppFvaZqJvKZXomA==", + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/@cspell/dict-golang/-/dict-golang-6.0.4.tgz", + "integrity": "sha512-jOfewPEyN6U9Q80okE3b1PTYBfqZgHh7w4o271GSuAX+VKJ1lUDhdR4bPKRxSDdO5jHArw2u5C8nH2CWGuygbQ==", "dev": true }, "node_modules/@cspell/dict-haskell": { @@ -601,9 +602,9 @@ "dev": true }, "node_modules/@cspell/dict-k8s": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@cspell/dict-k8s/-/dict-k8s-1.0.1.tgz", - "integrity": "sha512-gc5y4Nm3hVdMZNBZfU2M1AsAmObZsRWjCUk01NFPfGhFBXyVne41T7E62rpnzu5330FV/6b/TnFcPgRmak9lLw==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@cspell/dict-k8s/-/dict-k8s-1.0.2.tgz", + "integrity": "sha512-tLT7gZpNPnGa+IIFvK9SP1LrSpPpJ94a/DulzAPOb1Q2UBFwdpFd82UWhio0RNShduvKG/WiMZf/wGl98pn+VQ==", "dev": true }, "node_modules/@cspell/dict-latex": { @@ -624,6 +625,12 @@ "integrity": "sha512-eeC20Q+UnHcTVBK6pgwhSjGIVugO2XqU7hv4ZfXp2F9DxGx1RME0+1sKX4qAGhdFGwOBsEzb2fwUsAEP6Mibpg==", "dev": true }, + "node_modules/@cspell/dict-makefile": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@cspell/dict-makefile/-/dict-makefile-1.0.0.tgz", + "integrity": "sha512-3W9tHPcSbJa6s0bcqWo6VisEDTSN5zOtDbnPabF7rbyjRpNo0uHXHRJQF8gAbFzoTzBBhgkTmrfSiuyQm7vBUQ==", + "dev": true + }, "node_modules/@cspell/dict-node": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/@cspell/dict-node/-/dict-node-4.0.3.tgz", @@ -637,9 +644,9 @@ "dev": true }, "node_modules/@cspell/dict-php": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/@cspell/dict-php/-/dict-php-4.0.3.tgz", - "integrity": "sha512-PxtSmWJCDEB4M8R9ER9ijxBum/tvUqYT26QeuV58q2IFs5IrPZ6hocQKvnFGXItjCWH4oYXyAEAAzINlBC4Opg==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@cspell/dict-php/-/dict-php-4.0.4.tgz", + "integrity": "sha512-fRlLV730fJbulDsLIouZxXoxHt3KIH6hcLFwxaupHL+iTXDg0lo7neRpbqD5MScr/J3idEr7i9G8XWzIikKFug==", "dev": true }, "node_modules/@cspell/dict-powershell": { @@ -649,15 +656,15 @@ "dev": true }, "node_modules/@cspell/dict-public-licenses": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@cspell/dict-public-licenses/-/dict-public-licenses-2.0.4.tgz", - "integrity": "sha512-KjsfuGwMWvPkp6s0nR+s4mZc9SQhh1tHDOyQZfEVRwi+2ev7f8l7R6ts9sP2Mplb8UcxwO6YmKwxHjN+XHoMoA==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@cspell/dict-public-licenses/-/dict-public-licenses-2.0.5.tgz", + "integrity": "sha512-91HK4dSRri/HqzAypHgduRMarJAleOX5NugoI8SjDLPzWYkwZ1ftuCXSk+fy8DLc3wK7iOaFcZAvbjmnLhVs4A==", "dev": true }, "node_modules/@cspell/dict-python": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@cspell/dict-python/-/dict-python-4.1.9.tgz", - "integrity": "sha512-JMA4v/ZPJWuDt3PPFz+23VIY3iDIB+xOTQ6nw+WkcJU5yr6FUl5zMU9ModKrgujg3jGRuuJqofErZVPqHNHYAA==", + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@cspell/dict-python/-/dict-python-4.1.10.tgz", + "integrity": "sha512-ErF/Ohcu6Xk4QVNzFgo8p7CxkxvAKAmFszvso41qOOhu8CVpB35ikBRpGVDw9gsCUtZzi15Yl0izi4do6WcLkA==", "dev": true, "dependencies": { "@cspell/dict-data-science": "^1.0.11" @@ -688,15 +695,15 @@ "dev": true }, "node_modules/@cspell/dict-software-terms": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/@cspell/dict-software-terms/-/dict-software-terms-3.3.4.tgz", - "integrity": "sha512-+WpBcJmhPl+jZEEGqgeiyDeJuCJ/M2TuVPaHJJI83LQLvLf1z4/5dkHXU7fUkimpxXFJWWR1DlWLA3+PKBeTfg==", + "version": "3.3.9", + "resolved": "https://registry.npmjs.org/@cspell/dict-software-terms/-/dict-software-terms-3.3.9.tgz", + "integrity": "sha512-/O3EWe0SIznx18S7J3GAXPDe7sexn3uTsf4IlnGYK9WY6ZRuEywkXCB+5/USLTGf4+QC05pkHofphdvVSifDyA==", "dev": true }, "node_modules/@cspell/dict-sql": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@cspell/dict-sql/-/dict-sql-2.1.1.tgz", - "integrity": "sha512-v1mswi9NF40+UDUMuI148YQPEQvWjac72P6ZsjlRdLjEiQEEMEsTQ+zlkIdnzC9QCNyJaqD5Liq9Mn78/8Zxtw==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@cspell/dict-sql/-/dict-sql-2.1.2.tgz", + "integrity": "sha512-Pi0hAcvsSGtZZeyyAN1VfGtQJbrXos5x2QjJU0niAQKhmITSOrXU/1II1Gogk+FYDjWyV9wP2De0U2f7EWs6oQ==", "dev": true }, "node_modules/@cspell/dict-svelte": { @@ -724,21 +731,21 @@ "dev": true }, "node_modules/@cspell/dynamic-import": { - "version": "7.3.7", - "resolved": "https://registry.npmjs.org/@cspell/dynamic-import/-/dynamic-import-7.3.7.tgz", - "integrity": "sha512-ac52OLDMYBHkRQ8XzihOWnyfqri3M84ELTZdqBhR5YGcHW/mxKhsmXqudA980SdRRKaicD39yhX4idAFb4AsDg==", + "version": "7.3.9", + "resolved": "https://registry.npmjs.org/@cspell/dynamic-import/-/dynamic-import-7.3.9.tgz", + "integrity": "sha512-P6tAmDVhrW03hmhetxhBKlNTYwL2lk8ZehYQwSpXaLnaFrS3xrQvfUaJ3Mj9W2CIMzSYXlLmPO2FLRhXK2dnEw==", "dev": true, "dependencies": { - "import-meta-resolve": "^3.0.0" + "import-meta-resolve": "^3.1.1" }, "engines": { "node": ">=16" } }, "node_modules/@cspell/strong-weak-map": { - "version": "7.3.7", - "resolved": "https://registry.npmjs.org/@cspell/strong-weak-map/-/strong-weak-map-7.3.7.tgz", - "integrity": "sha512-n+jRgwH0wU+HsfqgCGVzPmWnZl4SyhtvPxusKwXj6L/STGdt8IP2rYl1PFOtyvgjPjh8xXe/jRrq7zH07btiKA==", + "version": "7.3.9", + "resolved": "https://registry.npmjs.org/@cspell/strong-weak-map/-/strong-weak-map-7.3.9.tgz", + "integrity": "sha512-XKpw/p3+EN+PWiFAWc45RJPI9zQRkPSVdUFeZb0YLseWF/CkogScgIe4CLfMLITiVbP0X/FKk90+aTPfAU38kg==", "dev": true, "engines": { "node": ">=16" @@ -1160,18 +1167,18 @@ } }, "node_modules/@eslint-community/regexpp": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.9.1.tgz", - "integrity": "sha512-Y27x+MBLjXa+0JWDhykM3+JE+il3kHKAEqabfEWq3SDhZjLYb6/BHL/JKFnH3fe207JaXkyDo685Oc2Glt6ifA==", + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", + "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", "dev": true, "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, "node_modules/@eslint/eslintrc": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.2.tgz", - "integrity": "sha512-+wvgpDsrB1YqAMdEUCcnTlpfVBH7Vqn6A/NT3D8WVXFIaKMlErPIZT3oCIAVCOtarRpMtelZLqJeU3t7WY6X6g==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.3.tgz", + "integrity": "sha512-yZzuIG+jnVu6hNSzFEN07e8BxF3uAzYtQb6uDkaYZLo6oYZDCq454c5kB8zxnzfCYyP4MIuyBn10L0DqwujTmA==", "dev": true, "dependencies": { "ajv": "^6.12.4", @@ -1192,21 +1199,21 @@ } }, "node_modules/@eslint/js": { - "version": "8.51.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.51.0.tgz", - "integrity": "sha512-HxjQ8Qn+4SI3/AFv6sOrDB+g6PpUTDwSJiQqOrnneEk8L71161srI9gjzzZvYVbzHiVg/BvcH95+cK/zfIt4pg==", + "version": "8.53.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.53.0.tgz", + "integrity": "sha512-Kn7K8dx/5U6+cT1yEhpX1w4PCSg0M+XyRILPgvwcEBjerFWCwQj5sbr3/VmxqV0JGHCBCzyd6LxypEuehypY1w==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, "node_modules/@humanwhocodes/config-array": { - "version": "0.11.11", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.11.tgz", - "integrity": "sha512-N2brEuAadi0CcdeMXUkhbZB84eskAc8MEX1By6qEchoVywSgXPIjou4rYsl0V3Hj0ZnuGycGCjdNgockbzeWNA==", + "version": "0.11.13", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz", + "integrity": "sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==", "dev": true, "dependencies": { - "@humanwhocodes/object-schema": "^1.2.1", + "@humanwhocodes/object-schema": "^2.0.1", "debug": "^4.1.1", "minimatch": "^3.0.5" }, @@ -1228,9 +1235,9 @@ } }, "node_modules/@humanwhocodes/object-schema": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", - "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.1.tgz", + "integrity": "sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==", "dev": true }, "node_modules/@isaacs/cliui": { @@ -1302,24 +1309,24 @@ } }, "node_modules/@types/adm-zip": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/@types/adm-zip/-/adm-zip-0.5.2.tgz", - "integrity": "sha512-33OTTnnW3onOE6HJuoqsi7T7Ojupz7zO/Vs5ddRNVCYQnu4lg05RqH/pr9eidHGvGyYfdO4uPO9cvegAMixBCQ==", + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/@types/adm-zip/-/adm-zip-0.5.4.tgz", + "integrity": "sha512-/pYie/76O0TTqU4L/z1XqQ5mA5QvScaP/EO3lpH7iQ6/AjioYyuvi2IAmQeimuTTnytl03e9g8YFYkGV2Bxojw==", "dev": true, "dependencies": { "@types/node": "*" } }, "node_modules/@types/linkify-it": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-3.0.3.tgz", - "integrity": "sha512-pTjcqY9E4nOI55Wgpz7eiI8+LzdYnw3qxXCfHyBDdPbYvbyLgWLJGh8EdPvqawwMK1Uo1794AUkkR38Fr0g+2g==", + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-3.0.5.tgz", + "integrity": "sha512-yg6E+u0/+Zjva+buc3EIb+29XEg4wltq7cSmd4Uc2EE/1nUVmxyzpX6gUXD0V8jIrG0r7YeOGVIbYRkxeooCtw==", "dev": true }, "node_modules/@types/markdown-it": { - "version": "13.0.2", - "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-13.0.2.tgz", - "integrity": "sha512-Tla7hH9oeXHOlJyBFdoqV61xWE9FZf/y2g+gFVwQ2vE1/eBzjUno5JCd3Hdb5oATve5OF6xNjZ/4VIZhVVx+hA==", + "version": "13.0.6", + "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-13.0.6.tgz", + "integrity": "sha512-0VqpvusJn1/lwRegCxcHVdmLfF+wIsprsKMC9xW8UPcTxhFcQtoN/fBU1zMe8pH7D/RuueMh2CaBaNv+GrLqTw==", "dev": true, "dependencies": { "@types/linkify-it": "*", @@ -1327,33 +1334,36 @@ } }, "node_modules/@types/mdurl": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-1.0.3.tgz", - "integrity": "sha512-T5k6kTXak79gwmIOaDF2UUQXFbnBE0zBUzF20pz7wDYu0RQMzWg+Ml/Pz50214NsFHBITkoi5VtdjFZnJ2ijjA==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-1.0.5.tgz", + "integrity": "sha512-6L6VymKTzYSrEf4Nev4Xa1LCHKrlTlYCBMTlQKFuddo1CvQcE52I0mwfOJayueUC7MJuXOeHTcIU683lzd0cUA==", "dev": true }, "node_modules/@types/node": { - "version": "20.8.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.8.3.tgz", - "integrity": "sha512-jxiZQFpb+NlH5kjW49vXxvxTjeeqlbsnTAdBTKpzEdPs9itay7MscYXz3Fo9VYFEsfQ6LJFitHad3faerLAjCw==", - "dev": true + "version": "20.9.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.9.0.tgz", + "integrity": "sha512-nekiGu2NDb1BcVofVcEKMIwzlx4NjHlcjhoxxKBNLtz15Y1z7MYf549DFvkHSId02Ax6kGwWntIBPC3l/JZcmw==", + "dev": true, + "dependencies": { + "undici-types": "~5.26.4" + } }, "node_modules/@types/web-bluetooth": { - "version": "0.0.17", - "resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.17.tgz", - "integrity": "sha512-4p9vcSmxAayx72yn70joFoL44c9MO/0+iVEBIQXe3v2h2SiAsEIo/G5v6ObFWvNKRFjbrVadNf9LqEEZeQPzdA==", + "version": "0.0.18", + "resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.18.tgz", + "integrity": "sha512-v/ZHEj9xh82usl8LMR3GarzFY1IrbXJw5L4QfQhokjRV91q+SelFqxQWSep1ucXEZ22+dSTwLFkXeur25sPIbw==", "dev": true }, "node_modules/@typescript-eslint/parser": { - "version": "6.7.4", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.7.4.tgz", - "integrity": "sha512-I5zVZFY+cw4IMZUeNCU7Sh2PO5O57F7Lr0uyhgCJmhN/BuTlnc55KxPonR4+EM3GBdfiCyGZye6DgMjtubQkmA==", + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.10.0.tgz", + "integrity": "sha512-+sZwIj+s+io9ozSxIWbNB5873OSdfeBEH/FR0re14WLI6BaKuSOnnwCJ2foUiu8uXf4dRp1UqHP0vrZ1zXGrog==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "6.7.4", - "@typescript-eslint/types": "6.7.4", - "@typescript-eslint/typescript-estree": "6.7.4", - "@typescript-eslint/visitor-keys": "6.7.4", + "@typescript-eslint/scope-manager": "6.10.0", + "@typescript-eslint/types": "6.10.0", + "@typescript-eslint/typescript-estree": "6.10.0", + "@typescript-eslint/visitor-keys": "6.10.0", "debug": "^4.3.4" }, "engines": { @@ -1373,13 +1383,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "6.7.4", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.7.4.tgz", - "integrity": "sha512-SdGqSLUPTXAXi7c3Ob7peAGVnmMoGzZ361VswK2Mqf8UOYcODiYvs8rs5ILqEdfvX1lE7wEZbLyELCW+Yrql1A==", + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.10.0.tgz", + "integrity": "sha512-TN/plV7dzqqC2iPNf1KrxozDgZs53Gfgg5ZHyw8erd6jd5Ta/JIEcdCheXFt9b1NYb93a1wmIIVW/2gLkombDg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.7.4", - "@typescript-eslint/visitor-keys": "6.7.4" + "@typescript-eslint/types": "6.10.0", + "@typescript-eslint/visitor-keys": "6.10.0" }, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -1390,9 +1400,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "6.7.4", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.7.4.tgz", - "integrity": "sha512-o9XWK2FLW6eSS/0r/tgjAGsYasLAnOWg7hvZ/dGYSSNjCh+49k5ocPN8OmG5aZcSJ8pclSOyVKP2x03Sj+RrCA==", + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.10.0.tgz", + "integrity": "sha512-36Fq1PWh9dusgo3vH7qmQAj5/AZqARky1Wi6WpINxB6SkQdY5vQoT2/7rW7uBIsPDcvvGCLi4r10p0OJ7ITAeg==", "dev": true, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -1403,13 +1413,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "6.7.4", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.7.4.tgz", - "integrity": "sha512-ty8b5qHKatlNYd9vmpHooQz3Vki3gG+3PchmtsA4TgrZBKWHNjWfkQid7K7xQogBqqc7/BhGazxMD5vr6Ha+iQ==", + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.10.0.tgz", + "integrity": "sha512-ek0Eyuy6P15LJVeghbWhSrBCj/vJpPXXR+EpaRZqou7achUWL8IdYnMSC5WHAeTWswYQuP2hAZgij/bC9fanBg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.7.4", - "@typescript-eslint/visitor-keys": "6.7.4", + "@typescript-eslint/types": "6.10.0", + "@typescript-eslint/visitor-keys": "6.10.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -1430,12 +1440,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "6.7.4", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.7.4.tgz", - "integrity": "sha512-pOW37DUhlTZbvph50x5zZCkFn3xzwkGtNoJHzIM3svpiSkJzwOYr/kVBaXmf+RAQiUDs1AHEZVNPg6UJCJpwRA==", + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.10.0.tgz", + "integrity": "sha512-xMGluxQIEtOM7bqFCo+rCMh5fqI+ZxV5RUUOa29iVPz1OgCZrtc7rFnz5cLUazlkPKYqX+75iuDq7m0HQ48nCg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.7.4", + "@typescript-eslint/types": "6.10.0", "eslint-visitor-keys": "^3.4.1" }, "engines": { @@ -1446,134 +1456,153 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@ungap/structured-clone": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", + "dev": true + }, + "node_modules/@vitejs/plugin-vue": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-4.3.1.tgz", + "integrity": "sha512-tUBEtWcF7wFtII7ayNiLNDTCE1X1afySEo+XNVMNkFXaThENyCowIEX095QqbJZGTgoOcSVDJGlnde2NG4jtbQ==", + "dev": true, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "peerDependencies": { + "vite": "^4.0.0", + "vue": "^3.2.25" + } + }, "node_modules/@vue/compiler-core": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.3.4.tgz", - "integrity": "sha512-cquyDNvZ6jTbf/+x+AgM2Arrp6G4Dzbb0R64jiG804HRMfRiFXWI6kqUVqZ6ZR0bQhIoQjB4+2bhNtVwndW15g==", + "version": "3.3.8", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.3.8.tgz", + "integrity": "sha512-hN/NNBUECw8SusQvDSqqcVv6gWq8L6iAktUR0UF3vGu2OhzRqcOiAno0FmBJWwxhYEXRlQJT5XnoKsVq1WZx4g==", "dev": true, "dependencies": { - "@babel/parser": "^7.21.3", - "@vue/shared": "3.3.4", + "@babel/parser": "^7.23.0", + "@vue/shared": "3.3.8", "estree-walker": "^2.0.2", "source-map-js": "^1.0.2" } }, "node_modules/@vue/compiler-dom": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.3.4.tgz", - "integrity": "sha512-wyM+OjOVpuUukIq6p5+nwHYtj9cFroz9cwkfmP9O1nzH68BenTTv0u7/ndggT8cIQlnBeOo6sUT/gvHcIkLA5w==", + "version": "3.3.8", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.3.8.tgz", + "integrity": "sha512-+PPtv+p/nWDd0AvJu3w8HS0RIm/C6VGBIRe24b9hSyNWOAPEUosFZ5diwawwP8ip5sJ8n0Pe87TNNNHnvjs0FQ==", "dev": true, "dependencies": { - "@vue/compiler-core": "3.3.4", - "@vue/shared": "3.3.4" + "@vue/compiler-core": "3.3.8", + "@vue/shared": "3.3.8" } }, "node_modules/@vue/compiler-sfc": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.3.4.tgz", - "integrity": "sha512-6y/d8uw+5TkCuzBkgLS0v3lSM3hJDntFEiUORM11pQ/hKvkhSKZrXW6i69UyXlJQisJxuUEJKAWEqWbWsLeNKQ==", + "version": "3.3.8", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.3.8.tgz", + "integrity": "sha512-WMzbUrlTjfYF8joyT84HfwwXo+8WPALuPxhy+BZ6R4Aafls+jDBnSz8PDz60uFhuqFbl3HxRfxvDzrUf3THwpA==", "dev": true, "dependencies": { - "@babel/parser": "^7.20.15", - "@vue/compiler-core": "3.3.4", - "@vue/compiler-dom": "3.3.4", - "@vue/compiler-ssr": "3.3.4", - "@vue/reactivity-transform": "3.3.4", - "@vue/shared": "3.3.4", + "@babel/parser": "^7.23.0", + "@vue/compiler-core": "3.3.8", + "@vue/compiler-dom": "3.3.8", + "@vue/compiler-ssr": "3.3.8", + "@vue/reactivity-transform": "3.3.8", + "@vue/shared": "3.3.8", "estree-walker": "^2.0.2", - "magic-string": "^0.30.0", - "postcss": "^8.1.10", + "magic-string": "^0.30.5", + "postcss": "^8.4.31", "source-map-js": "^1.0.2" } }, "node_modules/@vue/compiler-ssr": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.3.4.tgz", - "integrity": "sha512-m0v6oKpup2nMSehwA6Uuu+j+wEwcy7QmwMkVNVfrV9P2qE5KshC6RwOCq8fjGS/Eak/uNb8AaWekfiXxbBB6gQ==", + "version": "3.3.8", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.3.8.tgz", + "integrity": "sha512-hXCqQL/15kMVDBuoBYpUnSYT8doDNwsjvm3jTefnXr+ytn294ySnT8NlsFHmTgKNjwpuFy7XVV8yTeLtNl/P6w==", "dev": true, "dependencies": { - "@vue/compiler-dom": "3.3.4", - "@vue/shared": "3.3.4" + "@vue/compiler-dom": "3.3.8", + "@vue/shared": "3.3.8" } }, "node_modules/@vue/devtools-api": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.5.0.tgz", - "integrity": "sha512-o9KfBeaBmCKl10usN4crU53fYtC1r7jJwdGKjPT24t348rHxgfpZ0xL3Xm/gLUYnc0oTp8LAmrxOeLyu6tbk2Q==", + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.5.1.tgz", + "integrity": "sha512-+KpckaAQyfbvshdDW5xQylLni1asvNSGme1JFs8I1+/H5pHEhqUKMEQD/qn3Nx5+/nycBq11qAEi8lk+LXI2dA==", "dev": true }, "node_modules/@vue/reactivity": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.3.4.tgz", - "integrity": "sha512-kLTDLwd0B1jG08NBF3R5rqULtv/f8x3rOFByTDz4J53ttIQEDmALqKqXY0J+XQeN0aV2FBxY8nJDf88yvOPAqQ==", + "version": "3.3.8", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.3.8.tgz", + "integrity": "sha512-ctLWitmFBu6mtddPyOKpHg8+5ahouoTCRtmAHZAXmolDtuZXfjL2T3OJ6DL6ezBPQB1SmMnpzjiWjCiMYmpIuw==", "dev": true, "dependencies": { - "@vue/shared": "3.3.4" + "@vue/shared": "3.3.8" } }, "node_modules/@vue/reactivity-transform": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/@vue/reactivity-transform/-/reactivity-transform-3.3.4.tgz", - "integrity": "sha512-MXgwjako4nu5WFLAjpBnCj/ieqcjE2aJBINUNQzkZQfzIZA4xn+0fV1tIYBJvvva3N3OvKGofRLvQIwEQPpaXw==", + "version": "3.3.8", + "resolved": "https://registry.npmjs.org/@vue/reactivity-transform/-/reactivity-transform-3.3.8.tgz", + "integrity": "sha512-49CvBzmZNtcHua0XJ7GdGifM8GOXoUMOX4dD40Y5DxI3R8OUhMlvf2nvgUAcPxaXiV5MQQ1Nwy09ADpnLQUqRw==", "dev": true, "dependencies": { - "@babel/parser": "^7.20.15", - "@vue/compiler-core": "3.3.4", - "@vue/shared": "3.3.4", + "@babel/parser": "^7.23.0", + "@vue/compiler-core": "3.3.8", + "@vue/shared": "3.3.8", "estree-walker": "^2.0.2", - "magic-string": "^0.30.0" + "magic-string": "^0.30.5" } }, "node_modules/@vue/runtime-core": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.3.4.tgz", - "integrity": "sha512-R+bqxMN6pWO7zGI4OMlmvePOdP2c93GsHFM/siJI7O2nxFRzj55pLwkpCedEY+bTMgp5miZ8CxfIZo3S+gFqvA==", + "version": "3.3.8", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.3.8.tgz", + "integrity": "sha512-qurzOlb6q26KWQ/8IShHkMDOuJkQnQcTIp1sdP4I9MbCf9FJeGVRXJFr2mF+6bXh/3Zjr9TDgURXrsCr9bfjUw==", "dev": true, "dependencies": { - "@vue/reactivity": "3.3.4", - "@vue/shared": "3.3.4" + "@vue/reactivity": "3.3.8", + "@vue/shared": "3.3.8" } }, "node_modules/@vue/runtime-dom": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.3.4.tgz", - "integrity": "sha512-Aj5bTJ3u5sFsUckRghsNjVTtxZQ1OyMWCr5dZRAPijF/0Vy4xEoRCwLyHXcj4D0UFbJ4lbx3gPTgg06K/GnPnQ==", + "version": "3.3.8", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.3.8.tgz", + "integrity": "sha512-Noy5yM5UIf9UeFoowBVgghyGGPIDPy1Qlqt0yVsUdAVbqI8eeMSsTqBtauaEoT2UFXUk5S64aWVNJN4MJ2vRdA==", "dev": true, "dependencies": { - "@vue/runtime-core": "3.3.4", - "@vue/shared": "3.3.4", - "csstype": "^3.1.1" + "@vue/runtime-core": "3.3.8", + "@vue/shared": "3.3.8", + "csstype": "^3.1.2" } }, "node_modules/@vue/server-renderer": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.3.4.tgz", - "integrity": "sha512-Q6jDDzR23ViIb67v+vM1Dqntu+HUexQcsWKhhQa4ARVzxOY2HbC7QRW/ggkDBd5BU+uM1sV6XOAP0b216o34JQ==", + "version": "3.3.8", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.3.8.tgz", + "integrity": "sha512-zVCUw7RFskvPuNlPn/8xISbrf0zTWsTSdYTsUTN1ERGGZGVnRxM2QZ3x1OR32+vwkkCm0IW6HmJ49IsPm7ilLg==", "dev": true, "dependencies": { - "@vue/compiler-ssr": "3.3.4", - "@vue/shared": "3.3.4" + "@vue/compiler-ssr": "3.3.8", + "@vue/shared": "3.3.8" }, "peerDependencies": { - "vue": "3.3.4" + "vue": "3.3.8" } }, "node_modules/@vue/shared": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.3.4.tgz", - "integrity": "sha512-7OjdcV8vQ74eiz1TZLzZP4JwqM5fA94K6yntPS5Z25r9HDuGNzaGdgvwKYq6S+MxwF0TFRwe50fIR/MYnakdkQ==", + "version": "3.3.8", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.3.8.tgz", + "integrity": "sha512-8PGwybFwM4x8pcfgqEQFy70NaQxASvOC5DJwLQfpArw1UDfUXrJkdxD3BhVTMS+0Lef/TU7YO0Jvr0jJY8T+mw==", "dev": true }, "node_modules/@vueuse/core": { - "version": "10.4.1", - "resolved": "https://registry.npmjs.org/@vueuse/core/-/core-10.4.1.tgz", - "integrity": "sha512-DkHIfMIoSIBjMgRRvdIvxsyboRZQmImofLyOHADqiVbQVilP8VVHDhBX2ZqoItOgu7dWa8oXiNnScOdPLhdEXg==", + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/@vueuse/core/-/core-10.5.0.tgz", + "integrity": "sha512-z/tI2eSvxwLRjOhDm0h/SXAjNm8N5ld6/SC/JQs6o6kpJ6Ya50LnEL8g5hoYu005i28L0zqB5L5yAl8Jl26K3A==", "dev": true, "dependencies": { - "@types/web-bluetooth": "^0.0.17", - "@vueuse/metadata": "10.4.1", - "@vueuse/shared": "10.4.1", - "vue-demi": ">=0.14.5" + "@types/web-bluetooth": "^0.0.18", + "@vueuse/metadata": "10.5.0", + "@vueuse/shared": "10.5.0", + "vue-demi": ">=0.14.6" }, "funding": { "url": "https://github.com/sponsors/antfu" @@ -1606,14 +1635,14 @@ } }, "node_modules/@vueuse/integrations": { - "version": "10.4.1", - "resolved": "https://registry.npmjs.org/@vueuse/integrations/-/integrations-10.4.1.tgz", - "integrity": "sha512-uRBPyG5Lxoh1A/J+boiioPT3ELEAPEo4t8W6Mr4yTKIQBeW/FcbsotZNPr4k9uz+3QEksMmflWloS9wCnypM7g==", + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/@vueuse/integrations/-/integrations-10.5.0.tgz", + "integrity": "sha512-fm5sXLCK0Ww3rRnzqnCQRmfjDURaI4xMsx+T+cec0ngQqHx/JgUtm8G0vRjwtonIeTBsH1Q8L3SucE+7K7upJQ==", "dev": true, "dependencies": { - "@vueuse/core": "10.4.1", - "@vueuse/shared": "10.4.1", - "vue-demi": ">=0.14.5" + "@vueuse/core": "10.5.0", + "@vueuse/shared": "10.5.0", + "vue-demi": ">=0.14.6" }, "funding": { "url": "https://github.com/sponsors/antfu" @@ -1698,21 +1727,21 @@ } }, "node_modules/@vueuse/metadata": { - "version": "10.4.1", - "resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-10.4.1.tgz", - "integrity": "sha512-2Sc8X+iVzeuMGHr6O2j4gv/zxvQGGOYETYXEc41h0iZXIRnRbJZGmY/QP8dvzqUelf8vg0p/yEA5VpCEu+WpZg==", + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-10.5.0.tgz", + "integrity": "sha512-fEbElR+MaIYyCkeM0SzWkdoMtOpIwO72x8WsZHRE7IggiOlILttqttM69AS13nrDxosnDBYdyy3C5mR1LCxHsw==", "dev": true, "funding": { "url": "https://github.com/sponsors/antfu" } }, "node_modules/@vueuse/shared": { - "version": "10.4.1", - "resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-10.4.1.tgz", - "integrity": "sha512-vz5hbAM4qA0lDKmcr2y3pPdU+2EVw/yzfRsBdu+6+USGa4PxqSQRYIUC9/NcT06y+ZgaTsyURw2I9qOFaaXHAg==", + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-10.5.0.tgz", + "integrity": "sha512-18iyxbbHYLst9MqU1X1QNdMHIjks6wC7XTVf0KNOv5es/Ms6gjVFCAAWTVP2JStuGqydg3DT+ExpFORUEi9yhg==", "dev": true, "dependencies": { - "vue-demi": ">=0.14.5" + "vue-demi": ">=0.14.6" }, "funding": { "url": "https://github.com/sponsors/antfu" @@ -1745,9 +1774,9 @@ } }, "node_modules/acorn": { - "version": "8.10.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", - "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==", + "version": "8.11.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.2.tgz", + "integrity": "sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==", "dev": true, "bin": { "acorn": "bin/acorn" @@ -2026,9 +2055,9 @@ "dev": true }, "node_modules/commander": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-11.0.0.tgz", - "integrity": "sha512-9HMlXtt/BNoYr8ooyjjNRdIilOTkVJXB+GhxMTtOKwk0R4j4lS4NpjuqmRxroBfnfTSHQIHQB7wryHhXarNjmQ==", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz", + "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==", "dev": true, "engines": { "node": ">=16" @@ -2138,29 +2167,29 @@ } }, "node_modules/cspell": { - "version": "7.3.7", - "resolved": "https://registry.npmjs.org/cspell/-/cspell-7.3.7.tgz", - "integrity": "sha512-p23EuTu+7b2qioRxC7sV1TVfxIPm7928BtT4jYBHGeONiYP0EOOWNP8ynaksMYLTifQBzH1Q0LO4L5ogHiQsfw==", + "version": "7.3.9", + "resolved": "https://registry.npmjs.org/cspell/-/cspell-7.3.9.tgz", + "integrity": "sha512-QzunjO9CmV5+98UfG4ONhvPtrcAC6Y2pEKeOrp5oPeyAI7HwgxmfsR3ybHRlMPAGcwKtDOurBKxM7jqXNwkzmA==", "dev": true, "dependencies": { - "@cspell/cspell-json-reporter": "7.3.7", - "@cspell/cspell-pipe": "7.3.7", - "@cspell/cspell-types": "7.3.7", - "@cspell/dynamic-import": "7.3.7", + "@cspell/cspell-json-reporter": "7.3.9", + "@cspell/cspell-pipe": "7.3.9", + "@cspell/cspell-types": "7.3.9", + "@cspell/dynamic-import": "7.3.9", "chalk": "^5.3.0", "chalk-template": "^1.1.0", - "commander": "^11.0.0", - "cspell-gitignore": "7.3.7", - "cspell-glob": "7.3.7", - "cspell-io": "7.3.7", - "cspell-lib": "7.3.7", - "fast-glob": "^3.3.1", + "commander": "^11.1.0", + "cspell-gitignore": "7.3.9", + "cspell-glob": "7.3.9", + "cspell-io": "7.3.9", + "cspell-lib": "7.3.9", + "fast-glob": "^3.3.2", "fast-json-stable-stringify": "^2.1.0", - "file-entry-cache": "^7.0.0", + "file-entry-cache": "^7.0.1", "get-stdin": "^9.0.0", "semver": "^7.5.4", "strip-ansi": "^7.1.0", - "vscode-uri": "^3.0.7" + "vscode-uri": "^3.0.8" }, "bin": { "cspell": "bin.mjs", @@ -2174,14 +2203,14 @@ } }, "node_modules/cspell-dictionary": { - "version": "7.3.7", - "resolved": "https://registry.npmjs.org/cspell-dictionary/-/cspell-dictionary-7.3.7.tgz", - "integrity": "sha512-mJ0h2BGxYEqb/1FxKD50WuufKhDaCaIk8pwZQryqazXQCvoTpla0yud3KO61Cke92za8z37Rfb+5xATlywEfaw==", + "version": "7.3.9", + "resolved": "https://registry.npmjs.org/cspell-dictionary/-/cspell-dictionary-7.3.9.tgz", + "integrity": "sha512-lkWfX5QNbs4yKqD9wa+G+NHRWmLgFdyposgJOyd/ojDbx99CDPMhMhg9pyMKdYl6Yt8kjMow58/i12EYvD8wnA==", "dev": true, "dependencies": { - "@cspell/cspell-pipe": "7.3.7", - "@cspell/cspell-types": "7.3.7", - "cspell-trie-lib": "7.3.7", + "@cspell/cspell-pipe": "7.3.9", + "@cspell/cspell-types": "7.3.9", + "cspell-trie-lib": "7.3.9", "fast-equals": "^4.0.3", "gensequence": "^6.0.0" }, @@ -2196,12 +2225,12 @@ "dev": true }, "node_modules/cspell-gitignore": { - "version": "7.3.7", - "resolved": "https://registry.npmjs.org/cspell-gitignore/-/cspell-gitignore-7.3.7.tgz", - "integrity": "sha512-nP4Gg+zq5y0njzhiNYTLvaJIMAponBhJoTMzkXCOOKYEHJmiRQocfa3gO4t2s8iZ4YVhscbrB2h+dYvo3MLQqg==", + "version": "7.3.9", + "resolved": "https://registry.npmjs.org/cspell-gitignore/-/cspell-gitignore-7.3.9.tgz", + "integrity": "sha512-DLuu+K2q4xYNL4DpLyysUeiGU/NYYoObzfOYiISzOKYpi3aFLiUaiyfF6xWGsahmlijif+8bwSsIMmcvGa5dgA==", "dev": true, "dependencies": { - "cspell-glob": "7.3.7", + "cspell-glob": "7.3.9", "find-up": "^5.0.0" }, "bin": { @@ -2212,9 +2241,9 @@ } }, "node_modules/cspell-glob": { - "version": "7.3.7", - "resolved": "https://registry.npmjs.org/cspell-glob/-/cspell-glob-7.3.7.tgz", - "integrity": "sha512-DJX5wJ5dhcNzyycukZst+WtbIdpCLTL7DaKS0EKW/57QjzMwwMBgpsF89ufnreGHB8dHrPF85epF9qyOI1SRNg==", + "version": "7.3.9", + "resolved": "https://registry.npmjs.org/cspell-glob/-/cspell-glob-7.3.9.tgz", + "integrity": "sha512-7PaTkCzJWjQex3men857v3ExF7Q10jbQkfD+wdln2te9iNFd+HEkstA173vb828D9yeib1q1of8oONr2SeGycg==", "dev": true, "dependencies": { "micromatch": "^4.0.5" @@ -2224,13 +2253,13 @@ } }, "node_modules/cspell-grammar": { - "version": "7.3.7", - "resolved": "https://registry.npmjs.org/cspell-grammar/-/cspell-grammar-7.3.7.tgz", - "integrity": "sha512-4cyJ4Alq/wBGTctH7fNTbY9EZCihm11fbrGSYVe8w+msRNx6W8rugsMX009aHiw9zlvGrMAeTD08YFPnBVdfpA==", + "version": "7.3.9", + "resolved": "https://registry.npmjs.org/cspell-grammar/-/cspell-grammar-7.3.9.tgz", + "integrity": "sha512-s1QOPg4AxWE8XBewDQLe14j0uDyWGjREfm4dZFTrslAZUrQ8/df5s152M5LtgOEza33FrkKKE2axbGvgS9O7sQ==", "dev": true, "dependencies": { - "@cspell/cspell-pipe": "7.3.7", - "@cspell/cspell-types": "7.3.7" + "@cspell/cspell-pipe": "7.3.9", + "@cspell/cspell-types": "7.3.9" }, "bin": { "cspell-grammar": "bin.mjs" @@ -2240,12 +2269,12 @@ } }, "node_modules/cspell-io": { - "version": "7.3.7", - "resolved": "https://registry.npmjs.org/cspell-io/-/cspell-io-7.3.7.tgz", - "integrity": "sha512-zqGGllG/OM3Of7zaOELdrSoBpCyG9nJuSRCzLfKgnCG4g2zpoMfDZknJaY9VjZODHP99PvYWooF8E6kVxT34Fw==", + "version": "7.3.9", + "resolved": "https://registry.npmjs.org/cspell-io/-/cspell-io-7.3.9.tgz", + "integrity": "sha512-IbXOYaDxLg94uijv13kqb+6PQjEwGboQYtABuZs2+HuUVW89K2tE+fQcEhkAsrZ11sDj5lUqgEQj9omvknZSuA==", "dev": true, "dependencies": { - "@cspell/cspell-service-bus": "7.3.7", + "@cspell/cspell-service-bus": "7.3.9", "node-fetch": "^2.7.0" }, "engines": { @@ -2253,33 +2282,33 @@ } }, "node_modules/cspell-lib": { - "version": "7.3.7", - "resolved": "https://registry.npmjs.org/cspell-lib/-/cspell-lib-7.3.7.tgz", - "integrity": "sha512-KuFn0WTwmK50Ij1KVaXVuheleSOfv3oFIO3PfMuFg7llkfPfaRawF0b61da/EFGckU/hUc8uHRbBuGELlDo3tA==", + "version": "7.3.9", + "resolved": "https://registry.npmjs.org/cspell-lib/-/cspell-lib-7.3.9.tgz", + "integrity": "sha512-eFYYs8XoYmdu78UxrPisD+hAoXOLaLzcevKf9+oDPDgJmHpkGoFgbIBnHMRIsAM1e+QDS6OlWG/rybhZTqanCQ==", "dev": true, "dependencies": { - "@cspell/cspell-bundled-dicts": "7.3.7", - "@cspell/cspell-pipe": "7.3.7", - "@cspell/cspell-resolver": "7.3.7", - "@cspell/cspell-types": "7.3.7", - "@cspell/dynamic-import": "7.3.7", - "@cspell/strong-weak-map": "7.3.7", + "@cspell/cspell-bundled-dicts": "7.3.9", + "@cspell/cspell-pipe": "7.3.9", + "@cspell/cspell-resolver": "7.3.9", + "@cspell/cspell-types": "7.3.9", + "@cspell/dynamic-import": "7.3.9", + "@cspell/strong-weak-map": "7.3.9", "clear-module": "^4.1.2", "comment-json": "^4.2.3", "configstore": "^6.0.0", "cosmiconfig": "8.0.0", - "cspell-dictionary": "7.3.7", - "cspell-glob": "7.3.7", - "cspell-grammar": "7.3.7", - "cspell-io": "7.3.7", - "cspell-trie-lib": "7.3.7", + "cspell-dictionary": "7.3.9", + "cspell-glob": "7.3.9", + "cspell-grammar": "7.3.9", + "cspell-io": "7.3.9", + "cspell-trie-lib": "7.3.9", "fast-equals": "^5.0.1", "find-up": "^6.3.0", "gensequence": "^6.0.0", "import-fresh": "^3.3.0", "resolve-from": "^5.0.0", "vscode-languageserver-textdocument": "^1.0.11", - "vscode-uri": "^3.0.7" + "vscode-uri": "^3.0.8" }, "engines": { "node": ">=16" @@ -2368,13 +2397,13 @@ } }, "node_modules/cspell-trie-lib": { - "version": "7.3.7", - "resolved": "https://registry.npmjs.org/cspell-trie-lib/-/cspell-trie-lib-7.3.7.tgz", - "integrity": "sha512-Vv8TdTMZD3DE79SorTwn5NoWj8JD7DnYMeUK+5S6JDNLy4Ck+kTEPN6Ic9hvLAxuDmQjmoZI3TizrWvuCG66aA==", + "version": "7.3.9", + "resolved": "https://registry.npmjs.org/cspell-trie-lib/-/cspell-trie-lib-7.3.9.tgz", + "integrity": "sha512-aTWm2KYXjQ+MlM6kB37wmTV9RU8+fgZYkiFfMc48M0MhBc6XkHUibMGrFAS29gp+B70kWPxe+VHLmFIk9pRPyg==", "dev": true, "dependencies": { - "@cspell/cspell-pipe": "7.3.7", - "@cspell/cspell-types": "7.3.7", + "@cspell/cspell-pipe": "7.3.9", + "@cspell/cspell-types": "7.3.9", "gensequence": "^6.0.0" }, "engines": { @@ -2553,18 +2582,19 @@ } }, "node_modules/eslint": { - "version": "8.51.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.51.0.tgz", - "integrity": "sha512-2WuxRZBrlwnXi+/vFSJyjMqrNjtJqiasMzehF0shoLaW7DzS3/9Yvrmq5JiT66+pNjiX4UBnLDiKHcWAr/OInA==", + "version": "8.53.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.53.0.tgz", + "integrity": "sha512-N4VuiPjXDUa4xVeV/GC/RV3hQW9Nw+Y463lkWaKKXKYMvmRiRDAtfpuPFLN+E1/6ZhyR8J2ig+eVREnYgUsiag==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", - "@eslint/eslintrc": "^2.1.2", - "@eslint/js": "8.51.0", - "@humanwhocodes/config-array": "^0.11.11", + "@eslint/eslintrc": "^2.1.3", + "@eslint/js": "8.53.0", + "@humanwhocodes/config-array": "^0.11.13", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", @@ -2607,9 +2637,9 @@ } }, "node_modules/eslint-plugin-vue": { - "version": "9.17.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-9.17.0.tgz", - "integrity": "sha512-r7Bp79pxQk9I5XDP0k2dpUC7Ots3OSWgvGZNu3BxmKK6Zg7NgVtcOB6OCna5Kb9oQwJPl5hq183WD0SY5tZtIQ==", + "version": "9.18.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-9.18.1.tgz", + "integrity": "sha512-7hZFlrEgg9NIzuVik2I9xSnJA5RsmOfueYgsUGUokEDLJ1LHtxO0Pl4duje1BriZ/jDWb+44tcIlC3yi0tdlZg==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", @@ -2864,9 +2894,9 @@ } }, "node_modules/fast-glob": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz", - "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", "dev": true, "dependencies": { "@nodelib/fs.stat": "^2.0.2", @@ -2973,9 +3003,9 @@ "dev": true }, "node_modules/focus-trap": { - "version": "7.5.3", - "resolved": "https://registry.npmjs.org/focus-trap/-/focus-trap-7.5.3.tgz", - "integrity": "sha512-7UsT/eSJcTPF0aZp73u7hBRTABz26knRRTJfoTGFCQD5mUImLIIOwWWCrtoQdmWa7dykBi6H+Cp5i3S/kvsMeA==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/focus-trap/-/focus-trap-7.5.4.tgz", + "integrity": "sha512-N7kHdlgsO/v+iD/dMoJKtsSqs5Dz/dXZVebRgJw23LDk+jMi/974zyiOYDziY2JPp8xivq9BmUGwIJMiuSBi7w==", "dev": true, "dependencies": { "tabbable": "^6.2.0" @@ -3266,9 +3296,9 @@ } }, "node_modules/import-meta-resolve": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-3.0.0.tgz", - "integrity": "sha512-4IwhLhNNA8yy445rPjD/lWh++7hMDOml2eHtd58eG7h+qK3EryMuuRbsHGPikCoAgIkkDnckKfWSk2iDla/ejg==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-3.1.1.tgz", + "integrity": "sha512-qeywsE/KC3w9Fd2ORrRDUw6nS/nLwZpXgfrOc2IILvZYnCaEMd+D56Vfg9k4G29gIeVi3XKql1RQatME8iYsiw==", "dev": true, "funding": { "type": "github", @@ -3472,9 +3502,9 @@ "dev": true }, "node_modules/keyv": { - "version": "4.5.3", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.3.tgz", - "integrity": "sha512-QCiSav9WaX1PgETJ+SpNnx2PRRapJ/oRSXM4VO5OGYGSjrxbKPVFVhB3l2OCbLCk329N8qyAtsJjSjvVBWzEug==", + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", "dev": true, "dependencies": { "json-buffer": "3.0.1" @@ -3554,9 +3584,9 @@ } }, "node_modules/magic-string": { - "version": "0.30.4", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.4.tgz", - "integrity": "sha512-Q/TKtsC5BPm0kGqgBIF9oXAs/xEf2vRKiIB4wCRQTJOQIByZ1d+NnUOotvJOvNpi5RNIgVOMC3pOuaP1ZTDlVg==", + "version": "0.30.5", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.5.tgz", + "integrity": "sha512-7xlpfBaQaP/T6Vh8MO/EqXSW5En6INHEvEXQiuff7Gku0PWjU3uf6w/j9o7O+SpB5fOAkrI5HeoNgwjEO0pFsA==", "dev": true, "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.15" @@ -3632,6 +3662,15 @@ "balanced-match": "^1.0.0" } }, + "node_modules/markdownlint-cli/node_modules/commander": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-11.0.0.tgz", + "integrity": "sha512-9HMlXtt/BNoYr8ooyjjNRdIilOTkVJXB+GhxMTtOKwk0R4j4lS4NpjuqmRxroBfnfTSHQIHQB7wryHhXarNjmQ==", + "dev": true, + "engines": { + "node": ">=16" + } + }, "node_modules/markdownlint-cli/node_modules/minimatch": { "version": "9.0.3", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", @@ -3748,9 +3787,9 @@ } }, "node_modules/minisearch": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/minisearch/-/minisearch-6.1.0.tgz", - "integrity": "sha512-PNxA/X8pWk+TiqPbsoIYH0GQ5Di7m6326/lwU/S4mlo4wGQddIcf/V//1f9TB0V4j59b57b+HZxt8h3iMROGvg==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/minisearch/-/minisearch-6.2.0.tgz", + "integrity": "sha512-BECkorDF1TY2rGKt9XHdSeP9TP29yUbrAaCh/C03wpyf1vx3uYcP/+8XlMcpTkgoU0rBVnHMAOaP83Rc9Tm+TQ==", "dev": true }, "node_modules/ms": { @@ -3760,9 +3799,9 @@ "dev": true }, "node_modules/nanoid": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", - "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==", + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", "dev": true, "funding": [ { @@ -4022,9 +4061,9 @@ } }, "node_modules/preact": { - "version": "10.18.1", - "resolved": "https://registry.npmjs.org/preact/-/preact-10.18.1.tgz", - "integrity": "sha512-mKUD7RRkQQM6s7Rkmi7IFkoEHjuFqRQUaXamO61E6Nn7vqF/bo7EZCmSyrUnp2UWHw0O7XjZ2eeXis+m7tf4lg==", + "version": "10.18.2", + "resolved": "https://registry.npmjs.org/preact/-/preact-10.18.2.tgz", + "integrity": "sha512-X/K43vocUHDg0XhWVmTTMbec4LT/iBMh+csCEqJk+pJqegaXsvjdqN80ZZ3L+93azWCnWCZ+WGwYb8SplxeNjA==", "dev": true, "funding": { "type": "opencollective", @@ -4041,9 +4080,9 @@ } }, "node_modules/punycode": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", - "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", "dev": true, "engines": { "node": ">=6" @@ -4208,9 +4247,9 @@ } }, "node_modules/sass": { - "version": "1.69.0", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.69.0.tgz", - "integrity": "sha512-l3bbFpfTOGgQZCLU/gvm1lbsQ5mC/WnLz3djL2v4WCJBDrWm58PO+jgngcGRNnKUh6wSsdm50YaovTqskZ0xDQ==", + "version": "1.69.5", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.69.5.tgz", + "integrity": "sha512-qg2+UCJibLr2LCVOt3OlPhr/dqVHWOa9XtZf2OjbLs/T4VPSJ00udtgJxH3neXZm+QqX8B+3cU7RaLqp1iVfcQ==", "dev": true, "dependencies": { "chokidar": ">=3.0.0 <4.0.0", @@ -4225,9 +4264,9 @@ } }, "node_modules/search-insights": { - "version": "2.8.3", - "resolved": "https://registry.npmjs.org/search-insights/-/search-insights-2.8.3.tgz", - "integrity": "sha512-W9rZfQ9XEfF0O6ntgQOTI7Txc8nkZrO4eJ/pTHK0Br6wWND2sPGPoWg+yGhdIW7wMbLqk8dc23IyEtLlNGpeNw==", + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/search-insights/-/search-insights-2.10.0.tgz", + "integrity": "sha512-pQGrOE56QuTRmq4NzliRZe9rv914hBMBjOviuDliDHoIhmBGoyZRlFsPd4RprGGNC4PKdD2Jz54YN4Cmkb44mA==", "dev": true, "peer": true }, @@ -4293,9 +4332,9 @@ } }, "node_modules/shiki": { - "version": "0.14.4", - "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.14.4.tgz", - "integrity": "sha512-IXCRip2IQzKwxArNNq1S+On4KPML3Yyn8Zzs/xRgcgOWIr8ntIK3IKzjFPfjy/7kt9ZMjc+FItfqHRBg8b6tNQ==", + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.14.5.tgz", + "integrity": "sha512-1gCAYOcmCFONmErGTrS1fjzJLA7MGZmKzrBNX7apqSwhyITJg2O102uFzXUeBxNnEkDA9vHIKLyeKq0V083vIw==", "dev": true, "dependencies": { "ansi-sequence-parser": "^1.1.0", @@ -4564,6 +4603,12 @@ "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==", "dev": true }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true + }, "node_modules/unique-string": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-3.0.0.tgz", @@ -4595,9 +4640,9 @@ "dev": true }, "node_modules/vite": { - "version": "4.4.11", - "resolved": "https://registry.npmjs.org/vite/-/vite-4.4.11.tgz", - "integrity": "sha512-ksNZJlkcU9b0lBwAGZGGaZHCMqHsc8OpgtoYhsQ4/I2v5cnpmmmqe5pM4nv/4Hn6G/2GhTdj0DhZh2e+Er1q5A==", + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/vite/-/vite-4.5.0.tgz", + "integrity": "sha512-ulr8rNLA6rkyFAlVWw2q5YJ91v098AFQ2R0PRFwPzREXOUJQPtFUG0t+/ZikhaOCDqFoDhN6/v8Sq0o4araFAw==", "dev": true, "dependencies": { "esbuild": "^0.18.10", @@ -4650,30 +4695,31 @@ } }, "node_modules/vitepress": { - "version": "1.0.0-rc.20", - "resolved": "https://registry.npmjs.org/vitepress/-/vitepress-1.0.0-rc.20.tgz", - "integrity": "sha512-CykMUJ8JLxLcGWek0ew3wln4RYbsOd1+0YzXITTpajggpynm2S331TNkJVOkHrMRc6GYe3y4pS40GfgcW0ZwAw==", + "version": "1.0.0-rc.25", + "resolved": "https://registry.npmjs.org/vitepress/-/vitepress-1.0.0-rc.25.tgz", + "integrity": "sha512-1dqWiHNThNrVZ08ixmfEDBEH+764KOgnev9oXga/x6cN++Vb9pnuu8p3K6DQP+KZrYcG+WiX7jxal0iSNpAWuQ==", "dev": true, "dependencies": { "@docsearch/css": "^3.5.2", "@docsearch/js": "^3.5.2", - "@types/markdown-it": "^13.0.1", - "@vue/devtools-api": "^6.5.0", - "@vueuse/core": "^10.4.1", - "@vueuse/integrations": "^10.4.1", - "focus-trap": "^7.5.2", + "@types/markdown-it": "^13.0.4", + "@vitejs/plugin-vue": "4.3.1", + "@vue/devtools-api": "^6.5.1", + "@vueuse/core": "^10.5.0", + "@vueuse/integrations": "^10.5.0", + "focus-trap": "^7.5.4", "mark.js": "8.11.1", "minisearch": "^6.1.0", - "shiki": "^0.14.4", - "vite": "^4.4.9", - "vue": "^3.3.4" + "shiki": "^0.14.5", + "vite": "^4.5.0", + "vue": "^3.3.6" }, "bin": { "vitepress": "bin/vitepress.js" }, "peerDependencies": { "markdown-it-mathjax3": "^4.3.2", - "postcss": "^8.4.30" + "postcss": "^8.4.31" }, "peerDependenciesMeta": { "markdown-it-mathjax3": { @@ -4709,22 +4755,30 @@ "dev": true }, "node_modules/vue": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/vue/-/vue-3.3.4.tgz", - "integrity": "sha512-VTyEYn3yvIeY1Py0WaYGZsXnz3y5UnGi62GjVEqvEGPl6nxbOrCXbVOTQWBEJUqAyTUk2uJ5JLVnYJ6ZzGbrSw==", + "version": "3.3.8", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.3.8.tgz", + "integrity": "sha512-5VSX/3DabBikOXMsxzlW8JyfeLKlG9mzqnWgLQLty88vdZL7ZJgrdgBOmrArwxiLtmS+lNNpPcBYqrhE6TQW5w==", "dev": true, "dependencies": { - "@vue/compiler-dom": "3.3.4", - "@vue/compiler-sfc": "3.3.4", - "@vue/runtime-dom": "3.3.4", - "@vue/server-renderer": "3.3.4", - "@vue/shared": "3.3.4" + "@vue/compiler-dom": "3.3.8", + "@vue/compiler-sfc": "3.3.8", + "@vue/runtime-dom": "3.3.8", + "@vue/server-renderer": "3.3.8", + "@vue/shared": "3.3.8" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, "node_modules/vue-eslint-parser": { - "version": "9.3.1", - "resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-9.3.1.tgz", - "integrity": "sha512-Clr85iD2XFZ3lJ52/ppmUDG/spxQu6+MAeHXjjyI4I1NUYZ9xmenQp4N0oaHJhrA8OOxltCVxMRfANGa70vU0g==", + "version": "9.3.2", + "resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-9.3.2.tgz", + "integrity": "sha512-q7tWyCVaV9f8iQyIA5Mkj/S6AoJ9KBN8IeUSf3XEmBrOtxOZnfTg5s4KClbZBCK3GtnT/+RyCLZyDHuZwTuBjg==", "dev": true, "dependencies": { "debug": "^4.3.4", diff --git a/package.json b/package.json index 8e155f61c..ad5202d87 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@cap-js/docs", - "version": "0.20.0", + "version": "0.25.0", "description": "Capire on VitePress", "type": "module", "scripts": { @@ -27,6 +27,6 @@ "markdownlint-cli": ">=0.35.0", "markdownlint-rule-search-replace": "^1.1.1", "sass": "^1.62.1", - "vitepress": "^1.0.0-rc.20" + "vitepress": "^1.0.0-rc.25" } } From 93c03d9f7364652be47d496e136eafaba7005e44 Mon Sep 17 00:00:00 2001 From: sjvans <30337871+sjvans@users.noreply.github.com> Date: Thu, 9 Nov 2023 07:56:26 +0100 Subject: [PATCH 17/43] 5 -> 6 (#529) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 5 -> 6 * removed numbers * remove number --------- Co-authored-by: René Jeglinsky --- guides/using-services.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/guides/using-services.md b/guides/using-services.md index 898e6dedd..2ce26edf4 100644 --- a/guides/using-services.md +++ b/guides/using-services.md @@ -92,7 +92,7 @@ The user picks a supplier from the list. That list is coming [from the remote sy It should be also possible to search for suppliers and show the associated risks by extending the remote supplier service [with the local risk service](#extend-a-remote-by-a-local-service) and its risks. -## ① Get and Import an External Service API { #external-service-api } +## Get and Import an External Service API { #external-service-api } To communicate to remote services, CAP needs to know their definitions. Having the definitions in your project allows you to mock them during design time. @@ -236,7 +236,7 @@ To work with remote services, add the following dependency to your Maven project
-## ② Local Mocking {#local-mocking} +## Local Mocking {#local-mocking} When developing your application, you can mock the remote service. @@ -469,7 +469,7 @@ For example: [Try out the example application.](https://github.com/SAP-samples/cloud-cap-risk-management/tree/ext-service-s4hc-suppliers-ui-java){.learn-more} -## ③ Execute Queries {#execute-queries} +## Execute Queries {#execute-queries} You can send requests to remote services using CAP's powerful querying API. @@ -596,7 +596,7 @@ For Java, you can use the `HttpClient` API to implement your custom requests. Th [Learn more about using destinations.](#use-destinations-with-java){.learn-more} -## ④ Integrate and Extend {#integrate-and-extend} +## Integrate and Extend {#integrate-and-extend} By creating projections on remote service entities and using associations, you can create services that combine data from your local service and remote services. @@ -923,7 +923,7 @@ The following matrix can help you to find the best approach for your scenario: > 4 Depends on the connectivity and performance of the remote system.
-## ⑤ Connect and Deploy {#connect-and-deploy} +## Connect and Deploy {#connect-and-deploy} + + +# Transactional Outbox + +Usually the emit of messages should be delayed until the main transaction succeeded. Otherwise recipients will also receive messages in case of a rollback. +To solve this problem, an outbox is used internally to defer the emit of messages until the success of the current transaction. + + + + + +## Persistent Outbox (Default) + +Using the persistent outbox, the to-be-emitted message is stored in a database table first. The same database transaction is used +as for other operations, therefore transactional consistency is guaranteed. + +The persistent outbox is globally enabled for all deferrable services (for example for [cds.MessagingService](messaging) and `cds.AuditLogService`). +You can set the global outbox configuration, the defaults are: + +```json +{ + "requires": { + "outbox": { + "kind": "persistent-outbox", + "maxAttempts": 20, + "chunkSize": 100, + "storeLastError": true, + "parallel": true + } + } +} +``` + +The optional parameters are: + +- `maxAttempts` (default `20`): The number of unsuccessful emits until the message is ignored. It will still remain in the database table. +- `chunkSize` (default `100`): The number of messages which are read from the database table in one go. +- `storeLastError` (default `true`): Specifies if error information of the last failed emit should be stored in the outbox table. +- `parallel` (default `true`): Specifies if messages are sent in parallel (faster but the order isn't guaranteed). + + +Once the transaction succeeds, the messages are read from the database table and emitted. If an emit was successful, the respective message +is deleted from the database table. If not, there will be retries after (exponentially growing) waiting times. +After a maximum number of attempts, the message is ignored for processing and remains in the database table which +therefore also acts as a dead letter queue. +There is only one active message processor per service, tenant and app instance, hence there won't be +duplicate emits except in the unlikely case of an app crash right after the emit and before the deletion of the +message entry. +::: tip +Some errors during the emit are identified as unrecoverable, for example in [SAP Event Mesh](../guides/messaging/event-mesh) if the used topic is forbidden. +The respective message is then updated and the `attempts` field is set to `maxAttempts` to prevent further processing. +[Programming errors](./best-practices#error-types) crash the server instance and must be fixed. +::: + + +Your database model is automatically extended by the entity `cds.outbox.Messages`, as follows: + +```cds +using cuid from '@sap/cds/common'; + +namespace cds.outbox; + +entity Messages : cuid { + timestamp: Timestamp; + target: String; + msg: LargeString; + attempts: Integer default 0; + partition: Integer default 0; + lastError: LargeString; + lastAttemptTimestamp: Timestamp @cds.on.update : $now; +} +``` +::: tip +In your CDS model, you can refer to the entity `cds.outbox.Messages` using the path `@sap/cds/srv/outbox`, +for example to expose it in a service. +::: + +::: warning +- If the app crashes, another emit for the respective tenant and service is necessary to restart the message processing. +- The user id is stored to recreate the correct context. +::: + +To overwrite the outbox configuration for a particular service, you can specify the `outbox` option. + +Example: + +```json +{ + "requires": { + "messaging": { + "kind": "enterprise-messaging", + "outbox": { + "maxAttempts": 10, + "chunkSize": 10 + } + } + } +} +``` + +## In-Memory Outbox + +Messages are emitted when the current transaction is successful. Until then, messages are only kept in memory. +This is similar to the following code if done manually: +```js +cds.context.on('succeeded', () => this.emit(msg)) +``` +::: warning +The message is lost if its emit fails, there is no retry mechanism. +The app will crash if the error is identified as unrecoverable, for example in [SAP Event Mesh](../guides/messaging/event-mesh) if the used topic is forbidden. +::: + +## Immediate Emit + +To disable deferred emitting for a particular service, you can set the `outbox` option of your service to `false`: + +```json +{ + "requires": { + "messaging": { + "kind": "enterprise-messaging", + "outbox": false + } + } +} +``` + +## Troubleshooting + +### Delete Entries in the Outbox Table + +To manually delete entries in the table `cds.outbox.Messages`, you can either +expose it in a service or programmatically modify it using the `cds.outbox.Messages` +entity: + +```js +const db = await cds.connect.to('db') +const { Messages } = db.entities('cds.outbox') +await DELETE.from(Messages) +``` + +### Outbox Table Not Found + +If the outbox table is not found on the database, this can be caused by insufficient configuration data in _package.json_. + +In case you have overwritten `requires.db.model` there, make sure to add the outbox model path `@sap/cds/srv/outbox`: + +```jsonc +"requires": { + "db": { ... + "model": [..., "@sap/cds/srv/outbox"] + } +} +``` + +The following is only relevant if you're using @sap/cds version < 6.7.0 and you've configured `options.model` in custom build tasks. +Add the model path accordingly: + +```jsonc +"build": { + "tasks": [{ ... + "options": { "model": [..., "@sap/cds/srv/outbox"] } + }] +} +``` + +Note that model configuration isn't required for CAP projects using the [standard project layout](../get-started/jumpstart#project-structure) that contain the folders `db`, `srv`, and `app`. In this case, you can delete the entire `model` configuration. + From b13b2b16a01adf810a5cd7851f75c4b8baba0025 Mon Sep 17 00:00:00 2001 From: Johannes Vogel <31311694+johannes-vogel@users.noreply.github.com> Date: Thu, 9 Nov 2023 20:49:59 +0100 Subject: [PATCH 22/43] add: document SELECT.elements (#532) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * add: document SELECT.elements * Apply suggestions from code review --------- Co-authored-by: René Jeglinsky --- node.js/cds-ql.md | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/node.js/cds-ql.md b/node.js/cds-ql.md index f42e9ac6b..57bb79231 100644 --- a/node.js/cds-ql.md +++ b/node.js/cds-ql.md @@ -284,6 +284,35 @@ const [one] = await SELECT.from (Authors) +### .elements {.property} + + +The CSN outline of the selected elements as an object. Key is the selected element or alias, value is the CSN definition: + +Let's assume the following query: +```js +SELECT.from('sap.capire.bookshop.Books').columns('ID', 'title') +``` + +This query is represented within `.elements` as: + +```js +{ + ID: number { key: true, type: 'cds.Integer' }, + title: string { + '@mandatory': true, + localized: true, + type: 'cds.String', + length: 111, + '@Common.FieldControl': { '#': 'Mandatory' } + } +} +``` + +This is useful for custom implementations that act on the selection of specific elements. + + + ### .distinct {.property} Start the query with `SELECT.distinct` to skip duplicates as in SQL: From 25f59ca2a79b4fe2c1349b16004ee96e8e7ec3d8 Mon Sep 17 00:00:00 2001 From: Rene Jeglinsky Date: Fri, 10 Nov 2023 09:20:30 +0100 Subject: [PATCH 23/43] fix anchor --- node.js/outbox.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/node.js/outbox.md b/node.js/outbox.md index a900c697d..9a80ab728 100644 --- a/node.js/outbox.md +++ b/node.js/outbox.md @@ -17,7 +17,7 @@ To solve this problem, an outbox is used internally to defer the emit of message -## Persistent Outbox (Default) +## Persistent Outbox (Default) {#persistent-outbox} Using the persistent outbox, the to-be-emitted message is stored in a database table first. The same database transaction is used as for other operations, therefore transactional consistency is guaranteed. @@ -173,4 +173,3 @@ Add the model path accordingly: ``` Note that model configuration isn't required for CAP projects using the [standard project layout](../get-started/jumpstart#project-structure) that contain the folders `db`, `srv`, and `app`. In this case, you can delete the entire `model` configuration. - From dae6d688403f47e3850b3e8e45d26d33ab5d36a6 Mon Sep 17 00:00:00 2001 From: Stefan Henke Date: Fri, 10 Nov 2023 11:39:33 +0100 Subject: [PATCH 24/43] documentation for systemUser methods in Request Context (#494) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * systemUser methods * add images * revert changing anchor name * update diagram * get rid of png images * incorporate feedback * incorporate feedback into diagrams * add switch to provider tenant * add missing diagram * make auditlog doc public * incorporate feedback * revert changes * intermediate * simple graphics and alt texts * edited alt text * Update request-contexts.md * feedback --------- Co-authored-by: René Jeglinsky --- java/assets/nameduser.drawio.svg | 197 ++++++++++++++++ java/assets/requestcontext.drawio.svg | 237 ++++++++++++++++++++ java/assets/switchprovidertenant.drawio.svg | 216 ++++++++++++++++++ java/assets/switchtenant.drawio.svg | 158 +++++++++++++ java/request-contexts.md | 104 +++++++-- 5 files changed, 890 insertions(+), 22 deletions(-) create mode 100644 java/assets/nameduser.drawio.svg create mode 100644 java/assets/requestcontext.drawio.svg create mode 100644 java/assets/switchprovidertenant.drawio.svg create mode 100644 java/assets/switchtenant.drawio.svg diff --git a/java/assets/nameduser.drawio.svg b/java/assets/nameduser.drawio.svg new file mode 100644 index 000000000..a6452c37e --- /dev/null +++ b/java/assets/nameduser.drawio.svg @@ -0,0 +1,197 @@ + + + + + + + + + + +
+
+
+ OData Adapter +
+
+
+
+ + OData Adapter + +
+
+ + + + + + +
+
+
+ Custom ON Handler +
+
+
+
+ + Custom ON Handler + +
+
+ + + + + + + +
+
+
+ Custom AFTER Handler +
+
+
+
+ + Custom AFTER Handler + +
+
+ + + + + +
+
+
+ systemUser() +
+
+
+
+ + systemUser() + +
+
+ + + + +
+
+
+ Technical Service +
+
+
+
+ + Technical Service + +
+
+ + + + + +
+
+
+ tenant1 +
+
+
+
+ + tenant1 + +
+
+ + + + + +
+
+
+ John Doe +
+
+
+
+ + John Doe + +
+
+ + + + + +
+
+
+ Technical User +
+
+
+
+ + Technical... + +
+
+ + + + +
+
+
+ User: John Doe +
+ Tenant: tenant1 +
+
+
+
+ + User: John Doe... + +
+
+ + + + +
+
+
+ JWT token +
+
+
+
+ + JWT token + +
+
+
+ + + + + Text is not SVG - cannot display + + + +
\ No newline at end of file diff --git a/java/assets/requestcontext.drawio.svg b/java/assets/requestcontext.drawio.svg new file mode 100644 index 000000000..52eb8d5a9 --- /dev/null +++ b/java/assets/requestcontext.drawio.svg @@ -0,0 +1,237 @@ + + + + + + + + + + +
+
+
+ Named User +
+
+
+
+ + Named User + +
+
+ + + + +
+
+
+ Named User +
+
+ or +
+
+ System User  Subscriber +
+
+
+
+ + Named User... + +
+
+ + + + +
+
+
+ System User +
+ Subscriber +
+
+
+
+ + System User... + +
+
+ + + + +
+
+
+ System User +
+ Provider +
+
+
+
+ + System User... + +
+
+ + + + +
+
+
+ System User +
+ Provider +
+
+
+
+ + System User... + +
+
+ + + + +
+
+
+ System User +
+ Subscriber +
+
+
+
+ + System User... + +
+
+ + + + +
+
+
+ Switching to technical user +
+
+
+
+ + Switching to technical user + +
+
+ + + + +
+
+
+ Switching to provider tenant +
+
+
+
+ + Switching to provider tenant + +
+
+ + + + +
+
+
+ Switching to a specific tenant +
+
+
+
+ + Switching to a specific tenant + +
+
+ + + + + +
+
+
+ systemUserProvider() +
+
+
+
+ + systemUser... + +
+
+ + + + + +
+
+
+ systemUser() +
+
+
+
+ + systemUser... + +
+
+ + + + + +
+
+
+ systemUser(tenant) +
+
+
+
+ + systemUser... + +
+
+
+ + + + + Text is not SVG - cannot display + + + +
\ No newline at end of file diff --git a/java/assets/switchprovidertenant.drawio.svg b/java/assets/switchprovidertenant.drawio.svg new file mode 100644 index 000000000..8d3c9c78a --- /dev/null +++ b/java/assets/switchprovidertenant.drawio.svg @@ -0,0 +1,216 @@ + + + + + + + + + + +
+
+
+ Technical User +
+
+
+
+ + Technical... + +
+
+ + + + + + +
+
+
+ OData Adapter +
+
+
+
+ + OData Adapter + +
+
+ + + + +
+
+
+ ON Handler for OData Action +
+
+
+
+ + ON Handler for OData... + +
+
+ + + + + + + + +
+
+
+ systemUserProvider() +
+
+
+
+ + systemUserProvider() + +
+
+ + + + + +
+
+
+ Sidecar +
+ CAP Service +
+
+
+
+ + Sidecar... + +
+
+ + + + + +
+
+
+ @requires: 'internal-user' +
+
+
+
+ + @requires:... + +
+
+ + + + +
+
+
+ User: John Doe +
+ Tenant: tenant1 +
+
+
+
+ + User: John Doe... + +
+
+ + + + +
+
+
+ JWT token +
+
+
+
+ + JWT token + +
+
+ + + + + +
+
+
+ John Doe +
+
+
+
+ + John Doe + +
+
+ + + + +
+
+
+ tenant1 +
+
+
+
+ + tenant1 + +
+
+ + + + +
+
+
+ provider tenant +
+
+
+
+ + provider tenant + +
+
+
+ + + + + Text is not SVG - cannot display + + + +
\ No newline at end of file diff --git a/java/assets/switchtenant.drawio.svg b/java/assets/switchtenant.drawio.svg new file mode 100644 index 000000000..2e80138c5 --- /dev/null +++ b/java/assets/switchtenant.drawio.svg @@ -0,0 +1,158 @@ + + + + + + + + + + +
+
+
+ Technical User +
+
+
+
+ + Technical... + +
+
+ + + + + + + +
+
+
+ Technical User +
+
+
+
+ + Technical... + +
+
+ + + + +
+
+
+ Background Job +
+
+
+
+ + Background Job + +
+
+ + + + +
+
+
+ Job Scheduler +
+
+
+
+ + Job Scheduler + +
+
+ + + + + +
+
+
+ Tenant specific processing +
+
+
+
+ + Tenant specific proc... + +
+
+ + + + + +
+
+
+ systemUser("tenant1") +
+
+
+
+ + systemUser("tenant1") + +
+
+ + + + + +
+
+
+ provider tenant +
+
+
+
+ + provider tenant + +
+
+ + + + +
+
+
+ tenant1 +
+
+
+
+ + tenant1 + +
+
+
+ + + + + Text is not SVG - cannot display + + + +
\ No newline at end of file diff --git a/java/request-contexts.md b/java/request-contexts.md index 8ae59273c..dffffdff3 100644 --- a/java/request-contexts.md +++ b/java/request-contexts.md @@ -20,15 +20,17 @@ uacp: Used as link target from Help Portal at https://help.sap.com/products/BTP/ When [events](../about/#events) are processed on [services](./consumption-api), [event context](provisioning-api#eventcontext) objects are used to store information related to a specific event. However, when processing an HTTP request in a protocol adapter or receiving an asynchronous event from a messaging system not only a single event is triggered. Other services, like the [Persistence Service](./consumption-api#persistenceservice) or additional technical services might be involved in processing. All of these services and their event handler need access to certain overarching metadata, such as user information, the selected locale, the tenant, and its (extended) CDS model or headers and query parameters. -The CAP Java SDK manages and exposes this kind of information by means of [RequestContext](https://www.javadoc.io/doc/com.sap.cds/cds-services-api/latest/com/sap/cds/services/request/RequestContext.html) instances. They define a scope that is typically determined by the context of a single HTTP request. The active Request Context can be accessed from the Event Context. However, those two are managed independently, as Event Contexts are passed along event handlers, while Request Contexts are maintained as thread-locals. +The CAP Java SDK manages and exposes this kind of information by means of [RequestContext](https://www.javadoc.io/doc/com.sap.cds/cds-services-api/latest/com/sap/cds/services/request/RequestContext.html) instances. They define a scope that is typically determined by the context of a single HTTP request. The active Request Context can be accessed from the Event Context. However, those two are managed independently, as Event Contexts are passed along event handlers, while Request Contexts are maintained as thread-locals. For example, the Persistence Service requires the tenant to be set correctly in the Request Context in order to access the tenant-specific persistence. -Inside an event handler, it's guaranteed that a Request Context is available. How to access the exposed information is described in detail in [Reading Request Contexts](#reading-requestcontext). +Inside an event handler, it's guaranteed that a Request Context is available. How to access the exposed information is described in detail in [Accessing Request Contexts](#reading-requestcontext). -Usually, the protocol adapter opens a *single* Request Context that makes the request's parameters available to CAP services used during request processing. In contrast, an OData `$batch` request sequentially opens *different* Request Contexts with divergent parameters for the different requests inside the batch. In general, it's possible to explicitly define (nested) Request Contexts and control their scope of validity. All events triggered within the same context also share the same parameters. This is described in detail in [Defining Request Contexts](#defining-requestcontext). +Usually, the protocol adapter opens a *single* Request Context that makes the request's parameters available to CAP services used during request processing. In contrast, an OData `$batch` request sequentially opens *different* Request Contexts with divergent parameters for the different requests inside the batch. This behaviour ensures that Event Handlers triggered by the individual requests can only access the relevant parameters. + +In general, it's possible to explicitly define (nested) Request Contexts and control their scope of validity. For example, in some cases an application might be required to break out from the current tenant to access a shared, tenant-independent persistence. All events triggered within the same context also share the same parameters. This is described in detail in [Defining Request Contexts](#defining-requestcontext). How to propagate Request Context instances to several threads is explained in [Passing Request Contexts to Threads](#threading-requestcontext), and [Registering Global Parameter Providers](#global-providers) shows how you can influence or even override the standard way of retrieving the parameters from the request. -## Reading Request Contexts { #reading-requestcontext} +## Accessing Request Contexts { #reading-requestcontext} The Request Context provides information about the request's parameters as well as the current user: - [UserInfo](https://www.javadoc.io/doc/com.sap.cds/cds-services-api/latest/com/sap/cds/services/request/UserInfo.html): Exposes an API to fetch data of the (authenticated) user such as the logon name, id, CAP roles, and the tenant. @@ -108,12 +110,82 @@ The same functionality is provided for arbitrary custom interfaces, which are ex For example, if the request is processed by an HTTP-based protocol adapter, `ParameterInfo` provides access to the HTTP request information. It exposes the [correlation ID](./observability#correlation-ids), the locale, the headers, and the query parameters of a request. [AuthenticationInfo](https://www.javadoc.io/doc/com.sap.cds/cds-services-api/latest/com/sap/cds/services/authentication/AuthenticationInfo.html) stores the authentication claims of the authenticated user. For instance, if OAuth2-based authentication is used, this is a JWT token (for example, XSUAA or IAS). You can call `is(Class)` to find the concrete `AuthenticationInfo` type. -[JwtTokenAuthenticationInfo](https://www.javadoc.io/doc/com.sap.cds/cds-services-api/latest/com/sap/cds/services/authentication/JwtTokenAuthenticationInfo.html) represents a JWT token, whereas [BasicAuthenticationInfo](https://www.javadoc.io/doc/com.sap.cds/cds-services-api/latest/com/sap/cds/services/authentication/BasicAuthenticationInfo.html) can be observed on requests with basic authentication (e.g. test scenario with mock users). The method `as(Class)` helps to perform the downcast to a concrete subtype. +[JwtTokenAuthenticationInfo](https://www.javadoc.io/doc/com.sap.cds/cds-services-api/latest/com/sap/cds/services/authentication/JwtTokenAuthenticationInfo.html) represents a JWT token, but [BasicAuthenticationInfo](https://www.javadoc.io/doc/com.sap.cds/cds-services-api/latest/com/sap/cds/services/authentication/BasicAuthenticationInfo.html) can be observed on requests with basic authentication (e.g. test scenario with mock users). The method `as(Class)` helps to perform the downcast to a concrete subtype. -## Defining Request Contexts { #defining-requestcontext} +## Defining New Request Contexts { #defining-requestcontext} The CAP Java SDK allows you to create new Request Contexts and define their scope. This helps you to control, which set of parameters is used when events are processed by services. -To manually add, modify or reset specific attributes within the scope of a new Request Context, you can use the [RequestContextRunner](https://www.javadoc.io/doc/com.sap.cds/cds-services-api/latest/com/sap/cds/services/runtime/RequestContextRunner.html) API: + +There are a few typical use cases in a CAP-based, multitenant application on SAP BTP in which creation of new Request Contexts is necessary. These scenarios are identified by a combination of the user (technical or named) and the tenant (provider or subscribed). + +A named user can switch to a technical user in the same/subscriber tenant using the systemUser() method. Also, a named user can switch to a technical user in the provider tenant using the systemUserProvider() method. In addition technical users provider/subscriber tenants can switch to technical users on provider/subscriber tenants using the methods systemUserProvider() or systemUser(tenant). + +When calling CAP Services, it's important to call them in an appropriate Request Context. Services might, for example, trigger HTTP requests to external services by deriving the target tenant from the current Request Context. + +The `RequestContextRunner` API offers convenience methods that allow an easy transition from one scenario to the other. + +| Method | Description | +|----------------------|--------------------------------------------------------------------------------------------------------------------------------------| +| systemUserProvider() | Switches to a technical user targeting the provider account. | +| systemUser() | Switches to a technical user and preserves the tenant from the current `UserInfo` (for example down grade of a named user Request Context). | +| systemUser(tenant) | Switches to a technical user targeting a given subscriber account. | +| anonymousUser() | Switches to an anonymous user. | +| privilegedUser() | Elevates the current `UserInfo` to by-pass all authorization checks. | + +::: info Note +The [RequestContextRunner](https://www.javadoc.io/doc/com.sap.cds/cds-services-api/latest/com/sap/cds/services/runtime/RequestContextRunner.html) API does not allow you to create a Request Context based on a named user. Named user contexts are only created by the CAP Java framework as initial Request Context is based on appropriate authentication information (e.g. JWT token) attached to the incoming HTTP request. +::: + +In the following a few concrete examples are given: +- [Switching to Technical User](#switching-to-technical-user) +- [Switching to Provider Tenant](#switching-to-provider-tenant) +- [Switching to a Specific Technical Tenant](#switching-to-a-specific-technical-tenant) + +### Switching to Technical User + +The graphic is explained in the accompanying text. + +The incoming JWT token triggers the creation of an initial RequestContext with a named user. Accesses to the database in the OData Adapter as well as the custom `on` handler are executed within tenant1 and authorization checks are performed for user JohnDoe. An additionally defined `after` handler wants to call out to an external service using a technical user without propagating the named user JohnDoe. +Therefore, the `after` handler needs to create a new Request Context. To achieve this, it's required to call `requestContext()` on the current `CdsRuntime` and use the `systemUser()` method to remove the named user from the new Request Context: + +```java +@After(entity = Books_.CDS_NAME) +public void afterHandler(EventContext context){ + runtime.requestContext().systemUser().run(reqContext -> { + // call technical service + ... + }); +} +``` +### Switching to Technical Provider Tenant + +The graphic is explained in the accompanying text. + +The application offers an action for one of its CDS entities. Within the action, a communication happens with a remote CAP service using an internal technical user from the provider account. The corresponding `on` handler of the action needs to create a new Request Context by calling `requestContext()`. Using the `systemUserProvider()` method, the existing user information is removed and the tenant is automatically set to the provider tenant. This allows the application to perform an HTTP call to the remote CAP service, which is secured using the pseudo-role `internal-user`. + +```java +@On(entity = Books_.CDS_NAME) +public void onAction(AddToOrderContext context){ + runtime.requestContext().systemUserProvider().run(reqContext -> { + // call remote CAP service + ... + }); +} +``` +### Switching to a Specific Technical Tenant + +The graphic is explained in the accompanying text. + +The application is using a job scheduler that needs to regularly perform tasks on behalf of a certain tenant. By default, background executions (for example in a dedicated thread pool) aren't associated to any subscriber tenant and user. In this case, it's necessary to explicitly define a new Request Context based on the subscribed tenant by calling `systemUser(tenantId)`. This ensures that the Persistence Service performs the query for the specified tenant. + +```java +runtime.requestContext().systemUser(tenant).run(reqContext -> { + return persistenceService.run(Select.from(Books_.class)).listOf(Books.class); +}); +``` +## Modifying Request Contexts { #modifying-requestcontext} + +Besides the described common use cases, it's possible to modify parts of an existing Request Context. To manually add, modify or reset specific attributes within the scope of a new Request Context, you can use the [RequestContextRunner](https://www.javadoc.io/doc/com.sap.cds/cds-services-api/latest/com/sap/cds/services/runtime/RequestContextRunner.html) API. ```java List readBooksNotLocalized(EventContext context) { @@ -125,7 +197,7 @@ List readBooksNotLocalized(EventContext context) { } ``` -In the example, executing the CQN Select query on the Persistence Service needs to run inside a Request Context without locale in order to retrieve unlocalized data. To achieve this, a new Request Context is explicitly created by calling `requestContext()` on the current `CdsRuntime`. The code being executed in the passed `java.util.function.Function` is triggered with the `run()` method. Before the execution, the newly created context that wraps the functional code, can be modified arbitrarily: +In the example, executing the CQN Select query on the Persistence Service needs to run inside a Request Context without locale in order to retrieve unlocalized data. Before the execution, the newly created context that wraps the functional code, can be modified arbitrarily: - `modifyParameters()`: Add, modify, or remove (single) parameters. - `clearParameters()`: Resets all parameters. @@ -133,21 +205,9 @@ In the example, executing the CQN Select query on the Persistence Service needs Similarly, it's possible to fully control the `UserInfo` instance provided in the RequestContext. It's guaranteed, that the original parameters aren't touched by the nested `RequestContext`. In addition, all original parameter values, which aren't removed or modified are visible in the nested scope. This enables you to either define the parameters from scratch or just to put a modification layer on top. -A common scenario is to use this API to set the tenant in an asynchronous thread: - -```java -context.getCdsRuntime().requestContext() - .modifyUser(u -> u.setTenant("my-tenant-id")) - .run(requestContext -> { - // queries will be executed in context of my-tenant-id - persistenceService.run(Select.from(Books_.class)).listOf(Books.class); - }); -``` - Some more examples: - `modifyUser(user -> user.removeRole("read").setTenant(null).run(...)`: Creates a context with a user that is similar to the outer context but without role `read` and tenant. -- `privilegedUser().run(...)`: Runs with a privileged user, that passes all authorization requirements. - `modifyParameters(param -> param.setHeader("MY-HEADER", "my value"))`: Adds a header parameter `MY-HEADER:my value`. The modifications can be combined arbitrarily in fluent syntax. @@ -198,7 +258,7 @@ public class HeaderBasedUserInfoProvider implements UserInfoProvider { } ``` -It's allowed to define several providers of the same type. In Spring, the provider with the lowest `@Order` will be called first. In plain Java the order is given by registration order. You can reuse the provider with lower priority and build a modified result. To accomplish this, remember the previous provider instance, which is passed during registration via `setPrevious()` method call. Such a chain of providers can be used to normalize user names or adjust user roles to match specific needs. +It's allowed to define several providers of the same type. In Spring, the provider with the lowest `@Order` is first. In plain Java, the order is given by registration order. You can reuse the provider with lower priority and build a modified result. To accomplish this, remember the previous provider instance, which is passed during registration via `setPrevious()` method call. Such a chain of providers can be used to normalize user names or adjust user roles to match specific needs. ```java @Component @@ -244,6 +304,6 @@ Future result = Executors.newSingleThreadExecutor().submit(() -> { }); ``` -Even though the `threadContext` variable is not directly used in the example, executing the `run` method takes care of populating the Request Context to the thread-local store of the child thread. The Persistence Service then internally uses the thread-local store to access the Request Context in order to access the currently active tenant. +Even though the `threadContext` variable isn't directly used in the example, executing the `run` method takes care of populating the Request Context to the thread-local store of the child thread. The Persistence Service then internally uses the thread-local store to access the Request Context in order to access the currently active tenant. You're free to modify the parameters by means of the API described in [Defining Request Contexts](#defining-requestcontext) in addition. But be aware that `providedParameters()` resp. `providedUser()` might lead to unexpected behavior as typically the [standard providers](#global-providers) require to run in the context of the original worker thread to access request-local data. From 00fce948dca96a9f6773830d134b554cd4422482 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Jeglinsky?= Date: Fri, 10 Nov 2023 11:56:22 +0100 Subject: [PATCH 25/43] Update request-contexts.md --- java/request-contexts.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/request-contexts.md b/java/request-contexts.md index dffffdff3..1eeb79f35 100644 --- a/java/request-contexts.md +++ b/java/request-contexts.md @@ -157,7 +157,7 @@ public void afterHandler(EventContext context){ }); } ``` -### Switching to Technical Provider Tenant +### Switching to Technical Provider Tenant {#switching-to-provider-tenant} The graphic is explained in the accompanying text. From 51223cb29db9ad1d54b31abfb855d4dfdae0ea43 Mon Sep 17 00:00:00 2001 From: Steffen Waldmann Date: Mon, 13 Nov 2023 10:28:21 +0100 Subject: [PATCH 26/43] Move old MTX troubleshooting items --- get-started/troubleshooting.md | 59 ++++++++++++++++------------------ 1 file changed, 28 insertions(+), 31 deletions(-) diff --git a/get-started/troubleshooting.md b/get-started/troubleshooting.md index a5e018145..3023184bb 100644 --- a/get-started/troubleshooting.md +++ b/get-started/troubleshooting.md @@ -324,37 +324,6 @@ You can apply this solution also when using the `cds-mtx` library. You can eithe - On trial landscapes, you need to use `hanatrial` instead of `hana` as service type: `cf create-service hanatrial ...` - When using the `cds-mtx` library with more than one SAP HANA database mapped to your Cloud Foundry space, you can add the service creation parameters via the environment variable `CDS_MTX_PROVISIONING_CONTAINER="{\"provisioning_parameters\":{\"database_id\":\"XXX\"}}"`, where `XXX` represents the ID of the database instance. You can also pass the ID of the database with the subscription request. -### I get errors with response code 429 from the service-manager service when subscribing a tenant -> This is valid for the 'old' MTX Services package `@sap/cds-mtx`. - -You can reduce the number of request by adapting the configuration of the `@sap/instance-manager` library. See also [`@sap/instance-manager` documentation](https://www.npmjs.com/package/@sap/instance-manager). - ```json - "cds": { - "mtx": { - "provisioning": { - "instancemanageroptions": { - "polling_interval_millis": 3000 - } - } - } - } - ``` - -### I get errors with response code 429 from the service-manager service when running a tenant upgrade for all tenants -> This is valid for the 'old' MTX Services package `@sap/cds-mtx`. - -You can disable the database clustering for the update. - ```json - "cds": { - "mtx": { - "jobs": { - "clusterbydb": false - } - } - } - ``` - This setting requires at least `@sap/cds-mtx@2.6.2`. - ### How Do I Resolve Deployment Errors? @@ -655,6 +624,34 @@ Alternatively, without login: cds extend … -s ``` +### I get errors with response code 429 from the service-manager service when subscribing a tenant + +You can reduce the number of request by adapting the configuration of the `@sap/instance-manager` library. See also [`@sap/instance-manager` documentation](https://www.npmjs.com/package/@sap/instance-manager). + ```json + "cds": { + "mtx": { + "provisioning": { + "instancemanageroptions": { + "polling_interval_millis": 3000 + } + } + } + } + ``` + +### I get errors with response code 429 from the service-manager service when running a tenant upgrade for all tenants + +You can disable the database clustering for the update. + ```json + "cds": { + "mtx": { + "jobs": { + "clusterbydb": false + } + } + } + ``` +This setting requires at least `@sap/cds-mtx@2.6.2`. ## CAP on Kyma From f357a67efb6258bf2857ebf2dba80c596e78a420 Mon Sep 17 00:00:00 2001 From: Rene Jeglinsky Date: Mon, 13 Nov 2023 12:16:46 +0100 Subject: [PATCH 27/43] added teched session --- get-started/learning-sources.md | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/get-started/learning-sources.md b/get-started/learning-sources.md index 598c57157..5bd054afe 100644 --- a/get-started/learning-sources.md +++ b/get-started/learning-sources.md @@ -127,10 +127,7 @@ The Sustainable SaaS (SusaaS) sample application has been built in a partner col - [Build a Business Application Using CAP for Node.js](https://developers.sap.com/mission.cp-starter-extensions-cap.html) - [Build a Business Application Using CAP for Java](https://developers.sap.com/mission.cap-java-app.html) -- -- By Enablement Teams -- TechEd Hands-Ons - +- [AD264 - Build Extensions with SAP Cloud Application Programming Model (CAP)](https://github.com/SAP-samples/teched2023-AD264/) ## Videos From 715e6eee31e9be276186d78d4866773965641c15 Mon Sep 17 00:00:00 2001 From: Steffen Waldmann Date: Mon, 13 Nov 2023 17:36:10 +0100 Subject: [PATCH 28/43] Better styling for `cds-plugins` (#538) --- plugins/index.md | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/plugins/index.md b/plugins/index.md index 41c665dd9..f42f1d55a 100644 --- a/plugins/index.md +++ b/plugins/index.md @@ -18,6 +18,17 @@ These plugins are created and maintained in close collaboration and shared owner font-style: italic; margin: -44px 0 40px; } + main .vp-doc a:has(> img) { + display: inline-flex; + align-items: center; + transition: opacity 0.2s; + } + main .vp-doc a:has(> img):hover { + opacity: 0.7; + } + main .vp-doc a:has(> img):not(:last-child) { + margin-right: 1em; + } @@ -64,9 +75,8 @@ The GraphQL Adapter is a protocol adapter that generically generates a GraphQL s Available for: -[](https://www.npmjs.com/package/@cap-js/graphql) +[](https://www.npmjs.com/package/@cap-js/graphql) -Click on the icon to get detailed instructions. {.learn-more} @@ -77,12 +87,12 @@ The OData v2 Proxy is a protocol adapter that allows you to expose your services Available for: -[](https://www.npmjs.com/package/@cap-js-community/odata-v2-adapter) -[](../java/migration#v2adapter) +[](https://www.npmjs.com/package/@cap-js-community/odata-v2-adapter) +[](../java/migration#v2adapter) Click on the icons to get detailed instructions. {.learn-more} -See also [_Advanced > OData APIs > V2 Support_](../advanced/odata#v2-support) {.learn-more} +See also [_Advanced →> OData APIs → V2 Support_](../advanced/odata#v2-support) {.learn-more} @@ -94,7 +104,7 @@ The UI5 Dev Server is a CDS server plugin that enables the integration of UI5 (U Available for: -[](https://www.npmjs.com/package/cds-plugin-ui5) +[](https://www.npmjs.com/package/cds-plugin-ui5) Click on the icon to get detailed instructions. {.learn-more} @@ -118,7 +128,7 @@ annotate my.Incidents { Available for: -[](https://npmjs.com/package/@cap-js/change-tracking) +[](https://npmjs.com/package/@cap-js/change-tracking) Click on the icon to get detailed instructions. {.learn-more} @@ -148,8 +158,8 @@ Features: Available for: -[](../guides/data-privacy/audit-logging) -[](../java/auditlog) +[](../guides/data-privacy/audit-logging) +[](../java/auditlog) Click on the icons to get detailed instructions. {.learn-more} @@ -180,7 +190,7 @@ Features: Available for: -[](https://github.com/cap-js/notifications#readme) +[](https://github.com/cap-js/notifications#readme) Click on the icon to get detailed instructions. {.learn-more} From 9e5b7cec87fe402f4692fbf77db2e58833fbbeba Mon Sep 17 00:00:00 2001 From: Steffen Waldmann Date: Mon, 13 Nov 2023 17:36:40 +0100 Subject: [PATCH 29/43] Simplify troubleshooting guide (#537) --- get-started/jumpstart.md | 2 -- get-started/troubleshooting.md | 24 +++++------------------- 2 files changed, 5 insertions(+), 21 deletions(-) diff --git a/get-started/jumpstart.md b/get-started/jumpstart.md index 5c9de3f53..f8a80a990 100644 --- a/get-started/jumpstart.md +++ b/get-started/jumpstart.md @@ -42,8 +42,6 @@ Choose the **LTS** version, via the left-hand side button: npm add -g @sap/cds-dk cds #> run the installed CLI ``` -[Running into problems? → See the troubleshooting guide.](troubleshooting#npm-installation){.learn-more} - ### 3. Install Git diff --git a/get-started/troubleshooting.md b/get-started/troubleshooting.md index 3023184bb..b90f6e53e 100644 --- a/get-started/troubleshooting.md +++ b/get-started/troubleshooting.md @@ -17,23 +17,9 @@ uacp: This page is linked from the Help Portal at https://help.sap.com/products/ ## General { #cds} -### How Do I Resolve Installation Issues with Node.js and NPM? { #npm-installation} - -##### Check the registry settings of your npm configuration - -Make sure that you don't have old registry entries anymore for `@sap:registry` in your _.npmrc_. Just execute: - -```sh -npm config delete "@sap:registry" -``` - -Type `npm config list` to check the configuration, which is stored in a file _.npmrc_ in the user's home directory. There, no `@sap:registry` should appear. - -[Learn more about the move to **npmjs.org** in the blog post by DJ Adams.](https://blogs.sap.com/2020/07/02/sap-npm-packages-now-on-npmjs.org/){.learn-more} - ##### Check the Node.js version { #node-version} -Make sure you run the latest long-term support (LTS) version of Node.js with an even number like `16`. Refrain from using odd versions, for which some modules with native parts will have no support and thus might even fail to install. Check version with: +Make sure you run the latest long-term support (LTS) version of Node.js with an even number like `20`. Refrain from using odd versions, for which some modules with native parts will have no support and thus might even fail to install. Check version with: ```sh node -v @@ -124,7 +110,7 @@ cds.on('served', ()=>{ }) ``` -It is important to note that by Node.js `emit` are synchronous operations, so, **avoid _any_ `await` operations** in there, as that might lead to race conditions. In particular, when registering additional event handlers with a service, as shown in the snippet above, this could lead to very heard to detect and resolve issues with handler registrations. So, for example, don't do this: +It is important to note that by Node.js `emit` are synchronous operations, so, **avoid _any_ `await` operations** in there, as that might lead to race conditions. In particular, when registering additional event handlers with a service, as shown in the snippet above, this could lead to very hard to detect and resolve issues with handler registrations. So, for example, don't do this: #### DON'T: @@ -138,17 +124,17 @@ cds.on('served', async ()=>{ ### My app isn't showing up in Dynatrace Make sure that: -- Your app's start script is `cds run` instead of `npx cds run`. +- Your app's start script is `cds-serve` instead of `npx cds run`. - You have the dependency `@dynatrace/oneagent-sdk` in your _package.json_. ### Why are requests occasionally rejected with "Acquiring client from pool timed out" or "ResourceRequest timed out"? -This error indicates, that the settings of the pool containing the database clients don't match the application's needs. There are two possible root causes. +This error indicates database client pool settings don't match the application's requirements. There are two possible root causes: | | Explanation | | --- | ---- | | _Root Cause 1_ | The maximum number of database clients in the pool is reached and additional requests wait too long for the next client. -| _Root Cause 2_ | The amount of time for creating a new connection to the database takes too long. +| _Root Cause 2_ | The creation of a new connection to the database takes too long. | _Solution_ | Adapt `max` or `acquireTimeoutMillis` with more appropriate values, according to the [documentation](../node.js/databases#databaseservice-configuration). Always make sure that database transactions are either committed or rolled back. This can work in two ways: From 43519ec24bba506a99017f8b5f61bb9e70615528 Mon Sep 17 00:00:00 2001 From: Rene Jeglinsky Date: Tue, 14 Nov 2023 12:17:52 +0100 Subject: [PATCH 30/43] added redirect --- guides/domain-modeling.md | 1 + 1 file changed, 1 insertion(+) diff --git a/guides/domain-modeling.md b/guides/domain-modeling.md index 2997b849d..3c36a5a66 100644 --- a/guides/domain-modeling.md +++ b/guides/domain-modeling.md @@ -2,6 +2,7 @@ synopsis: > Most projects start with capturing the essential objects of their domain in a respective domain model. Find here an introduction to the basics of domain modeling with CDS, complemented with recommended best practices. +redirect_from: guides/domain-models status: released --- From c2cf5bf83b4d911c9e8b4eaf0c2db1a453178d22 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 14 Nov 2023 13:28:09 +0100 Subject: [PATCH 31/43] chore(deps): update cap java sdk to v2.4.0 (#540) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .vitepress/config.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.vitepress/config.ts b/.vitepress/config.ts index d310fa46d..442a237a6 100644 --- a/.vitepress/config.ts +++ b/.vitepress/config.ts @@ -21,8 +21,8 @@ if (!siteURL.pathname.endsWith('/')) siteURL.pathname += '/' const redirectLinks: Record = {} const latestVersions = { - java_services: '2.3.1', - java_cds4j: '2.3.1' + java_services: '2.4.0', + java_cds4j: '2.4.0' } const localSearchOptions = { From ef7b8ef5034372faa05eb86c72f9220de6e486ad Mon Sep 17 00:00:00 2001 From: Matthias Kuhr <52661546+MatKuhr@users.noreply.github.com> Date: Tue, 14 Nov 2023 19:03:58 +0100 Subject: [PATCH 32/43] Update Remote Services Docs with Recent Cloud SDK Improvements (#534) * Update for Cloud SDK 4.24+ * Update remote-services.md * Update java/remote-services.md * Update java/remote-services.md * Update java/remote-services.md * Update java/remote-services.md Co-authored-by: Matthias Kuhr <52661546+MatKuhr@users.noreply.github.com> --------- Co-authored-by: Marc Becker --- java/remote-services.md | 41 ++++++++++++++--------------------------- 1 file changed, 14 insertions(+), 27 deletions(-) diff --git a/java/remote-services.md b/java/remote-services.md index 601d2cff4..2d2f0acc5 100644 --- a/java/remote-services.md +++ b/java/remote-services.md @@ -120,10 +120,6 @@ In this case, the destination with name `s4-business-partner-api` would be obtai Given that this destination holds the URL `https://s4.sap.com`, the resulting service URL for OData requests would be `https://s4.sap.com/sap/opu/odata/sap/API_BUSINESS_PARTNER`. The `type` property defines the protocol used by the remote API. The CAP Java SDK currently supports `odata-v4` (default) or `odata-v2`. -::: warning -When using SAP S/4HANA On-Premise via Cloud Connector with a non-default sap-client, you might observe 401 Unauthorized errors. -This is a known issue and can be avoided by adding an additional property `URL.headers.sap-client` with the value of the sap-client to the destination in the SAP BTP Destination Service's configuration. -::: #### Configuring Destination Strategies @@ -279,36 +275,27 @@ DefaultHttpDestination #### OAuth2 Client Credentials { #oauth2-client-credentials} ```java -DefaultHttpDestination httpDestination = DefaultHttpDestination - .builder("https://example.org") - .name("my-destination").build(); - ClientCredentials clientCredentials = new ClientCredentials("clientid", "clientsecret"); -ClientCredentialsHttpDestination clientCredentialsHttpDestination = - new ClientCredentialsHttpDestination( - httpDestination, - clientCredentials, - URI.create("https://xsuaa.url"), - new XsuaaService() - ); + +OAuth2DestinationBuilder + .forTargetUrl("https://example.org") + .withTokenEndpoint("https://xsuaa.url") + .withClient(clientCredentials, OnBehalfOf.TECHNICAL_USER_CURRENT_TENANT) + .withProperties(Map.of("name", "my-destination")) + .build(); ``` #### User Token Authentication { #user-token-authentication} ```java -DefaultHttpDestination httpDestination = DefaultHttpDestination - .builder("https://example.org") - .name("my-destination").build(); - ClientCredentials clientCredentials = new ClientCredentials("clientid", "clientsecret"); -ClientCredentialsHttpDestination clientCredentialsHttpDestination = - new ClientCredentialsHttpDestination( - httpDestination, - clientCredentials, - ClientCredentialsGrantType.USER_TOKEN, - URI.create("https://xsuaa.url"), - new XsuaaService() - ); + +OAuth2DestinationBuilder + .forTargetUrl("https://example.org") + .withTokenEndpoint("https://xsuaa.url") + .withClient(clientCredentials, OnBehalfOf.NAMED_USER_CURRENT_TENANT) + .withProperties(Map.of("name", "my-destination")) + .build(); ``` From 2b6f89a41ed084f93c2734c6195c2029859585b3 Mon Sep 17 00:00:00 2001 From: Matthias Schur <107557548+MattSchur@users.noreply.github.com> Date: Tue, 14 Nov 2023 19:11:55 +0100 Subject: [PATCH 33/43] Java typed entity refs (#542) --- java/query-api.md | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/java/query-api.md b/java/query-api.md index 34ad33544..62ed1f906 100644 --- a/java/query-api.md +++ b/java/query-api.md @@ -1212,17 +1212,20 @@ The Query Builder API supports using expressions in many places. Expressions con ### Entity References {#entity-refs} -Entity references specify entity sets. They can be used to define the target entity set of a [CQL](../cds/cql) statement. They can either be defined inline using lambda expressions in the Query Builder (see [Target Entity Sets](#target-entity-sets)) or via the `CQL.entity` method. The following example shows an entity reference describing the set of *authors* that have published books in the year 2020: +Entity references specify entity sets. They can be used to define the target entity set of a [CQL](../cds/cql) statement. They can either be defined inline using lambda expressions in the Query Builder (see [Target Entity Sets](#target-entity-sets)) or via the `CQL.entity` method, which is available in an _untyped_ version as well as in a _typed_ version that uses the generated [model interfaces](../java/advanced#model-interfaces). The following example shows an entity reference describing the set of *authors* that have published books in the year 2020: ```java -import static com.sap.cds.ql.CQL.entity; +import com.sap.cds.ql.CQL; + +// bookshop.Books[year = 2020].author // [!code focus] +Authors_ authors = CQL.entity(Books_.class).filter(b -> b.year().eq(2020)).author(); // [!code focus] -// bookshop.Books[year = 2020].author +// or as untyped entity ref StructuredType authors = - entity("bookshop.Books").filter(b -> b.get("year").eq(2020)).to("author"); + CQL.entity("bookshop.Books").filter(b -> b.get("year").eq(2020)).to("author"); -// SELECT from bookshop.Books[year = 2020].author { name } -Select.from(authors).columns("name"); +// SELECT from bookshop.Books[year = 2020].author { name } // [!code focus] +Select.from(authors).columns("name"); // [!code focus] ``` You can also get [entity references](query-execution#entity-refs) from the result of a CDS QL statement to address an entity via its key values in other statements. From acef96aaabca5b90ad88f8708607a9c0199503c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrian=20G=C3=B6rler?= Date: Tue, 14 Nov 2023 20:15:13 +0100 Subject: [PATCH 34/43] Java: Config for SAP HANA HEX Engine (#526) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update persistence-services.md Document config for SAP HANA HEX Engine * Update java/persistence-services.md * Apply suggestions from code review Co-authored-by: Matthias Schur <107557548+MattSchur@users.noreply.github.com> * Update java/persistence-services.md --------- Co-authored-by: René Jeglinsky Co-authored-by: Matthias Schur <107557548+MattSchur@users.noreply.github.com> --- java/persistence-services.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/java/persistence-services.md b/java/persistence-services.md index 8804c1a0b..41e6de6cd 100644 --- a/java/persistence-services.md +++ b/java/persistence-services.md @@ -121,10 +121,25 @@ cds: ### SAP HANA +#### Service Bindings + SAP HANA can be configured when running locally as well as when running productively in the cloud. The datasource is auto-configured based on available service bindings in the `VCAP_SERVICES` environment variable or locally the _default-env.json_. This only works if an application profile is used, that doesn't explicitly configure a datasource using `spring.datasource.url`. Such an explicit configuration always takes precedence over service bindings from the environment. Service bindings of type *service-manager* and, in a Spring-based application, *hana* are used to auto-configure datasources. If multiple datasources are used by the application, you can select one auto-configured datasource to be used by the default Persistence Service through the property `cds.dataSource.binding`. +#### SQL Optimization Mode + +By default, the SAP HANA adapter in CAP Java generates SQL that is compatible with SAP HANA 2.x ([HANA Service](https://help.sap.com/docs/HANA_SERVICE_CF/6a504812672d48ba865f4f4b268a881e/08c6e596b53843ad97ae68c2d2c237bc.html)) and [SAP HANA Cloud](https://www.sap.com/products/technology-platform/hana.html). +To generate SQL that is optimized for the new [HEX engine](https://help.sap.com/docs/SAP_HANA_PLATFORM/9de0171a6027400bb3b9bee385222eff/3861d0908ef14e8bbec1d76ea871ac0f.html#sap-hana-execution-engine-(hex)) in SAP HANA Cloud, set the [CDS property](development/properties#cds-properties): + +```yaml +cds.sql.hana.optimizationMode: hex +``` + +:::tip +Use the [hints](../java/query-execution#hana-hints) `hdb.USE_HEX_PLAN` and `hdb.NO_USE_HEX_PLAN` to overrule the configured optimization mode per statement. +::: + ### PostgreSQL { #postgresql-1 } PostgreSQL can be configured when running locally as well as when running productively in the cloud. Similar to HANA, the datasource is auto-configured based on available service bindings, if the feature `cds-feature-postgresql` is added. From cea3a2973cab6f05b206b0a26c694d78737961cd Mon Sep 17 00:00:00 2001 From: Matthias Schur <107557548+MattSchur@users.noreply.github.com> Date: Tue, 14 Nov 2023 20:41:47 +0100 Subject: [PATCH 35/43] Java Runtime Views (#541) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Java Runtime Views * Apply suggestions from code review Co-authored-by: Adrian Görler * move to query-execution * Update advanced.md * Update java/query-execution.md Co-authored-by: René Jeglinsky --------- Co-authored-by: Adrian Görler Co-authored-by: René Jeglinsky --- java/query-execution.md | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/java/query-execution.md b/java/query-execution.md index 706153747..2be46eb8d 100644 --- a/java/query-execution.md +++ b/java/query-execution.md @@ -275,6 +275,45 @@ entity DeliveredOrders as select from bookshop.Order where status = 'delivered'; entity Orders as SELECT from bookshop.Order inner join bookshop.OrderHeader on Order.header.ID = OrderHeader.ID { Order.ID, Order.items, OrderHeader.status }; ``` +## Runtime Views { #runtimeviews} + +The CDS compiler generates [SQL DDL](../guides/databases?impl-variant=java#generating-sql-ddl) statements based on your CDS model, which include SQL views for all CDS [views and projections](../cds/cdl#views-and-projections). This means adding or changing CDS views requires a deployment of the database schema changes. + +To avoid schema updates due to adding or updating CDS views, annotate them with [@cds.persistence.skip](../guides/databases#cds-persistence-skip). In this case the CDS compiler won't generate corresponding static database views. Instead, the CDS views are dynamically resolved by the CAP Java runtime. + +```cds +entity Books { + key id : UUID; + title : String; + stock : Integer; + author : Association to one Authors; +} +@cds.persistence.skip // [!code focus] +entity BooksWithLowStock as projection on Books { // [!code focus] + id, title, author.name as author // [!code focus] +} where stock < 10; // [!code focus] +``` + +At runtime, CAP Java resolves queries against runtime views until an entity is reached that isn't annotated with *@cds.persistence.skip*. For example, the CQL query + +```sql +Select BooksWithLowStock where author = 'Kafka' +``` + +is executed against SQL databases as + +```SQL +SELECT B.ID, B.TITLE, A.NAME as "author" FROM BOOKS B + LEFT OUTER JOIN AUTHORS A ON B.AUTHOR_ID = A.ID +WHERE B.STOCK < 10 AND A.NAME = ? +``` + +::: tip +Runtime views are supported for [CDS projections](../cds/cdl#as-projection-on). Constant values and expressions such as *case when* are currently ignored. + +Complex views using aggregations or union/join/subqueries in `FROM` are not yet supported. +::: + ### Using I/O Streams in Queries As described in section [Predefined Types](./data#predefined-types) it's possible to stream the data, if the element is annotated with `@Core.MediaType`. The following example demonstrates how to allocate the stream for element `coverImage`, pass it through the API to an underlying database and close the stream. From 7d355c207d2ed6c3b2a5d643013563783def2bb4 Mon Sep 17 00:00:00 2001 From: Rene Jeglinsky Date: Wed, 15 Nov 2023 12:28:44 +0100 Subject: [PATCH 36/43] mark as beta --- get-started/learning-sources.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/get-started/learning-sources.md b/get-started/learning-sources.md index 5bd054afe..f723b3ebb 100644 --- a/get-started/learning-sources.md +++ b/get-started/learning-sources.md @@ -139,12 +139,12 @@ The Sustainable SaaS (SusaaS) sample application has been built in a partner col - [Surviving and Thriving with the SAP Cloud Application Programming Model -
a Series of Blog Post by Max Streifeneder (2023). ](https://blogs.sap.com/tag/captricks/) - [Multitenant SaaS applications on SAP BTP using CAP? Tried-and-True! -
by Martin Frick (2022).](https://blogs.sap.com/2022/10/19/multitenant-saas-applications-on-sap-btp-using-cap-tried-and-true/) -## Courses +## Courses {.impl .beta} - OpenSAP - -## CAP Plugins +## CAP Plugins {.impl .beta} - CAP community From a294f105bf8c7c8d436cbd405a2d8c648183df25 Mon Sep 17 00:00:00 2001 From: Nico Schoenteich Date: Wed, 15 Nov 2023 12:53:01 +0100 Subject: [PATCH 37/43] Typo (#544) --- guides/domain-modeling.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/guides/domain-modeling.md b/guides/domain-modeling.md index 3c36a5a66..171086bd1 100644 --- a/guides/domain-modeling.md +++ b/guides/domain-modeling.md @@ -16,7 +16,7 @@ Domain Models capture the static, data-related aspects of a problem domain in te ### Capture Intent — *What, not How!* -CDS focuses on *conceptual modelling*: we want to capure intent, not imperative implementations — that is: What, not How. Not only does that keep domain models concise and comprehensible, it also allows us to provide optimized generic implementations. +CDS focuses on *conceptual modelling*: we want to capture intent, not imperative implementations — that is: What, not How. Not only does that keep domain models concise and comprehensible, it also allows us to provide optimized generic implementations. For example, given an entity definition like that: From ef661b076b120562d69aedbfd31cf86795a6f623 Mon Sep 17 00:00:00 2001 From: Rene Jeglinsky Date: Wed, 15 Nov 2023 13:01:27 +0100 Subject: [PATCH 38/43] no empty sections --- get-started/learning-sources.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/get-started/learning-sources.md b/get-started/learning-sources.md index f723b3ebb..48a6066a9 100644 --- a/get-started/learning-sources.md +++ b/get-started/learning-sources.md @@ -139,12 +139,12 @@ The Sustainable SaaS (SusaaS) sample application has been built in a partner col - [Surviving and Thriving with the SAP Cloud Application Programming Model -
a Series of Blog Post by Max Streifeneder (2023). ](https://blogs.sap.com/tag/captricks/) - [Multitenant SaaS applications on SAP BTP using CAP? Tried-and-True! -
by Martin Frick (2022).](https://blogs.sap.com/2022/10/19/multitenant-saas-applications-on-sap-btp-using-cap-tried-and-true/) -## Courses {.impl .beta} + From 796a050b0c3b1031db7ed4b4d7362cd3a65b1a33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Jeglinsky?= Date: Fri, 17 Nov 2023 10:33:03 +0100 Subject: [PATCH 39/43] Java: remove system user, user should not exist (#547) remove system user, user should not exist --- get-started/troubleshooting.md | 10 ++++++++++ java/security.md | 9 +-------- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/get-started/troubleshooting.md b/get-started/troubleshooting.md index b90f6e53e..32b80d474 100644 --- a/get-started/troubleshooting.md +++ b/get-started/troubleshooting.md @@ -188,6 +188,16 @@ module.exports = cds.server A new option `privilegedUser()` can be leveraged when [defining](../java/request-contexts#defining-requestcontext) your own `RequestContext`. Adding this introduces a user, which passes all authorization restrictions. This is useful for scenarios, where a restricted service should be called through the [local service consumption API](../java/consumption-api) either in a request thread regardless of the original user's authorizations or in a background thread. +### Why do I get a "User should not exist" error during build time? + +| | Explanation | +| --- | ---- | +| _Root Cause_ | You've [explicitly configured a mock](../java/security#explicitly-defined-mock-users) user with a name that is already used by a [preconfigured mock user](../java/security#preconfigured-mock-users). +| _Solution_ | Rename the mock user and build your project again. + + + + ### How can I expose custom REST APIs with CAP? From time to time you might want to expose additional REST APIs in your CAP application, that aren't covered through CAPs existing protocol adapters (for example, OData V4). A common example for this might be a CSV file upload or another type of custom REST endpoint. diff --git a/java/security.md b/java/security.md index 0dd442a86..71f69af25 100644 --- a/java/security.md +++ b/java/security.md @@ -263,7 +263,7 @@ cds: additional: email: myviewer@crazycars.com features: - - cruise + - cruise - park - name: Privileged-User @@ -271,17 +271,10 @@ cds: privileged: true features: - "*" - - - name: System - password: system-pass - system-user: true - roles: - - mtcallback ``` - Mock user with name `Viewer-User` is a typical business user with SaaS-tenant `CrazyCars` who has assigned role `Viewer` and user attribute `Country` (`$user.Country` evaluates to value list `[GER, FR]`). This user also has the additional attribute `email`, which can be retrieved with `UserInfo.getAdditionalAttribute("email")`. The [features](../java/reflection-api#feature-toggles) `cruise` and `park` are enabled for this mock user. - `Privileged-User` is a user running in privileged mode. Such a user is helpful in tests that bypasses all authorization handlers. -- Technical user `System` can be used, for example, to simulate SaaS registry calls for tenant provisioning in a multitenancy scenario. Property `cds.security.mock.enabled = false` disables any mock user configuration. From 830b41cb9ab5838a6d790213bba336fff6755c77 Mon Sep 17 00:00:00 2001 From: Rene Jeglinsky Date: Fri, 17 Nov 2023 11:37:53 +0100 Subject: [PATCH 40/43] add fragment --- guides/authorization.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/guides/authorization.md b/guides/authorization.md index 6cc40aab8..7c7c0c358 100644 --- a/guides/authorization.md +++ b/guides/authorization.md @@ -112,14 +112,14 @@ For XSUAA or IAS authentication, the request user is attached with the pseudo ro ::: #### internal-user -Pseudo-role `internal-user` allows to define application endpoints that can be accessed exclusively by the own PaaS tenant (technical communication). The advantage is that similar to `system-user` no technical CAP roles need to be defined to protect such internal endpoints. However, in contrast to `system-user`, the endpoints protected by this pseudo-role do not allow requests from any external technical clients. Hence is suitable for **technical intra-application communication**, see [Security > Application Zone](../guides/security/overview#application-zone). +Pseudo-role `internal-user` allows to define application endpoints that can be accessed exclusively by the own PaaS tenant (technical communication). The advantage is that similar to `system-user` no technical CAP roles need to be defined to protect such internal endpoints. However, in contrast to `system-user`, the endpoints protected by this pseudo-role do not allow requests from any external technical clients. Hence is suitable for **technical intra-application communication**, see [Security > Application Zone](../guides/security/overview#application-zone). ::: tip For XSUAA or IAS authentication, the request user is attached with the pseudo role `internal-user` if the presented JWT token has been issued with grant type `client_credentials` or `client_x509` on basis of the **identical** XSUAA or IAS service instance. ::: ::: warning -All technical clients that have access to the application's XSUAA or IAS service instance can call your service endpoints as `internal-user`. +All technical clients that have access to the application's XSUAA or IAS service instance can call your service endpoints as `internal-user`. **Refrain from sharing this service instance with untrusted clients**, for instance by passing services keys or [SAP BTP Destination Service](https://help.sap.com/docs/connectivity/sap-btp-connectivity-cf/create-destinations-from-scratch) instances. ::: @@ -857,6 +857,7 @@ If generic enforcement doesn't fit your needs, you can override or adapt it with - [Authorization Enforcement in Node.js](../node.js/authentication#enforcement) - [Enforcement API & Custom Handlers in Java](../java/security#enforcement-api) +
## Role Assignments with XSUAA { #xsuaa-configuration} From cc5b758b9a329d68bfa1d80802c41f5dede78748 Mon Sep 17 00:00:00 2001 From: Daniel Hutzel Date: Fri, 17 Nov 2023 16:35:38 +0100 Subject: [PATCH 41/43] Added a few more Learning sources --- get-started/learning-sources.md | 35 ++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/get-started/learning-sources.md b/get-started/learning-sources.md index 48a6066a9..7aecc445d 100644 --- a/get-started/learning-sources.md +++ b/get-started/learning-sources.md @@ -7,10 +7,8 @@ status: released In here, we collected several interesting learning resources for you. Not all of them are maintained by the CAP team, not all of them cover CAP in its entirety but they are well prepared sources we can recommend for your learning. From the short description we provide for every resources, you're hopefully able to tell if that fits to the need you're currently having. -:::info -We're just getting started with this page. - -Feel free to add/propose resources that helped you and also to improve the descriptions. +::: tip Contributions Welcome... +We're just getting started with this page. Please help us in that endeavour by adding/proposing resources that helped you and also to improve the descriptions. ::: @@ -82,7 +80,7 @@ Available for: ### Incidents Mgmt {.github} -A reference sample application for CAP and development recommendations provided by the SAP BTP Developer Guide. +A reference sample application for CAP and the SAP BTP Developer Guide. Available for: @@ -90,13 +88,9 @@ Available for: -### SFlight Fiori {.github} - -The purpose of this sample app is to: +### SFlight Fiori App {.github} -- Demonstrate SAP Fiori annotations -- Demonstrate and compare SAP Fiori features on various stacks (CAP Node.js, CAP Java SDK, ABAP) -- Run UI test suites on various stacks +This sample is a CAP adaptation of the popular [SFLIGHT](https://blog.sap-press.com/what-is-sflight-and-the-flight-and-booking-data-model-for-abap) sample app in ABAP. It is a great source for how to add SAP Fiori applications to a CAP project, including adding UI test suites on various stacks. Available for: @@ -105,7 +99,7 @@ Available for: -### Star Wars {.github} +### Star Wars App {.github} SWAPI - the Star Wars API. This sample is based upon the sample at [swapi.dev](https://swapi.dev) which in turn was based upon [swapi.co](https://swapi.dev/about). The original source can be found at https://github.com/Juriy/swapi. @@ -115,6 +109,8 @@ Available for: - [](https://github.com/SAP-samples/cloud-cap-hana-swapi) {._nodejs} + + ### BTP SaaS App {.github} The Sustainable SaaS (SusaaS) sample application has been built in a partner collaboration to help interested developers, partners, and customers in developing multitenant Software as a Service applications using CAP and deploying them to the SAP Business Technology Platform (SAP BTP). @@ -125,19 +121,26 @@ The Sustainable SaaS (SusaaS) sample application has been built in a partner col ## Tutorials +- [TechEd 2023 Hands-On Session AD264 – Build Extensions with CAP](https://github.com/SAP-samples/teched2023-AD264/) - [Build a Business Application Using CAP for Node.js](https://developers.sap.com/mission.cp-starter-extensions-cap.html) - [Build a Business Application Using CAP for Java](https://developers.sap.com/mission.cap-java-app.html) -- [AD264 - Build Extensions with SAP Cloud Application Programming Model (CAP)](https://github.com/SAP-samples/teched2023-AD264/) +- [CAP Service Intefration CodeJam](https://github.com/sap-samples/cap-service-integration-codejam) by DJ Adams + ## Videos -- [Devtoberfest Session about Hybrid Testing and Alternative DBs by Thomas Jung.](https://youtu.be/vqub4vJbZX8?si=j5ZkPR6vPb59iBBy) +- [Hybrid Testing and Alternative DBs](https://youtu.be/vqub4vJbZX8?si=j5ZkPR6vPb59iBBy)
by Thomas Jung +- [Consume External Services](https://youtu.be/rWQFbXFEr1M)
by Thomas Jung +- [Building a CAP app in 60 min](https://youtu.be/zoJ7umKZKB4)
by Martin Stenzig +- [Integrating an external API into a CAP service](https://youtu.be/T_rjax3VY2E)
by DJ Adams + + ## Blogs -- [Surviving and Thriving with the SAP Cloud Application Programming Model -
a Series of Blog Post by Max Streifeneder (2023). ](https://blogs.sap.com/tag/captricks/) -- [Multitenant SaaS applications on SAP BTP using CAP? Tried-and-True! -
by Martin Frick (2022).](https://blogs.sap.com/2022/10/19/multitenant-saas-applications-on-sap-btp-using-cap-tried-and-true/) +- [Surviving and Thriving with the SAP Cloud Application Programming Model](https://blogs.sap.com/tag/captricks/)
by Max Streifeneder (2023) +- [Multitenant SaaS applications on SAP BTP using CAP? Tried-and-True!](https://blogs.sap.com/2022/10/19/multitenant-saas-applications-on-sap-btp-using-cap-tried-and-true/)
by Martin Frick (2022)