diff --git a/app/admin/fiori-service.cds b/app/admin/fiori-service.cds
index 4cb63190..38176e7d 100644
--- a/app/admin/fiori-service.cds
+++ b/app/admin/fiori-service.cds
@@ -94,7 +94,7 @@ annotate AdminService.GenreHierarchy with {
ID @UI.Hidden;
};
-annotate AdminService.GenreHierarchy with @Hierarchy.RecursiveHierarchyActions #GenreHierarchy: {
+annotate AdminService.GenreHierarchy with @Hierarchy.RecursiveHierarchyActions #GenreHierarchyHierarchy: {
$Type : 'Hierarchy.RecursiveHierarchyActionsType',
// any name can be the action name with namespace/no bound action name
ChangeNextSiblingAction: 'AdminService.moveSibling',
@@ -104,7 +104,7 @@ annotate AdminService.GenreHierarchy with @UI: {
PresentationVariant #VH: {
$Type : 'UI.PresentationVariantType',
Visualizations : ['@UI.LineItem'],
- RecursiveHierarchyQualifier: 'GenreHierarchy'
+ RecursiveHierarchyQualifier: 'GenreHierarchyHierarchy'
},
LineItem : [{
$Type: 'UI.DataField',
@@ -196,19 +196,7 @@ annotate AdminService.Books.texts {
//
// Annotations for hierarchy ContentsHierarchy
//
-annotate AdminService.ContentsHierarchy with @Aggregation.RecursiveHierarchy#ContentsHierarchy: {
- $Type: 'Aggregation.RecursiveHierarchyType',
- NodeProperty: ID, // identifies a node
- ParentNavigationProperty: parent // navigates to a node's parent
- };
-
-annotate AdminService.ContentsHierarchy with @Hierarchy.RecursiveHierarchy#ContentsHierarchy: {
- $Type: 'Hierarchy.RecursiveHierarchyType',
- LimitedDescendantCount: LimitedDescendantCount,
- DistanceFromRoot: DistanceFromRoot,
- DrillState: DrillState,
- LimitedRank: LimitedRank
-};
+annotate AdminService.ContentsHierarchy with @hierarchy;
annotate AdminService.Books actions {
@(
diff --git a/app/admin/webapp/manifest.json b/app/admin/webapp/manifest.json
index 49d05c2c..160a8a11 100644
--- a/app/admin/webapp/manifest.json
+++ b/app/admin/webapp/manifest.json
@@ -124,7 +124,7 @@
"controlConfiguration": {
"contents/@com.sap.vocabularies.UI.v1.LineItem": {
"tableSettings": {
- "hierarchyQualifier": "ContentsHierarchy",
+ "hierarchyQualifier": "ContentsHierarchyHierarchy",
"type": "TreeTable"
}
}
diff --git a/app/browse-genres/fiori-service.cds b/app/browse-genres/fiori-service.cds
index 809df04f..e05b2ff5 100644
--- a/app/browse-genres/fiori-service.cds
+++ b/app/browse-genres/fiori-service.cds
@@ -5,16 +5,5 @@
using CatalogService from '../../srv/cat-service';
-annotate CatalogService.GenreHierarchy with @Aggregation.RecursiveHierarchy#GenreHierarchy: {
- $Type: 'Aggregation.RecursiveHierarchyType',
- NodeProperty: ID, // identifies a node
- ParentNavigationProperty: parent // navigates to a node's parent
- };
+annotate CatalogService.GenreHierarchy with @hierarchy;
-annotate CatalogService.GenreHierarchy with @Hierarchy.RecursiveHierarchy#GenreHierarchy: {
- $Type: 'Hierarchy.RecursiveHierarchyType',
- LimitedDescendantCount: LimitedDescendantCount,
- DistanceFromRoot: DistanceFromRoot,
- DrillState: DrillState,
- LimitedRank: LimitedRank
-};
diff --git a/app/browse-genres/webapp/manifest.json b/app/browse-genres/webapp/manifest.json
index 875c6231..3814bfa7 100644
--- a/app/browse-genres/webapp/manifest.json
+++ b/app/browse-genres/webapp/manifest.json
@@ -88,7 +88,7 @@
"controlConfiguration": {
"@com.sap.vocabularies.UI.v1.LineItem": {
"tableSettings": {
- "hierarchyQualifier": "GenreHierarchy",
+ "hierarchyQualifier": "GenreHierarchyHierarchy",
"type": "TreeTable"
}
}
diff --git a/app/genres/fiori-service.cds b/app/genres/fiori-service.cds
index 63862035..46f160c5 100644
--- a/app/genres/fiori-service.cds
+++ b/app/genres/fiori-service.cds
@@ -5,17 +5,4 @@
using AdminService from '../../srv/admin-service';
-annotate AdminService.GenreHierarchy with @Aggregation.RecursiveHierarchy#GenreHierarchy: {
- $Type: 'Aggregation.RecursiveHierarchyType',
- NodeProperty: ID, // identifies a node
- ParentNavigationProperty: parent // navigates to a node's parent
- };
-
- annotate AdminService.GenreHierarchy with @Hierarchy.RecursiveHierarchy#GenreHierarchy: {
- $Type: 'Hierarchy.RecursiveHierarchyType',
- // ExternalKey : null,
- LimitedDescendantCount: LimitedDescendantCount,
- DistanceFromRoot: DistanceFromRoot,
- DrillState: DrillState,
- LimitedRank: LimitedRank
-};
+annotate AdminService.GenreHierarchy with @hierarchy;
diff --git a/app/genres/webapp/manifest.json b/app/genres/webapp/manifest.json
index 68d48208..b7ca4b3c 100644
--- a/app/genres/webapp/manifest.json
+++ b/app/genres/webapp/manifest.json
@@ -93,7 +93,7 @@
"controlConfiguration": {
"@com.sap.vocabularies.UI.v1.LineItem": {
"tableSettings": {
- "hierarchyQualifier": "GenreHierarchy",
+ "hierarchyQualifier": "GenreHierarchyHierarchy",
"type": "TreeTable"
}
}
diff --git a/db/books.cds b/db/books.cds
index 48d24ccf..b3cfcbc2 100644
--- a/db/books.cds
+++ b/db/books.cds
@@ -3,7 +3,6 @@ namespace my.bookshop;
using {Currency, sap, managed, cuid} from '@sap/cds/common';
using my.bookshop.Reviews from './reviews';
using my.bookshop.TechnicalBooleanFlag from './common';
-using {my.common.Hierarchy as Hierarchy} from './hierarchy';
@fiori.draft.enabled
entity Books : cuid, managed {
@@ -46,7 +45,7 @@ annotate Authors with
/**
* Hierarchically organized Code List for Genres
*/
-entity Genres : sap.common.CodeList, Hierarchy {
+entity Genres : sap.common.CodeList {
key ID : UUID;
// move siblings
siblingRank : Integer;
@@ -59,7 +58,7 @@ entity Genres : sap.common.CodeList, Hierarchy {
/**
* Hierarchically organized entity for Contents
*/
-entity Contents: Hierarchy {
+entity Contents {
key ID : UUID;
name : String;
page : Integer;
diff --git a/db/hierarchy.cds b/db/hierarchy.cds
deleted file mode 100644
index f8ec83cb..00000000
--- a/db/hierarchy.cds
+++ /dev/null
@@ -1,23 +0,0 @@
-namespace my.common;
-
-aspect Hierarchy {
- virtual LimitedDescendantCount : Integer64;
- virtual DistanceFromRoot : Integer64;
- virtual DrillState : String;
- virtual LimitedRank : Integer64;
-}
-
-
-annotate Hierarchy with @Capabilities.FilterRestrictions.NonFilterableProperties: [
- 'LimitedDescendantCount',
- 'DistanceFromRoot',
- 'DrillState',
- 'LimitedRank'
-];
-
-annotate Hierarchy with @Capabilities.SortRestrictions.NonSortableProperties: [
- 'LimitedDescendantCount',
- 'DistanceFromRoot',
- 'DrillState',
- 'LimitedRank'
-];
diff --git a/package-lock.json b/package-lock.json
index 24549492..e4cf21a2 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -86,7 +86,6 @@
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.2.tgz",
"integrity": "sha512-q1mjIoW1VX4IvSocvM/vbTiveKC4k9eLrajNEuSsmjymSDEbpGddtpfOoN7YGAqBK3NG+uqo8ia4PDTt8buCYA==",
"license": "MIT",
- "peer": true,
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
@@ -99,6 +98,7 @@
"resolved": "https://registry.npmjs.org/@sap/cds/-/cds-9.8.3.tgz",
"integrity": "sha512-up/kNVCZK8bOTyye1QN2BjMpH+IgcdyhT3U9mS3TqkpmjNE2JXq8Qz1iNAN+nYcXRWJ1XAUNpgebLfuh/49MGQ==",
"license": "SEE LICENSE IN LICENSE",
+ "peer": true,
"dependencies": {
"@sap/cds-compiler": "^6.4",
"@sap/cds-fiori": "^2",
@@ -144,10 +144,11 @@
"hasShrinkwrap": true,
"license": "SEE LICENSE IN LICENSE",
"dependencies": {
- "@cap-js/asyncapi": "^1.0.0",
- "@cap-js/openapi": "^1.0.0",
- "@sap/cds": ">=8.3",
- "@sap/cds-mtxs": ">=2",
+ "@cap-js/asyncapi": "1.0.3",
+ "@cap-js/openapi": "1.3.1",
+ "@sap/cds": "9.7.0-2026012314574056-SNAPSHOT",
+ "@sap/cds-compiler": "6.7.0",
+ "@sap/cds-mtxs": "3.6.2",
"@sap/hdi-deploy": "^5",
"axios": "^1",
"express": "^4.22.1 || ^5",
@@ -164,7 +165,7 @@
"cds-tsx": "bin/cds-tsx.js"
},
"optionalDependencies": {
- "@cap-js/sqlite": ">=1"
+ "@cap-js/sqlite": "2.1.2"
}
},
"node_modules/@sap/cds-dk/node_modules/@cap-js/asyncapi": {
@@ -226,7 +227,6 @@
"integrity": "sha512-zeR9k5pd4gxjZ0abRoIaxdc7I3nDktoXZk2qOv9gCNWx3mVwEn32VRhyLaRsDiJjTs0xq/T8mfPtyuXu7GWBcA==",
"dev": true,
"license": "MIT",
- "peer": true,
"engines": {
"node": "^20.19.0 || ^22.13.0 || >=24"
},
@@ -247,6 +247,7 @@
"integrity": "sha512-B85qUoUCVHN8gqHdD4nY+0LM1hEGGllEqWnMxsIBijBv3KXB4tOkf/krC8POieKZGMFRHylAcW1fAYVgbLqppw==",
"dev": true,
"license": "SEE LICENSE IN LICENSE",
+ "peer": true,
"dependencies": {
"@sap/cds-compiler": "^6.4",
"@sap/cds-fiori": "^2",
@@ -309,6 +310,16 @@
"@sap/cds": "^9"
}
},
+ "node_modules/@sap/cds-dk/node_modules/@sap/cds/node_modules/@sap/cds-fiori": {
+ "version": "2.1.1",
+ "integrity": "sha512-X+4v4LBAT8HIt0zr28/kJNS15nlNlcM97vAMW+agLrmK134nyBiMwUMcp8BMhxlG9B2PykrnAKH56D9O3tfoBg==",
+ "dev": true,
+ "license": "SEE LICENSE IN LICENSE",
+ "peerDependencies": {
+ "@sap/cds": ">=8",
+ "express": "^4"
+ }
+ },
"node_modules/@sap/cds-dk/node_modules/@sap/hdi": {
"version": "4.8.0",
"integrity": "sha512-tkJmY2ffm6mt4/LFwRBihlQkMxNAXa3ngvRe2N/6+qLIsUNdrH/M03S5mkygXq56K+KoVVZYuradajCusMWwsw==",
@@ -855,6 +866,7 @@
"integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==",
"dev": true,
"license": "MIT",
+ "peer": true,
"dependencies": {
"accepts": "^2.0.0",
"body-parser": "^2.2.1",
@@ -1164,6 +1176,7 @@
"integrity": "sha512-xYL/W+fq2TyGHyzm8muolQnw8tdh4+2NQ8mQP2FpLSuhfJ8l0jQNSUZoAXic7NfMEan1Jvf8V1L4blwkgTc6+A==",
"dev": true,
"license": "Apache-2.0",
+ "peer": true,
"dependencies": {
"iconv-lite": "0.7.0"
},
diff --git a/pom.xml b/pom.xml
index cd542fd7..9bdfd471 100644
--- a/pom.xml
+++ b/pom.xml
@@ -28,7 +28,7 @@
21
- 4.8.0
+ 4.9.0-m2616
5.27.0
3.6.9
4.1.1
diff --git a/srv/src/test/java/my/bookshop/GenreHierarchyTest.java b/srv/src/test/java/my/bookshop/GenreHierarchyTest.java
index d6d374bb..7b17b97c 100644
--- a/srv/src/test/java/my/bookshop/GenreHierarchyTest.java
+++ b/srv/src/test/java/my/bookshop/GenreHierarchyTest.java
@@ -45,7 +45,7 @@ void startOneLevel() throws Exception {
genresURI
+ "?$select=DrillState,ID,name,DistanceFromRoot"
+ "&$apply=orderby(name)/"
- + "com.sap.vocabularies.Hierarchy.v1.TopLevels(HierarchyNodes=$root/GenreHierarchy,HierarchyQualifier='GenreHierarchy',NodeProperty='ID',Levels=1)"
+ + "com.sap.vocabularies.Hierarchy.v1.TopLevels(HierarchyNodes=$root/GenreHierarchy,HierarchyQualifier='GenreHierarchyHierarchy',NodeProperty='ID',Levels=1)"
+ "&$count=true"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.value[0].ID").value("8bbf14c6-b378-4e35-9b4f-05a9c8878001"))
@@ -68,7 +68,7 @@ void startTwoLevels() throws Exception {
genresURI
+ "?$select=DrillState,ID,name,DistanceFromRoot"
+ "&$apply=orderby(name)/"
- + "com.sap.vocabularies.Hierarchy.v1.TopLevels(HierarchyNodes=$root/GenreHierarchy,HierarchyQualifier='GenreHierarchy',NodeProperty='ID',Levels=2)"
+ + "com.sap.vocabularies.Hierarchy.v1.TopLevels(HierarchyNodes=$root/GenreHierarchy,HierarchyQualifier='GenreHierarchyHierarchy',NodeProperty='ID',Levels=2)"
+ "&$count=true"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.value[0].name").value("Fiction"))
@@ -107,7 +107,7 @@ void collapseAll() throws Exception {
get(
genresURI
+ "?$select=DrillState,ID,name"
- + "&$apply=orderby(name)/com.sap.vocabularies.Hierarchy.v1.TopLevels(HierarchyNodes=$root/GenreHierarchy,HierarchyQualifier='GenreHierarchy',NodeProperty='ID',Levels=1)"
+ + "&$apply=orderby(name)/com.sap.vocabularies.Hierarchy.v1.TopLevels(HierarchyNodes=$root/GenreHierarchy,HierarchyQualifier='GenreHierarchyHierarchy',NodeProperty='ID',Levels=1)"
+ "&$count=true&$skip=0&$top=238"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.value[0].name").value("Fiction"))
@@ -123,7 +123,7 @@ void expandAllTop100() throws Exception {
String url =
genresURI
+ "?$select=DistanceFromRoot,DrillState,ID,LimitedDescendantCount,name"
- + "&$apply=orderby(name)/com.sap.vocabularies.Hierarchy.v1.TopLevels(HierarchyNodes=$root/GenreHierarchy,HierarchyQualifier='GenreHierarchy',NodeProperty='ID')"
+ + "&$apply=orderby(name)/com.sap.vocabularies.Hierarchy.v1.TopLevels(HierarchyNodes=$root/GenreHierarchy,HierarchyQualifier='GenreHierarchyHierarchy',NodeProperty='ID')"
+ "&$count=true&$skip=0&$top=100";
client
@@ -147,7 +147,7 @@ void search() throws Exception {
+ "?$select=DistanceFromRoot,DrillState,ID,LimitedDescendantCount,name"
+ "&$apply=ancestors($root/GenreHierarchy,GenreHierarchy,ID,search(\"true\"),keep start)"
+ "/orderby(name)"
- + "/com.sap.vocabularies.Hierarchy.v1.TopLevels(HierarchyNodes=$root/GenreHierarchy,HierarchyQualifier='GenreHierarchy',NodeProperty='ID')"
+ + "/com.sap.vocabularies.Hierarchy.v1.TopLevels(HierarchyNodes=$root/GenreHierarchy,HierarchyQualifier='GenreHierarchyHierarchy',NodeProperty='ID')"
+ "&$count=true"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.value[0].name").value("Fiction"))
@@ -177,7 +177,7 @@ void filterNotExpanded() throws Exception {
genresURI
+ "?$select=DrillState,ID,name,DistanceFromRoot"
+ "&$apply=ancestors($root/GenreHierarchy,GenreHierarchy,ID,filter(name eq 'Autobiography'),keep start)/orderby(name)"
- + "/com.sap.vocabularies.Hierarchy.v1.TopLevels(HierarchyNodes=$root/GenreHierarchy,HierarchyQualifier='GenreHierarchy',NodeProperty='ID',Levels=1)"))
+ + "/com.sap.vocabularies.Hierarchy.v1.TopLevels(HierarchyNodes=$root/GenreHierarchy,HierarchyQualifier='GenreHierarchyHierarchy',NodeProperty='ID',Levels=1)"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.value[0].name").value("Non-Fiction"))
.andExpect(jsonPath("$.value[0].DrillState").value("collapsed"))
@@ -196,7 +196,7 @@ void filterExpandLevels() throws Exception {
genresURI
+ "?$select=DistanceFromRoot,DrillState,ID,LimitedDescendantCount,name"
+ "&$apply=ancestors($root/GenreHierarchy,GenreHierarchy,ID,filter(name eq 'Autobiography'),keep start)/orderby(name)"
- + "/com.sap.vocabularies.Hierarchy.v1.TopLevels(HierarchyNodes=$root/GenreHierarchy,HierarchyQualifier='GenreHierarchy',NodeProperty='ID',Levels=1,ExpandLevels="
+ + "/com.sap.vocabularies.Hierarchy.v1.TopLevels(HierarchyNodes=$root/GenreHierarchy,HierarchyQualifier='GenreHierarchyHierarchy',NodeProperty='ID',Levels=1,ExpandLevels="
+ expandLevelsJson
+ ")&$count=true";
String uriString = UriComponentsBuilder.fromUriString(unencoded).toUriString();
@@ -219,7 +219,7 @@ void startTwoLevelsOrderByDesc() throws Exception {
genresURI
+ "?$select=DrillState,ID,name,DistanceFromRoot"
+ "&$apply=orderby(name desc)/"
- + "com.sap.vocabularies.Hierarchy.v1.TopLevels(HierarchyNodes=$root/GenreHierarchy,HierarchyQualifier='GenreHierarchy',NodeProperty='ID',Levels=2)"
+ + "com.sap.vocabularies.Hierarchy.v1.TopLevels(HierarchyNodes=$root/GenreHierarchy,HierarchyQualifier='GenreHierarchyHierarchy',NodeProperty='ID',Levels=2)"
+ "&$count=true"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.value[0].name").value("Non-Fiction"))