Skip to content

Commit 2bf3c87

Browse files
February Release (#1684)
2 parents 6af9df0 + d5c727f commit 2bf3c87

51 files changed

Lines changed: 840 additions & 550 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.vitepress/config.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,8 +101,8 @@ config.rewrites = rewrites
101101
// Add custom capire info to the theme config
102102
config.themeConfig.capire = {
103103
versions: {
104-
java_services: '3.7.1',
105-
java_cds4j: '3.7.0'
104+
java_services: '3.8.0',
105+
java_cds4j: '3.8.0'
106106
},
107107
gotoLinks: [],
108108
maven_host_base: 'https://repo1.maven.org/maven2'

.vitepress/languages/log.tmLanguage.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@
2525
"name": "comment.indented",
2626
"match": "^\\s+(?!___________________________)(.*)"
2727
},
28+
{
29+
"name": "comment.code",
30+
"match": "//\\s*\\[\\!code.*$"
31+
},
2832
{
2933
"begin": "\\{",
3034
"end": "\\}",

.vitepress/theme/components/ConfigInspect.vue

Lines changed: 32 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,13 @@
1515
</div>
1616
<div class="vp-code-group vp-doc" v-else>
1717
<CodeGroup :groups="[
18-
{ id: 'pkg-priv', label: '~/.cdsrc.json', lang: 'json', group, code: pkgStr, private: true },
19-
{ id: 'pkg', label: 'package/.cdsrc.json', lang: 'json', group, code: pkgStr },
20-
{ id: 'js', label: '.cdsrc.js', lang: 'js', group, code: jsStr },
21-
{ id: 'yml', label: '.cdsrc.yaml', lang: 'yml', group, code: ymlStr },
18+
{ id: 'pkg-rc', label: 'package.json', lang: 'json', group, code: pkgStr },
19+
{ id: 'pkg-priv', label: '~/.cdsrc.json', lang: 'json', group, code: rcJsonStr, private: true },
20+
{ id: 'pkg', label: '.cdsrc.json', lang: 'json', group, code: rcJsonStr },
21+
{ id: 'js', label: '.cdsrc.js', lang: 'js', group, code: rcJsStr },
22+
{ id: 'yml', label: '.cdsrc.yaml', lang: 'yml', group, code: rcYmlStr },
2223
{ id: 'env', label: '.env file', lang: 'properties', group, code: propStr },
23-
{ id: 'shl', label: 'Linux/macOS Shells', lang: 'sh', group, code: envStr, transient: true },
24+
{ id: 'shl', label: 'Linux/macOS Shells', lang: 'sh', group, code: 'export '+envStr, transient: true },
2425
{ id: 'shp', label: 'Powershell', lang: 'powershell', group, code: '$Env:'+envStr, transient: true },
2526
{ id: 'shw', label: 'Cmd Shell', lang: 'cmd', group, code: 'set '+envStr, transient: true }
2627
]" />
@@ -53,19 +54,23 @@
5354
h('input', { type: 'radio', name: 'group', id: `${b.group}-${b.id}`, checked: idx === 0 }),
5455
h('label', { for: `${b.group}-${b.id}` }, b.label)
5556
])),
56-
h('div', { class: 'blocks' }, groups.flatMap((b, idx) => [
57-
h('div', { class: ['language-'+b.lang, idx === 0 ? 'active': ''] }, [
58-
h('button', { title: 'Copy Code', class: 'copy' }),
59-
h('span', { class: 'lang' }, b.lang),
60-
h('pre', { class: 'shiki' },
61-
h('code',
62-
h('span', { class: 'line' },
63-
h('span', b.code)
57+
h('div', { class: 'blocks' }, groups
58+
.filter((b) => filesOnly ? !b.transient : true)
59+
.filter((b) => showPrivate ? true : !b.private)
60+
.flatMap((b, idx) => [
61+
h('div', { class: ['language-'+b.lang, idx === 0 ? 'active': ''] }, [
62+
h('button', { title: 'Copy Code', class: 'copy' }),
63+
h('span', { class: 'lang' }, b.lang),
64+
h('pre', { class: 'shiki' },
65+
h('code',
66+
h('span', { class: 'line' },
67+
h('span', b.code)
68+
)
6469
)
6570
)
66-
)
67-
])
68-
]))
71+
])
72+
]
73+
))
6974
], {
7075
props: {
7176
groups: { type: Array<{id:string, group:string, code:string, label:string, lang:string, transient?:boolean, private?:boolean }>, required: true }
@@ -85,8 +90,9 @@
8590
const popperVisible = ref(false)
8691
const group = ref()
8792
const pkgStr = ref()
88-
const jsStr = ref()
89-
const ymlStr = ref()
93+
const rcJsonStr = ref()
94+
const rcJsStr = ref()
95+
const rcYmlStr = ref()
9096
const propStr = ref()
9197
const envStr = ref()
9298
const javaAppyml = ref()
@@ -111,11 +117,16 @@
111117
const pkg = toJson(key, jsonVal ?? value)
112118
113119
pkgStr.value = JSON.stringify(pkg, null, 2)
114-
jsStr.value = 'module.exports = ' + pkgStr.value.replace(/"(\w*?)":/g, '$1:')
120+
rcJsonStr.value = JSON.stringify(pkg.cds, null, 2)
121+
rcJsStr.value = 'module.exports = ' + rcJsonStr.value.replace(/"(\w*?)":/g, '$1:')
122+
rcYmlStr.value = yaml.stringify(pkg.cds)
115123
propStr.value = `${key}=${jsonVal ? JSON.stringify(jsonVal) : value}`
116-
envStr.value = `${key.replaceAll('_', '__').replaceAll('.', '_').toUpperCase()}=${jsonVal ? JSON.stringify(jsonVal) : value}`
117124
118-
javaAppyml.value = ymlStr.value = yaml.stringify(pkg)
125+
let envKey = key.replaceAll('_', '__').replaceAll('.', '_')
126+
if (/^[a-z_]+$/.test(envKey)) envKey = envKey.toUpperCase() // only uppercase if not camelCase
127+
envStr.value = `${envKey}=${jsonVal ? JSON.stringify(jsonVal) : value}`
128+
129+
javaAppyml.value = yaml.stringify(pkg)
119130
javaEnvStr.value = `-D${propStr.value}`
120131
})
121132

.vitepress/theme/styles.scss

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@
4545

4646
--vp-c-text-1: #000;
4747
.VPHomeHero {
48-
h1 { line-height: 1.2em; }
48+
h1 span.name { line-height: 1.2em; }
4949
}
5050
h1, h2, h3, h4, h5 { color: rgba(60, 60, 67) }
5151
h6 { color: transparent; position: absolute; margin-top: -7.7em; right: 0px; } // we use h6 for alternative anchor targets

advanced/fiori.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -454,7 +454,7 @@ Adding the annotation `@fiori.draft.enabled` won't work if the corresponding `_t
454454
![An SAP Fiori UI showing how a book is edited in the bookshop sample and that the translations tab is used for non-standard languages.](../assets/draft-for-localized-data.png){style="margin:0"}
455455
[See it live in **cap/samples**.](https://github.com/sap-samples/cloud-cap-samples/tree/main/fiori/app/admin-books/fiori-service.cds#L50){.learn-more}
456456

457-
If you're editing data in multiple languages, the _General_ tab in the example above is reserved for the default language (often "en"). Any change to other languages has to be done in the _Translations_ tab, where a corresponding language can be chosen from a drop-down menu as illustrated above. This also applies if you use the URL parameter `sap-language` on the draft page.
457+
If you're editing data in multiple languages, the _General_ tab in the example above is reserved for the default language (often "en"). Any change to other languages has to be done in the _Translations_ tab, where a corresponding language can be chosen [from a drop-down menu](https://github.com/SAP-samples/cloud-cap-samples/blob/14ac3efaa13fc025f621b4eed369d03f1ca48341/fiori/app/admin-books/fiori-service.cds#L70) as illustrated above. This also applies if you use the URL parameter `sap-language` on the draft page.
458458

459459
### Validating Drafts
460460

advanced/hybrid-testing.md

Lines changed: 16 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,10 @@ Only services that have the `shareable` flag in the metadata set to `true` can b
142142
See the [CloudFoundry docs](https://docs.cloudfoundry.org/devguide/services/sharing-instances.html) for further details.
143143
:::
144144

145+
::: tip Allow dynamic deploy targets
146+
Service bindings created by `cds bind` contain the Cloud Foundry API endpoint, org, and space. You can allow your services to connect to the currently targeted Cloud Foundry org and space by removing these properties from the binding structure.
147+
:::
148+
145149
### Services on Kubernetes
146150

147151

@@ -522,31 +526,25 @@ No credentials are saved!
522526
In your CI/CD pipeline you can resolve the bindings and inject them into the test commands:
523527

524528
```sh
525-
# Install DK for "cds env"
526-
npm i @sap/cds-dk --no-save
527-
528529
# Login
529530
cf auth $USER $PASSWORD
530-
531-
## Uncomment if your service bindings have
532-
## no "org" and "space" set (see note below)
533-
# cf target -o $ORG -s $SPACE
531+
# Optional if your bindings have org and space removed to be agnostic
532+
cf target -o $ORG -s $SPACE
534533

535534
# Set profile
536-
export CDS_ENV=integration-test
535+
export CDS_ENV=integration-test # [!code highlight]
536+
537537
# Set resolved bindings
538-
export cds_requires="$(cds env get requires --resolve-bindings)"
538+
export cds_requires="$(cds env get requires --resolve-bindings)" # [!code highlight]
539539

540-
# Execute test
541-
npm run integration-test
540+
# Run tests
541+
npm run integration-test # [!code highlight]
542542
```
543543

544-
<!-- TODO: "cds deploy" should take the existing bindings for hana -->
545-
546-
With `CDS_ENV`, you specify the configuration profile for the test, where you previously put the service binding configuration.
544+
Some comments to the previous snippet:
545+
- With `CDS_ENV` you specify the [configuration profile](../node.js/cds-env#profiles) for the test, where you previously put the service binding configuration.
546+
- [`cds env get requires`](../node.js/cds-env#services) prints the `requires` section of the configuration as a JSON string. Through `--resolve-bindings`, it includes the credentials of the service bindings from the cloud. To make the credentials available for all subsequent `cds` commands and the tests, the `requires` JSON string is put into the `cds_requires` script variable.
547+
- In `npm run integration-test` any test code can run, for example, [`cds.test`](../node.js/cds-test).
547548

548-
`cds env get requires` prints the `requires` section of the configuration as a JSON string. By adding the `--resolve-bindings` option, it includes the credentials of the service bindings from the cloud. To make the credentials available for all subsequent `cds` commands and the tests, the `requires` JSON string is put into the `cds_requires` variable.
549549

550-
::: tip Allow dynamic deploy targets
551-
Service bindings created by `cds bind` contain the Cloud Foundry API endpoint, org, and space. You can allow your services to connect to the currently targeted Cloud Foundry org and space by removing these properties from the binding structure.
552-
:::
550+
<!-- TODO: "cds deploy" should take the existing bindings for hana -->

advanced/odata.md

Lines changed: 42 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -633,14 +633,48 @@ The CDS path `f.struc.y` is translated to the OData path `f/struc_y`:
633633
</Schema>
634634
```
635635

636-
::: warning Restrictions concerning the foreign key elements of managed associations
636+
#### Managed Associations
637637

638-
1. Usually an annotation assigned to a managed association is copied to the foreign key elements of the association.
639-
This is a workaround for the lack of possibility to directly annotate a foreign key element.
640-
This copy mechanism is _not_ applied for annotations with expression values. So it is currently not possible
641-
to use expression-valued annotations for annotating foreign keys of a managed association.
638+
The OData backend translates managed associations into unmanaged ones plus explicit foreign key elements.
639+
During this translation, annotations assigned to the managed association are copied to the respective foreign key elements.
642640

643-
2. In an expression-valued annotation, it is not possible to reference the foreign key element
641+
Example:
642+
```cds
643+
service S {
644+
entity Authors { key ID : Integer; name : String; }
645+
entity Books { key ID : Integer; author : Association to Authors; }
646+
647+
annotate Books:author with @Common.Text: (author.name);
648+
}
649+
```
650+
651+
Resulting OData API:
652+
```xml
653+
<Schema Namespace="S">
654+
<!-- ... -->
655+
<EntityType Name="Authors">
656+
<!-- ... -->
657+
<Property Name="name" Type="Edm.String"/>
658+
</EntityType>
659+
<EntityType Name="Books">
660+
<!-- ... -->
661+
<NavigationProperty Name="author" Type="S.Authors"/>
662+
<Property Name="author_ID" Type="Edm.Int32"/>
663+
</EntityType>
664+
<Annotations Target="S.Books/author_ID">
665+
<Annotation Term="Common.Text" Path="author/name"/>
666+
</Annotations>
667+
</Schema>
668+
```
669+
670+
Instead of relying on this copy mechanism, you can also explicitly annotate a foreign key element:
671+
```cds
672+
annotate Books:author.ID with @Common.Text: ($self.author.name); // here $self is necessary
673+
```
674+
675+
::: warning Restriction concerning the foreign key elements of managed associations
676+
677+
In an expression-valued annotation, it is not possible to reference the foreign key element
644678
of a managed association.
645679

646680
:::
@@ -1187,6 +1221,7 @@ GET /Books?$apply=aggregate(stock with sum as stock) HTTP/1.1
11871221
#### Currencies and Units of Measure
11881222

11891223
If a property represents a monetary amount, it may have a related property that indicates the amount's *currency code*. Analogously, a property representing a measured quantity can be related to a *unit of measure*. To indicate that a property is a currency code or a unit of measure it can be annotated with the [Semantics Annotations](https://help.sap.com/docs/SAP_NETWEAVER_750/cc0c305d2fab47bd808adcad3ca7ee9d/fbcd3a59a94148f6adad80b9c97304ff.html) `@Semantics.currencyCode` or `@Semantics.unitOfMeasure`.
1224+
The aggregation method (typically, sum) is specified with the `@Aggregation.default` annotation.
11901225

11911226
```cds
11921227
@Aggregation.CustomAggregate#amount : 'Edm.Decimal'
@@ -1195,6 +1230,7 @@ entity Sales {
11951230
key id : GUID;
11961231
productId : GUID;
11971232
@Semantics.amount.currencyCode: 'currency'
1233+
@Aggregation.default: #SUM
11981234
amount : Decimal(10,2);
11991235
@Semantics.currencyCode
12001236
currency : String(3);

advanced/performance-modeling.md

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -207,9 +207,10 @@ GET http://localhost/odata/OrderItemsViewAssoc?$expand=Items&$select=OrderNo,Ite
207207
First sort on the `OrdersItems` and then join back to the `OrdersHeaders` with the help of an association:
208208

209209
```cds
210-
view SortedOrdersAssoc as select
211-
from OrdersItems {*, Header.OrderNo, Header.buyer, Header.currency }
212-
order by OrdersItems.title;
210+
view SortedOrdersAssoc as select {*, Header.OrderNo, Header.buyer, Header.currency } as Flatten
211+
from (
212+
select from OrdersItems {*} order by OrdersItems.title
213+
);
213214
```
214215

215216
#### **Bad**{.bad}
@@ -239,9 +240,11 @@ Basically, what is true for [Sorting](#sorting) is also valid for filtering.
239240
#### **Good**{.good}
240241

241242
```cds
242-
view FilteredOrdersAssoc as select
243-
from OrdersItems {*, Header.OrderNo, Header.buyer, Header.currency }
244-
where OrdersItems.price > 100;
243+
view FilteredOrdersAssoc as select {*, Header.OrderNo, Header.buyer, Header.currency } as Flatten
244+
from (
245+
select from OrdersItems {*}
246+
where OrdersItems.price > 100
247+
);
245248
```
246249

247250
#### **Bad**{.bad}
@@ -260,7 +263,7 @@ view FilteredOrdersJoin as select
260263
from OrdersHeaders JOIN OrdersItems on OrdersHeaders.ID = OrdersItems.Header.ID
261264
where price > 100;
262265
```
263-
This query cannot utilize database indexes properly.
266+
This query needs to identify that prices can be filtered before the join. Filtering beforehand prevents the full join from being materialized and then reduced to a smaller subset.
264267

265268
## Calculated Fields
266269
Database operations on calculated fields cannot leverage any DB indexes. This impacts performance significantly, as calculated fields cause full table scans.

advanced/publishing-apis/openapi.md

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -128,12 +128,12 @@ See [Frequently Asked Questions](#faq) for examples on how to use these annotati
128128
| &emsp;&emsp;`/SortRestrictions/...` | EntitySet, Singleton | `$orderby` system query option for reading related entities via a navigation path |
129129
| &emsp;&emsp;`/TopSupported` | EntitySet, Singleton | `$top` system query option for reading contained entities via a navigation path |
130130
| &emsp;&emsp;`/UpdateRestrictions/...` | EntitySet, Singleton | `PATCH` operation for modifying a contained entity via a navigation path |
131-
| `ReadByKeyRestrictions`<br />&emsp;`/Readable` | EntitySet | `GET` operation for reading a single entity by key |
132131
| &emsp;`/Description` | EntitySet | `summary` of Operation Object |
133132
| &emsp;`/LongDescription` | EntitySet | `description` of Operation Object |
134133
| `ReadRestrictions`<br />&emsp;`/Readable` | EntitySet, Singleton | `GET` operation for reading an entity set or singleton |
135134
| &emsp;`/Description` | EntitySet, Singleton | `summary` of Operation Object |
136135
| &emsp;`/LongDescription` | EntitySet, Singleton | `description` of Operation Object |
136+
| &emsp;`ReadByKeyRestrictions`<br />&emsp;&emsp;`/Readable` | EntitySet | `GET` operation for reading a single entity by key |
137137
| `SearchRestrictions`<br />&emsp;`/Searchable` | EntitySet | `$search` system query option for `GET` operation |
138138
| `SelectSupport`<br />&emsp;`/Supported` | EntitySet, Singleton | `$select` system query option for `GET` operation |
139139
| `SkipSupported` | EntitySet | `$skip` system query option for `GET` operation |
@@ -170,17 +170,26 @@ This is an example of a CDS service annotated with the annotations above:
170170
annotate MyService with @(
171171
Authorization: {
172172
Authorizations: [
173-
{ $Type : 'Auth.Http', Name : 'Basic', Scheme : 'basic' },
174-
{ $Type : 'Auth.Http', Name : 'JWT', Scheme : 'bearer', BearerFormat : 'JWT' },
173+
{ $Type : 'Authorization.Http', Name : 'Basic', Scheme : 'basic' },
174+
{ $Type : 'Authorization.Http', Name : 'JWT', Scheme : 'bearer', BearerFormat : 'JWT' },
175+
{ $Type : 'Authorization.OAuth2ClientCredentials', Name : 'OAuth2',
176+
Scopes : [{
177+
Scope : 'some_scope',
178+
Description: 'Scope description'
179+
}],
180+
RefreshUrl : 'https://some.host/oauth/token/refresh',
181+
TokenUrl : 'https://some.host/oauth/token'
182+
},
175183
],
176184
SecuritySchemes: [
177185
{ Authorization : 'Basic' },
178186
{ Authorization : 'JWT', RequiredScopes : [] },
187+
{ Authorization : 'OAuth2' },
179188
]
180189
}
181190
);
182191
```
183-
[See it in context.](https://github.com/chgeo/cds-swagger-ui-express/blob/e5794c55b53dd3e43ebe8ffcfff69341b6eac9c7/tests/app/services.cds#L23-L34){.learn-more}
192+
[See it in context.](https://github.com/chgeo/cds-swagger-ui-express/blob/651013b529168b30c024f8653c249f170ba9d114/tests/app/services.cds#L35-L55){.learn-more}
184193

185194

186195
## [Common](https://github.com/SAP/odata-vocabularies/blob/main/vocabularies/Common.md)

cds/cdl.md

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -426,6 +426,20 @@ entity Books {
426426
}
427427
```
428428

429+
You can declare structured types based on other
430+
definitions using the `projection on` syntax.
431+
You can use nested projections or aliases as known from entity projections.
432+
Only the effective signature of the projection is relevant.
433+
434+
<!-- cds-mode: upcoming -->
435+
```cds
436+
type CustomerData : projection on Customer {
437+
name.firstName, // select from structures
438+
name.lastName,
439+
address as customerAddress, // aliases
440+
}
441+
```
442+
429443

430444
### Arrayed Types
431445

@@ -628,6 +642,14 @@ type Complex {
628642
}
629643
```
630644

645+
If the element has an enum type, you can use the enum symbol instead of a literal value:
646+
```cds
647+
type Status : String enum {open; closed;}
648+
entity Order {
649+
status : Status default #open;
650+
}
651+
```
652+
631653

632654
### Type References
633655

@@ -2071,7 +2093,7 @@ service MyOrders { ...
20712093
}
20722094
```
20732095

2074-
An event can also be defined as projection on an entity, type, or another event.
2096+
An event can also be defined as projection on an entity, structured type, or another event.
20752097
Only the effective signature of the projection is relevant.
20762098
```cds
20772099
service MyOrders { ...

0 commit comments

Comments
 (0)