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"))