From 1605d14309d2fe9cd26f2c58d8db8386a6e659e2 Mon Sep 17 00:00:00 2001 From: D070615 Date: Thu, 16 Jan 2025 16:43:15 +0100 Subject: [PATCH 01/22] adapted model and custom expand --- db/books.cds | 4 +- db/data/my.bookshop-Books.csv | 10 +-- db/data/my.bookshop-Genres.csv | 47 +++++--------- srv/admin-service.cds | 1 + .../handlers/AdminServiceHandler.java | 2 +- .../handlers/HierarchyExpandHandler.java | 49 ++++++++++++++ .../bookshop/handlers/HierarchyHandler.java | 4 +- .../java/my/bookshop/GenreHierarchyTest.java | 4 +- .../handlers/HierarchyHandlerSorterTest.java | 3 +- .../handlers/HierarchyHandlerTest.java | 64 +++++++++++++++++++ 10 files changed, 145 insertions(+), 43 deletions(-) create mode 100644 srv/src/main/java/my/bookshop/handlers/HierarchyExpandHandler.java create mode 100644 srv/src/test/java/my/bookshop/handlers/HierarchyHandlerTest.java diff --git a/db/books.cds b/db/books.cds index e7aa3928..9a2bee75 100644 --- a/db/books.cds +++ b/db/books.cds @@ -49,8 +49,6 @@ annotate Authors with * Hierarchically organized Code List for Genres */ entity Genres : sap.common.CodeList { - key ID : Integer; + key ID : UUID; parent : Association to Genres; - children : Composition of many Genres - on children.parent = $self; } diff --git a/db/data/my.bookshop-Books.csv b/db/data/my.bookshop-Books.csv index e5453379..66fec8ad 100644 --- a/db/data/my.bookshop-Books.csv +++ b/db/data/my.bookshop-Books.csv @@ -1,6 +1,6 @@ ID;TITLE;DESCR;AUTHOR_ID;STOCK;PRICE;CURRENCY_CODE;GENRE_ID;RATING;ISBN -f846b0b9-01d4-4f6d-82a4-d79204f62278;Wuthering Heights;"Wuthering Heights, Emily Brontë's only novel, was published in 1847 under the pseudonym ""Ellis Bell"". It was written between October 1845 and June 1846. Wuthering Heights and Anne Brontë's Agnes Grey were accepted by publisher Thomas Newby before the success of their sister Charlotte's novel Jane Eyre. After Emily's death, Charlotte edited the manuscript of Wuthering Heights and arranged for the edited version to be published as a posthumous second edition in 1850.";335c7bcd-b826-4f14-a788-e0bf6738617a;12;11.11;GBP;103;4.5;979-8698267973 -9b084139-0b1e-43b6-b12a-7b3669d75f02;Jane Eyre;"Jane Eyre /ɛər/ (originally published as Jane Eyre: An Autobiography) is a novel by English writer Charlotte Brontë, published under the pen name ""Currer Bell"", on 16 October 1847, by Smith, Elder & Co. of London. The first American edition was published the following year by Harper & Brothers of New York. Primarily a bildungsroman, Jane Eyre follows the experiences of its eponymous heroine, including her growth to adulthood and her love for Mr. Rochester, the brooding master of Thornfield Hall. The novel revolutionised prose fiction in that the focus on Jane's moral and spiritual development is told through an intimate, first-person narrative, where actions and events are coloured by a psychological intensity. The book contains elements of social criticism, with a strong sense of Christian morality at its core and is considered by many to be ahead of its time because of Jane's individualistic character and how the novel approaches the topics of class, sexuality, religion and feminism.";e3da2c2e-72ee-45d5-8def-52964c7b252a;11;12.34;GBP;103;3.0;979-8598716472 -51061ce3-ddde-4d70-a2dc-6314afbcc73e;The Raven;"“The Raven"" is a narrative poem by American writer Edgar Allan Poe. First published in January 1845, the poem is often noted for its musicality, stylized language, and supernatural atmosphere. It tells of a talking raven's mysterious visit to a distraught lover, tracing the man's slow fall into madness. The lover, often identified as being a student, is lamenting the loss of his love, Lenore. Sitting on a bust of Pallas, the raven seems to further distress the protagonist with its constant repetition of the word ""Nevermore"". The poem makes use of folk, mythological, religious, and classical references.";e7643aae-2d2f-4656-bb2d-1328ad3c8045;333;13.13;USD;117;2.5;978-1092909747 -aebdfc8a-0dfa-4468-bd36-48aabd65e663;Eleonora;"""Eleonora"" is a short story by Edgar Allan Poe, first published in 1842 in Philadelphia in the literary annual The Gift. It is often regarded as somewhat autobiographical and has a relatively ""happy"" ending.";e7643aae-2d2f-4656-bb2d-1328ad3c8045;555;14;USD;117;1.0;979-8669820985 -4a519e61-3c3a-4bd9-ab12-d7e0c5329933;Catweazle;Catweazle is a British fantasy television series, starring Geoffrey Bayldon in the title role, and created by Richard Carpenter for London Weekend Television. The first series, produced and directed by Quentin Lawrence, was screened in the UK on ITV in 1970. The second series, directed by David Reid and David Lane, was shown in 1971. Each series had thirteen episodes, most but not all written by Carpenter, who also published two books based on the scripts.;3c081d9d-abda-4da9-8b6a-4f4555bb26bc;22;15;EUR;110;4.0;978-3473523023 +f846b0b9-01d4-4f6d-82a4-d79204f62278;Wuthering Heights;"Wuthering Heights, Emily Brontë's only novel, was published in 1847 under the pseudonym ""Ellis Bell"". It was written between October 1845 and June 1846. Wuthering Heights and Anne Brontë's Agnes Grey were accepted by publisher Thomas Newby before the success of their sister Charlotte's novel Jane Eyre. After Emily's death, Charlotte edited the manuscript of Wuthering Heights and arranged for the edited version to be published as a posthumous second edition in 1850.";335c7bcd-b826-4f14-a788-e0bf6738617a;12;11.11;GBP;f846b0b9-01d4-4f6d-82a4-d79204f62369;4.5;979-8698267973 +9b084139-0b1e-43b6-b12a-7b3669d75f02;Jane Eyre;"Jane Eyre /ɛər/ (originally published as Jane Eyre: An Autobiography) is a novel by English writer Charlotte Brontë, published under the pen name ""Currer Bell"", on 16 October 1847, by Smith, Elder & Co. of London. The first American edition was published the following year by Harper & Brothers of New York. Primarily a bildungsroman, Jane Eyre follows the experiences of its eponymous heroine, including her growth to adulthood and her love for Mr. Rochester, the brooding master of Thornfield Hall. The novel revolutionised prose fiction in that the focus on Jane's moral and spiritual development is told through an intimate, first-person narrative, where actions and events are coloured by a psychological intensity. The book contains elements of social criticism, with a strong sense of Christian morality at its core and is considered by many to be ahead of its time because of Jane's individualistic character and how the novel approaches the topics of class, sexuality, religion and feminism.";e3da2c2e-72ee-45d5-8def-52964c7b252a;11;12.34;GBP;f846b0b9-01d4-4f6d-82a4-d79204f62571;3.0;979-8598716472 +51061ce3-ddde-4d70-a2dc-6314afbcc73e;The Raven;"“The Raven"" is a narrative poem by American writer Edgar Allan Poe. First published in January 1845, the poem is often noted for its musicality, stylized language, and supernatural atmosphere. It tells of a talking raven's mysterious visit to a distraught lover, tracing the man's slow fall into madness. The lover, often identified as being a student, is lamenting the loss of his love, Lenore. Sitting on a bust of Pallas, the raven seems to further distress the protagonist with its constant repetition of the word ""Nevermore"". The poem makes use of folk, mythological, religious, and classical references.";e7643aae-2d2f-4656-bb2d-1328ad3c8045;333;13.13;USD;d846b0b9-01d4-4f6d-82a4-d79204f62487;2.5;978-1092909747 +aebdfc8a-0dfa-4468-bd36-48aabd65e663;Eleonora;"""Eleonora"" is a short story by Edgar Allan Poe, first published in 1842 in Philadelphia in the literary annual The Gift. It is often regarded as somewhat autobiographical and has a relatively ""happy"" ending.";e7643aae-2d2f-4656-bb2d-1328ad3c8045;555;14;USD;d846b0b9-01d4-4f6d-82a4-d79204f62590;1.0;979-8669820985 +4a519e61-3c3a-4bd9-ab12-d7e0c5329933;Catweazle;Catweazle is a British fantasy television series, starring Geoffrey Bayldon in the title role, and created by Richard Carpenter for London Weekend Television. The first series, produced and directed by Quentin Lawrence, was screened in the UK on ITV in 1970. The second series, directed by David Reid and David Lane, was shown in 1971. Each series had thirteen episodes, most but not all written by Carpenter, who also published two books based on the scripts.;3c081d9d-abda-4da9-8b6a-4f4555bb26bc;22;15;EUR;d846b0b9-01d4-4f6d-82a4-d79204f62590;4.0;978-3473523023 diff --git a/db/data/my.bookshop-Genres.csv b/db/data/my.bookshop-Genres.csv index 84bb85b0..56e304e0 100644 --- a/db/data/my.bookshop-Genres.csv +++ b/db/data/my.bookshop-Genres.csv @@ -1,31 +1,18 @@ ID;parent_ID;name -100;;Fiction -101;100;Action -102;100;Adventure -103;100;Drama -105;100;Poetry -106;100;Science Fiction -107;106;Utopian and Dystopian -108;107;Dystopia -109;108;Cyberpunk -110;109;Steampunk -104;100;Fantasy -111;104;Epic fantasy -112;104;High fantasy -113;100;Graphic Novel -115;100;Short Story -116;100;Romance -117;100;Mystery -123;117;Thriller -124;117;Suspense -118;100;Horror -119;100;Historical Fiction -120;100;Contemporary Fiction -121;100;Magical Realism -122;100;Literary Fiction -128;100;Fairy Tale -200;;Non-Fiction -201;200;Biography -202;201;Autobiography -203;200;Essay -204;200;Speech +f846b0b9-01d4-4f6d-82a4-d79204f62369;;Fiction +f846b0b9-01d4-4f6d-82a4-d79204f62570;f846b0b9-01d4-4f6d-82a4-d79204f62369;Drama +f846b0b9-01d4-4f6d-82a4-d79204f62571;f846b0b9-01d4-4f6d-82a4-d79204f62369;Poetry +f846b0b9-01d4-4f6d-82a4-d79204f62572;f846b0b9-01d4-4f6d-82a4-d79204f62369;Fantasy +f846b0b9-01d4-4f6d-82a4-d79204f62592;f846b0b9-01d4-4f6d-82a4-d79204f62572;Epic fantasy +f846b0b9-01d4-4f6d-82a4-d79204f62593;f846b0b9-01d4-4f6d-82a4-d79204f62572;High fantasy +f846b0b9-01d4-4f6d-82a4-d79204f62573;f846b0b9-01d4-4f6d-82a4-d79204f62369;Science Fiction +f846b0b9-01d4-4f6d-82a4-d79204f62574;f846b0b9-01d4-4f6d-82a4-d79204f62369;Romance +f846b0b9-01d4-4f6d-82a4-d79204f62575;f846b0b9-01d4-4f6d-82a4-d79204f62369;Mystery +f846b0b9-01d4-4f6d-82a4-d79204f62576;f846b0b9-01d4-4f6d-82a4-d79204f62369;Thriller +f846b0b9-01d4-4f6d-82a4-d79204f62577;f846b0b9-01d4-4f6d-82a4-d79204f62369;Dystopia +f846b0b9-01d4-4f6d-82a4-d79204f62578;f846b0b9-01d4-4f6d-82a4-d79204f62369;Fairy Tale +d846b0b9-01d4-4f6d-82a4-d79204f62487;;Non-Fiction +d846b0b9-01d4-4f6d-82a4-d79204f62588;d846b0b9-01d4-4f6d-82a4-d79204f62487;Biography +d846b0b9-01d4-4f6d-82a4-d79204f62589;d846b0b9-01d4-4f6d-82a4-d79204f62588;Autobiography +d846b0b9-01d4-4f6d-82a4-d79204f62590;d846b0b9-01d4-4f6d-82a4-d79204f62487;Essay +d846b0b9-01d4-4f6d-82a4-d79204f62591;d846b0b9-01d4-4f6d-82a4-d79204f62487;Speech \ No newline at end of file diff --git a/srv/admin-service.cds b/srv/admin-service.cds index 4d3b93f3..eb705efa 100644 --- a/srv/admin-service.cds +++ b/srv/admin-service.cds @@ -45,6 +45,7 @@ annotate AdminService.Books with @cds.search: { // Enable Fiori Draft for Orders annotate AdminService.Orders with @odata.draft.enabled; annotate AdminService.Books with @odata.draft.enabled; +annotate AdminService.GenreHierarchy with @odata.draft.enabled; // workaround to enable the value help for languages // Necessary because auto exposure is currently not working diff --git a/srv/src/main/java/my/bookshop/handlers/AdminServiceHandler.java b/srv/src/main/java/my/bookshop/handlers/AdminServiceHandler.java index 06ec29c6..5a2f33d6 100644 --- a/srv/src/main/java/my/bookshop/handlers/AdminServiceHandler.java +++ b/srv/src/main/java/my/bookshop/handlers/AdminServiceHandler.java @@ -284,7 +284,7 @@ public void addBooksViaCsv(CdsUpdateEventContext context, Upload upload) { book.setStock(Integer.valueOf(p[4]).intValue()); book.setPrice(BigDecimal.valueOf(Double.valueOf(p[5]))); book.setCurrencyCode(p[6]); - book.setGenreId(Integer.valueOf(p[7])); + book.setGenreId(String.valueOf(p[7])); // separate transaction per line context.getCdsRuntime().changeSetContext().run(ctx -> { diff --git a/srv/src/main/java/my/bookshop/handlers/HierarchyExpandHandler.java b/srv/src/main/java/my/bookshop/handlers/HierarchyExpandHandler.java new file mode 100644 index 00000000..2daa240f --- /dev/null +++ b/srv/src/main/java/my/bookshop/handlers/HierarchyExpandHandler.java @@ -0,0 +1,49 @@ +package my.bookshop.handlers; + +import java.util.List; + +import org.springframework.context.annotation.Profile; +import org.springframework.stereotype.Component; + +import com.sap.cds.ql.CQL; +import com.sap.cds.ql.cqn.CqnSelect; +import com.sap.cds.ql.cqn.CqnSelectListItem; +import com.sap.cds.ql.cqn.Modifier; +import com.sap.cds.ql.cqn.transformation.CqnAncestorsTransformation; +import com.sap.cds.ql.cqn.transformation.CqnTransformation; +import com.sap.cds.services.cds.CdsReadEventContext; + +import com.sap.cds.services.handler.EventHandler; +import com.sap.cds.services.handler.annotations.On; +import com.sap.cds.services.handler.annotations.ServiceName; + +import cds.gen.adminservice.AdminService_; +import cds.gen.adminservice.GenreHierarchy_; + + +@Component +@ServiceName(AdminService_.CDS_NAME) +/** + * For testing purposes of modifying requests for hierarchies + */ +@Profile("hybrid") +public class HierarchyExpandHandler implements EventHandler { + + @On(entity = GenreHierarchy_.CDS_NAME) + void removeExpand(CdsReadEventContext event) { + List trafos = event.getCqn().transformations(); + if (trafos.size() < 1) { + return; + } + if (trafos.get(0) instanceof CqnAncestorsTransformation) { + CqnSelect original = event.getCqn(); + CqnSelect copy = CQL.copy(original, new Modifier() { + public List items(List items) { + return items.stream().filter(i -> !i.isExpand()).toList(); + } + }); + event.setCqn(copy); + } + event.proceed(); + } +} diff --git a/srv/src/main/java/my/bookshop/handlers/HierarchyHandler.java b/srv/src/main/java/my/bookshop/handlers/HierarchyHandler.java index bdd875a0..391fb505 100644 --- a/srv/src/main/java/my/bookshop/handlers/HierarchyHandler.java +++ b/srv/src/main/java/my/bookshop/handlers/HierarchyHandler.java @@ -1,4 +1,4 @@ -package my.bookshop.handlers; +/* package my.bookshop.handlers; import java.util.ArrayDeque; import java.util.Comparator; @@ -54,6 +54,7 @@ * The handler is neither functionally complete nor correct for all requests. It * is not intended as a blue-print for custom code. */ +/* public class HierarchyHandler implements EventHandler { private final PersistenceService db; @@ -311,3 +312,4 @@ Deque getPath(GenreHierarchy gh) { } } } + */ \ No newline at end of file diff --git a/srv/src/test/java/my/bookshop/GenreHierarchyTest.java b/srv/src/test/java/my/bookshop/GenreHierarchyTest.java index 1c1abe72..95d7616f 100644 --- a/srv/src/test/java/my/bookshop/GenreHierarchyTest.java +++ b/srv/src/test/java/my/bookshop/GenreHierarchyTest.java @@ -1,4 +1,4 @@ -package my.bookshop; +/* package my.bookshop; import static org.assertj.core.api.Assumptions.assumeThat; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; @@ -237,4 +237,4 @@ void testStartTwoLevelsOrderByDescHANA() throws Exception { private boolean isOnHana() { return env.acceptsProfiles(Profiles.of("cloud")); } -} +} */ \ No newline at end of file diff --git a/srv/src/test/java/my/bookshop/handlers/HierarchyHandlerSorterTest.java b/srv/src/test/java/my/bookshop/handlers/HierarchyHandlerSorterTest.java index 46bc78c2..79115dd7 100644 --- a/srv/src/test/java/my/bookshop/handlers/HierarchyHandlerSorterTest.java +++ b/srv/src/test/java/my/bookshop/handlers/HierarchyHandlerSorterTest.java @@ -1,4 +1,4 @@ -package my.bookshop.handlers; +/* package my.bookshop.handlers; import static org.junit.jupiter.api.Assertions.assertEquals; import java.util.List; @@ -68,3 +68,4 @@ private static List sorted(GenreHierarchy... h) { return List.of(h).stream().sorted(new Sorter()).toList(); } } + */ \ No newline at end of file diff --git a/srv/src/test/java/my/bookshop/handlers/HierarchyHandlerTest.java b/srv/src/test/java/my/bookshop/handlers/HierarchyHandlerTest.java new file mode 100644 index 00000000..00d86738 --- /dev/null +++ b/srv/src/test/java/my/bookshop/handlers/HierarchyHandlerTest.java @@ -0,0 +1,64 @@ +/* package my.bookshop.handlers; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import java.util.List; +import org.junit.jupiter.api.Test; + +import cds.gen.adminservice.GenreHierarchy; +import my.bookshop.handlers.HierarchyHandler.Sorter; + +public class HierarchyHandlerTest { + + @Test + public void testSortingRoots() { + GenreHierarchy h1 = GenreHierarchy.create(); + h1.setName("Philosophical fiction"); + GenreHierarchy h2 = GenreHierarchy.create(); + h2.setName("Epic"); + List list = List.of(h1,h2); + List sorted = list.stream().sorted(new Sorter()).toList(); + assertEquals("Epic", sorted.get(0).getName()); + } + + @Test + public void testSortingChildren() { + GenreHierarchy root = GenreHierarchy.create(); + root.setName("Folklore"); + GenreHierarchy h1 = GenreHierarchy.create(); + h1.setName("Urban legend"); + h1.setParnt(root); + GenreHierarchy h2 = GenreHierarchy.create(); + h2.setName("Fairy tale"); + h2.setParnt(root); + List list = List.of(h1,h2); + List sorted = list.stream().sorted(new Sorter()).toList(); + assertEquals("Fairy tale", sorted.get(0).getName()); + } + + @Test + public void testSortingChildrenWithDifRoot() { + GenreHierarchy root1 = GenreHierarchy.create(); + root1.setName("Thriller"); + GenreHierarchy root2 = GenreHierarchy.create(); + root2.setName("Folklore"); + GenreHierarchy h2 = GenreHierarchy.create(); + h2.setName("Urban legend"); + h2.setParnt(root2); + List list = List.of(root1,h2); + List sorted = list.stream().sorted(new Sorter()).toList(); + assertEquals("Urban legend", sorted.get(0).getName()); + } + + @Test + public void testSortingChildrenSameRoot() { + GenreHierarchy root = GenreHierarchy.create(); + root.setName("Folklore"); + GenreHierarchy h1 = GenreHierarchy.create(); + h1.setName("Urban legend"); + h1.setParnt(root); + List list = List.of(h1,root); + List sorted = list.stream().sorted(new Sorter()).toList(); + assertEquals("Folklore", sorted.get(0).getName()); + } +} + */ \ No newline at end of file From 1ce8ee1f9644e38660ff0dc7d3945aa90f04d638 Mon Sep 17 00:00:00 2001 From: D070615 Date: Wed, 22 Jan 2025 12:43:59 +0100 Subject: [PATCH 02/22] add content entity --- app/admin/fiori-service.cds | 69 +++++++++++++++++++++++++++++++- app/admin/webapp/manifest.json | 48 +++++++++++++--------- db/books.cds | 14 +++++++ db/data/my.bookshop-Contents.csv | 23 +++++++++++ srv/admin-service.cds | 4 +- 5 files changed, 136 insertions(+), 22 deletions(-) create mode 100644 db/data/my.bookshop-Contents.csv diff --git a/app/admin/fiori-service.cds b/app/admin/fiori-service.cds index bee1aa19..2d1d0f0b 100644 --- a/app/admin/fiori-service.cds +++ b/app/admin/fiori-service.cds @@ -35,6 +35,11 @@ annotate AdminService.Books with @(UI : { $Type : 'UI.ReferenceFacet', Label : '{i18n>Admin}', Target : '@UI.FieldGroup#Admin' + }, + { + $Type : 'UI.ReferenceFacet', + Label : '{i18n>Contents}', + Target : 'contents/@UI.PresentationVariant' } ], FieldGroup #General : {Data : [ @@ -59,7 +64,11 @@ annotate AdminService.Books with @(UI : { ]} }); -// Add Value Help for Tree Table + +//////////////////////////////////////////////////////////////////////////// +// +// Value Help for Tree Table +// annotate AdminService.Books with { genre @(Common: { Label : 'Genre', @@ -98,6 +107,44 @@ annotate AdminService.GenreHierarchy with @UI: { }] }; +annotate AdminService.ContentsHierarchy with @UI: { + PresentationVariant : { + $Type : 'UI.PresentationVariantType', + RequestAtLeast: [name], + Visualizations: ['@UI.LineItem', ], + }, + LineItem : [{ + $Type: 'UI.DataField', + Value: name, + }, + { + $Type: 'UI.DataField', + Value: page, + }], + HeaderInfo : { + $Type : 'UI.HeaderInfoType', + TypeName : 'Organization Level', + TypeNamePlural: 'Organization Levels', + Title : { + $Type: 'UI.DataField', + Value: name, + } + }, + FieldGroup : { + $Type: 'UI.FieldGroupType', + Data : [{ + $Type: 'UI.DataField', + Value: page, + Label : 'Page Number' + }], + }, + Facets : [{ + $Type : 'UI.ReferenceFacet', + Target: '@UI.FieldGroup', + Label : 'Informations', + }], +}; + //////////////////////////////////////////////////////////// // // Draft for Localized Data @@ -137,6 +184,26 @@ 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, + Matched: Matched, + MatchedDescendantCount: MatchedDescendantCount, + LimitedRank: LimitedRank +}; + annotate AdminService.Books actions { @( Common.SideEffects : { diff --git a/app/admin/webapp/manifest.json b/app/admin/webapp/manifest.json index 4225597f..41009a9d 100644 --- a/app/admin/webapp/manifest.json +++ b/app/admin/webapp/manifest.json @@ -51,7 +51,7 @@ "settings": { "synchronizationMode": "None", "operationMode": "Server", - "autoExpandSelect" : true, + "autoExpandSelect": true, "earlyRequests": true, "groupProperties": { "default": { @@ -74,9 +74,9 @@ "target": "BooksDetails" }, { - "pattern": "Books({key}/author({key2}):?query:", - "name": "AuthorsDetails", - "target": "AuthorsDetails" + "pattern": "Books({key}/contents({key2}):?query:", + "name": "ContentsDetails", + "target": "ContentsDetails" } ], "targets": { @@ -85,12 +85,12 @@ "id": "BooksList", "name": "sap.fe.templates.ListReport", "options": { - "settings" : { - "entitySet" : "Books", - "navigation" : { - "Books" : { - "detail" : { - "route" : "BooksDetails" + "settings": { + "entitySet": "Books", + "navigation": { + "Books": { + "detail": { + "route": "BooksDetails" } } }, @@ -112,25 +112,33 @@ "id": "BooksDetailsList", "name": "sap.fe.templates.ObjectPage", "options": { - "settings" : { - "entitySet" : "Books", - "navigation" : { - "Authors" : { - "detail" : { - "route" : "AuthorsDetails" + "settings": { + "entitySet": "Books", + "navigation": { + "contents": { + "detail": { + "route": "ContentsDetails" + } + } + }, + "controlConfiguration": { + "contents/@com.sap.vocabularies.UI.v1.LineItem": { + "tableSettings": { + "hierarchyQualifier": "ContentsHierarchy", + "type": "TreeTable" } } } } } }, - "AuthorsDetails": { + "ContentsDetails": { "type": "Component", - "id": "AuthorsDetailsList", + "id": "ContentsDetails", "name": "sap.fe.templates.ObjectPage", "options": { - "settings" : { - "entitySet" : "Authors" + "settings": { + "entitySet": "ContentsHierarchy" } } } diff --git a/db/books.cds b/db/books.cds index 9a2bee75..31390374 100644 --- a/db/books.cds +++ b/db/books.cds @@ -22,6 +22,7 @@ entity Books : cuid, managed { reviews : Association to many Reviews on reviews.book = $self; isReviewable : TechnicalBooleanFlag not null default true; + contents : Composition of many Contents on contents.book = $self; } entity Authors : cuid, managed { @@ -51,4 +52,17 @@ annotate Authors with entity Genres : sap.common.CodeList { key ID : UUID; parent : Association to Genres; + children : Composition of many Genres on children.parent = $self; } + +/** + * Hierarchically organized entity for Contents + */ +entity Contents { + key ID : UUID; + name : String; + page : Integer; + parent : Association to Contents @odata.draft.enclosed; + book : Association to Books @odata.draft.enclosed; +} + diff --git a/db/data/my.bookshop-Contents.csv b/db/data/my.bookshop-Contents.csv new file mode 100644 index 00000000..bd522c22 --- /dev/null +++ b/db/data/my.bookshop-Contents.csv @@ -0,0 +1,23 @@ +ID;parent_ID;name;page;book_ID +f846b0b9-01d4-4f6d-82a4-d79204f62514;;Foreword;3;9b084139-0b1e-43b6-b12a-7b3669d75f02 +f846b0b9-01d4-4f6d-82a4-d79204f62515;;Chapter 1;4;9b084139-0b1e-43b6-b12a-7b3669d75f02 +f846b0b9-01d4-4f6d-82a4-d79204f62516;f846b0b9-01d4-4f6d-82a4-d79204f62515;Section 1.1;5;9b084139-0b1e-43b6-b12a-7b3669d75f02 +f846b0b9-01d4-4f6d-82a4-d79204f62517;f846b0b9-01d4-4f6d-82a4-d79204f62515;Section 1.2;28;9b084139-0b1e-43b6-b12a-7b3669d75f02 +f846b0b9-01d4-4f6d-82a4-d79204f62518;f846b0b9-01d4-4f6d-82a4-d79204f62517;Subsection 1.2.1;30;9b084139-0b1e-43b6-b12a-7b3669d75f02 +f846b0b9-01d4-4f6d-82a4-d79204f62519;f846b0b9-01d4-4f6d-82a4-d79204f62517;Subsection 1.2.2;33;9b084139-0b1e-43b6-b12a-7b3669d75f02 +f846b0b9-01d4-4f6d-82a4-d79204f62520;f846b0b9-01d4-4f6d-82a4-d79204f62515;Section 1.3;36;9b084139-0b1e-43b6-b12a-7b3669d75f02 +f846b0b9-01d4-4f6d-82a4-d79204f62521;;Chapter 2;54;9b084139-0b1e-43b6-b12a-7b3669d75f02 +f846b0b9-01d4-4f6d-82a4-d79204f62522;f846b0b9-01d4-4f6d-82a4-d79204f62521;Section 2.1;56;9b084139-0b1e-43b6-b12a-7b3669d75f02 +f846b0b9-01d4-4f6d-82a4-d79204f62523;f846b0b9-01d4-4f6d-82a4-d79204f62521;Section 2.2;58;9b084139-0b1e-43b6-b12a-7b3669d75f02 +f846b0b9-01d4-4f6d-82a4-d79204f62524;;Conclusion;63;9b084139-0b1e-43b6-b12a-7b3669d75f02 +f846b0b9-01d4-4f6d-82a4-d79204f62525;;Endnotes;65;9b084139-0b1e-43b6-b12a-7b3669d75f02 +f846b0b9-01d4-4f6d-82a4-d79204f62526;;Copyright notice;2;51061ce3-ddde-4d70-a2dc-6314afbcc73e +f846b0b9-01d4-4f6d-82a4-d79204f62527;;Chapter 1;3;51061ce3-ddde-4d70-a2dc-6314afbcc73e +f846b0b9-01d4-4f6d-82a4-d79204f62528;f846b0b9-01d4-4f6d-82a4-d79204f62527;Section 1.1;5;51061ce3-ddde-4d70-a2dc-6314afbcc73e +f846b0b9-01d4-4f6d-82a4-d79204f62529;f846b0b9-01d4-4f6d-82a4-d79204f62527;Section 1.2;13;51061ce3-ddde-4d70-a2dc-6314afbcc73e +f846b0b9-01d4-4f6d-82a4-d79204f62530;;Chapter 2;20;51061ce3-ddde-4d70-a2dc-6314afbcc73e +f846b0b9-01d4-4f6d-82a4-d79204f62531;f846b0b9-01d4-4f6d-82a4-d79204f62530;Section 2.1;21;51061ce3-ddde-4d70-a2dc-6314afbcc73e +f846b0b9-01d4-4f6d-82a4-d79204f62532;f846b0b9-01d4-4f6d-82a4-d79204f62530;Section 2.2;25;51061ce3-ddde-4d70-a2dc-6314afbcc73e +f846b0b9-01d4-4f6d-82a4-d79204f62533;f846b0b9-01d4-4f6d-82a4-d79204f62530;Section 2.3;27;51061ce3-ddde-4d70-a2dc-6314afbcc73e +f846b0b9-01d4-4f6d-82a4-d79204f62534;;Chapter 3;30;51061ce3-ddde-4d70-a2dc-6314afbcc73e +f846b0b9-01d4-4f6d-82a4-d79204f62535;;Endnotes;41;51061ce3-ddde-4d70-a2dc-6314afbcc73e \ No newline at end of file diff --git a/srv/admin-service.cds b/srv/admin-service.cds index eb705efa..380b5437 100644 --- a/srv/admin-service.cds +++ b/srv/admin-service.cds @@ -21,7 +21,9 @@ service AdminService @(requires: 'admin') { entity Authors as projection on my.Authors; entity Orders as select from my.Orders; extend my.Genres with Hierarchy; - entity GenreHierarchy as projection on my.Genres; + entity GenreHierarchy as projection on my.Genres excluding {children}; + extend my.Contents with Hierarchy; + entity ContentsHierarchy as projection on my.Contents; @cds.persistence.skip entity Upload @odata.singleton { From 4ef3a3c982c2a1bcab7165086719188d728cd193 Mon Sep 17 00:00:00 2001 From: D070615 Date: Thu, 6 Feb 2025 18:11:34 +0100 Subject: [PATCH 03/22] expand handler --- app/admin/fiori-service.cds | 7 ++++--- .../handlers/HierarchyExpandHandler.java | 16 ++++++++++------ 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/app/admin/fiori-service.cds b/app/admin/fiori-service.cds index 2d1d0f0b..05d2d358 100644 --- a/app/admin/fiori-service.cds +++ b/app/admin/fiori-service.cds @@ -104,7 +104,8 @@ annotate AdminService.GenreHierarchy with @UI: { LineItem : [{ $Type: 'UI.DataField', Value: name, - }] + Label : 'Genre' + }], }; annotate AdminService.ContentsHierarchy with @UI: { @@ -123,8 +124,8 @@ annotate AdminService.ContentsHierarchy with @UI: { }], HeaderInfo : { $Type : 'UI.HeaderInfoType', - TypeName : 'Organization Level', - TypeNamePlural: 'Organization Levels', + TypeName : 'Contents Level', + TypeNamePlural: 'Contents Levels', Title : { $Type: 'UI.DataField', Value: name, diff --git a/srv/src/main/java/my/bookshop/handlers/HierarchyExpandHandler.java b/srv/src/main/java/my/bookshop/handlers/HierarchyExpandHandler.java index 2daa240f..667b53bd 100644 --- a/srv/src/main/java/my/bookshop/handlers/HierarchyExpandHandler.java +++ b/srv/src/main/java/my/bookshop/handlers/HierarchyExpandHandler.java @@ -1,5 +1,4 @@ package my.bookshop.handlers; - import java.util.List; import org.springframework.context.annotation.Profile; @@ -21,10 +20,12 @@ import cds.gen.adminservice.GenreHierarchy_; + @Component @ServiceName(AdminService_.CDS_NAME) /** * For testing purposes of modifying requests for hierarchies + * Remove when generic solution for $apply and $expand is available */ @Profile("hybrid") public class HierarchyExpandHandler implements EventHandler { @@ -37,12 +38,15 @@ void removeExpand(CdsReadEventContext event) { } if (trafos.get(0) instanceof CqnAncestorsTransformation) { CqnSelect original = event.getCqn(); - CqnSelect copy = CQL.copy(original, new Modifier() { - public List items(List items) { - return items.stream().filter(i -> !i.isExpand()).toList(); - } - }); + Boolean isExpand = original.items().stream().filter(CqnSelectListItem::isExpand).findAny().isPresent(); + if (isExpand) { + CqnSelect copy = CQL.copy(original, new Modifier() { + public List items(List items) { + return items.stream().filter(i -> !i.isExpand()).toList(); + } + }); event.setCqn(copy); + } } event.proceed(); } From b3d6cbbc72542086e9548eda70429eb5a6b8be83 Mon Sep 17 00:00:00 2001 From: D070615 Date: Fri, 7 Feb 2025 15:57:40 +0100 Subject: [PATCH 04/22] add nextsiblingaction --- app/admin/fiori-service.cds | 6 ++++++ db/books.cds | 7 ++++--- db/data/my.bookshop-Genres.csv | 36 +++++++++++++++++----------------- srv/admin-service.cds | 11 ++++++++++- 4 files changed, 38 insertions(+), 22 deletions(-) diff --git a/app/admin/fiori-service.cds b/app/admin/fiori-service.cds index 05d2d358..c8d82e12 100644 --- a/app/admin/fiori-service.cds +++ b/app/admin/fiori-service.cds @@ -95,6 +95,12 @@ annotate AdminService.GenreHierarchy with { ID @UI.Hidden; }; +@Hierarchy.RecursiveHierarchyActions #GenreHierarchy: { + $Type : 'Hierarchy.RecursiveHierarchyActionsType', + // any name can be the action name with namespace/no bound action name + ChangeNextSiblingAction: 'AdminService.moveSiblingAction', +} + annotate AdminService.GenreHierarchy with @UI: { PresentationVariant #VH: { $Type : 'UI.PresentationVariantType', diff --git a/db/books.cds b/db/books.cds index 31390374..8bc090ea 100644 --- a/db/books.cds +++ b/db/books.cds @@ -50,9 +50,10 @@ annotate Authors with * Hierarchically organized Code List for Genres */ entity Genres : sap.common.CodeList { - key ID : UUID; - parent : Association to Genres; - children : Composition of many Genres on children.parent = $self; + key ID : UUID; + siblingRank : Integer; + parent : Association to Genres; + children : Composition of many Genres on children.parent = $self; } /** diff --git a/db/data/my.bookshop-Genres.csv b/db/data/my.bookshop-Genres.csv index 56e304e0..67653cae 100644 --- a/db/data/my.bookshop-Genres.csv +++ b/db/data/my.bookshop-Genres.csv @@ -1,18 +1,18 @@ -ID;parent_ID;name -f846b0b9-01d4-4f6d-82a4-d79204f62369;;Fiction -f846b0b9-01d4-4f6d-82a4-d79204f62570;f846b0b9-01d4-4f6d-82a4-d79204f62369;Drama -f846b0b9-01d4-4f6d-82a4-d79204f62571;f846b0b9-01d4-4f6d-82a4-d79204f62369;Poetry -f846b0b9-01d4-4f6d-82a4-d79204f62572;f846b0b9-01d4-4f6d-82a4-d79204f62369;Fantasy -f846b0b9-01d4-4f6d-82a4-d79204f62592;f846b0b9-01d4-4f6d-82a4-d79204f62572;Epic fantasy -f846b0b9-01d4-4f6d-82a4-d79204f62593;f846b0b9-01d4-4f6d-82a4-d79204f62572;High fantasy -f846b0b9-01d4-4f6d-82a4-d79204f62573;f846b0b9-01d4-4f6d-82a4-d79204f62369;Science Fiction -f846b0b9-01d4-4f6d-82a4-d79204f62574;f846b0b9-01d4-4f6d-82a4-d79204f62369;Romance -f846b0b9-01d4-4f6d-82a4-d79204f62575;f846b0b9-01d4-4f6d-82a4-d79204f62369;Mystery -f846b0b9-01d4-4f6d-82a4-d79204f62576;f846b0b9-01d4-4f6d-82a4-d79204f62369;Thriller -f846b0b9-01d4-4f6d-82a4-d79204f62577;f846b0b9-01d4-4f6d-82a4-d79204f62369;Dystopia -f846b0b9-01d4-4f6d-82a4-d79204f62578;f846b0b9-01d4-4f6d-82a4-d79204f62369;Fairy Tale -d846b0b9-01d4-4f6d-82a4-d79204f62487;;Non-Fiction -d846b0b9-01d4-4f6d-82a4-d79204f62588;d846b0b9-01d4-4f6d-82a4-d79204f62487;Biography -d846b0b9-01d4-4f6d-82a4-d79204f62589;d846b0b9-01d4-4f6d-82a4-d79204f62588;Autobiography -d846b0b9-01d4-4f6d-82a4-d79204f62590;d846b0b9-01d4-4f6d-82a4-d79204f62487;Essay -d846b0b9-01d4-4f6d-82a4-d79204f62591;d846b0b9-01d4-4f6d-82a4-d79204f62487;Speech \ No newline at end of file +ID;parent_ID;name;siblingRank +f846b0b9-01d4-4f6d-82a4-d79204f62369;;Fiction;1000 +f846b0b9-01d4-4f6d-82a4-d79204f62570;f846b0b9-01d4-4f6d-82a4-d79204f62369;Drama;2000 +f846b0b9-01d4-4f6d-82a4-d79204f62571;f846b0b9-01d4-4f6d-82a4-d79204f62369;Poetry;3000 +f846b0b9-01d4-4f6d-82a4-d79204f62572;f846b0b9-01d4-4f6d-82a4-d79204f62369;Fantasy;4000 +f846b0b9-01d4-4f6d-82a4-d79204f62592;f846b0b9-01d4-4f6d-82a4-d79204f62572;Epic fantasy;5000 +f846b0b9-01d4-4f6d-82a4-d79204f62593;f846b0b9-01d4-4f6d-82a4-d79204f62572;High fantasy;6000 +f846b0b9-01d4-4f6d-82a4-d79204f62573;f846b0b9-01d4-4f6d-82a4-d79204f62369;Science Fiction;7000 +f846b0b9-01d4-4f6d-82a4-d79204f62574;f846b0b9-01d4-4f6d-82a4-d79204f62369;Romance;8000 +f846b0b9-01d4-4f6d-82a4-d79204f62575;f846b0b9-01d4-4f6d-82a4-d79204f62369;Mystery;9000 +f846b0b9-01d4-4f6d-82a4-d79204f62576;f846b0b9-01d4-4f6d-82a4-d79204f62369;Thriller;10000 +f846b0b9-01d4-4f6d-82a4-d79204f62577;f846b0b9-01d4-4f6d-82a4-d79204f62369;Dystopia;11000 +f846b0b9-01d4-4f6d-82a4-d79204f62578;f846b0b9-01d4-4f6d-82a4-d79204f62369;Fairy Tale;12000 +d846b0b9-01d4-4f6d-82a4-d79204f62487;;Non-Fiction;13000 +d846b0b9-01d4-4f6d-82a4-d79204f62588;d846b0b9-01d4-4f6d-82a4-d79204f62487;Biography;14000 +d846b0b9-01d4-4f6d-82a4-d79204f62589;d846b0b9-01d4-4f6d-82a4-d79204f62588;Autobiography;15000 +d846b0b9-01d4-4f6d-82a4-d79204f62590;d846b0b9-01d4-4f6d-82a4-d79204f62487;Essay;16000 +d846b0b9-01d4-4f6d-82a4-d79204f62591;d846b0b9-01d4-4f6d-82a4-d79204f62487;Speech;17000 \ No newline at end of file diff --git a/srv/admin-service.cds b/srv/admin-service.cds index 380b5437..45b10b8f 100644 --- a/srv/admin-service.cds +++ b/srv/admin-service.cds @@ -21,7 +21,16 @@ service AdminService @(requires: 'admin') { entity Authors as projection on my.Authors; entity Orders as select from my.Orders; extend my.Genres with Hierarchy; - entity GenreHierarchy as projection on my.Genres excluding {children}; + + type NextSibling { + ID : UUID; + } + entity GenreHierarchy as projection on my.Genres + excluding {children} order by siblingRank + actions { + action moveSiblingAction(NextSibling : NextSibling) returns GenreHierarchy; + }; + extend my.Contents with Hierarchy; entity ContentsHierarchy as projection on my.Contents; From c2174cb6b35563f34fc253afd7d456f236dfb23b Mon Sep 17 00:00:00 2001 From: D070615 Date: Fri, 14 Feb 2025 10:23:11 +0100 Subject: [PATCH 05/22] custom handler for nextSiblingAction --- srv/admin-service.cds | 2 +- .../HierarchySiblingActionHandler.java | 66 +++++++++++++++++++ 2 files changed, 67 insertions(+), 1 deletion(-) create mode 100644 srv/src/main/java/my/bookshop/handlers/HierarchySiblingActionHandler.java diff --git a/srv/admin-service.cds b/srv/admin-service.cds index 45b10b8f..0bed6930 100644 --- a/srv/admin-service.cds +++ b/srv/admin-service.cds @@ -28,7 +28,7 @@ service AdminService @(requires: 'admin') { entity GenreHierarchy as projection on my.Genres excluding {children} order by siblingRank actions { - action moveSiblingAction(NextSibling : NextSibling) returns GenreHierarchy; + action moveSiblingAction(NextSibling : NextSibling); }; extend my.Contents with Hierarchy; diff --git a/srv/src/main/java/my/bookshop/handlers/HierarchySiblingActionHandler.java b/srv/src/main/java/my/bookshop/handlers/HierarchySiblingActionHandler.java new file mode 100644 index 00000000..5c97d196 --- /dev/null +++ b/srv/src/main/java/my/bookshop/handlers/HierarchySiblingActionHandler.java @@ -0,0 +1,66 @@ +package my.bookshop.handlers; + +import java.util.List; +import java.util.Optional; + +import org.springframework.context.annotation.Profile; +import org.springframework.stereotype.Component; + +import com.sap.cds.ql.Select; +import com.sap.cds.ql.Update; +import com.sap.cds.ql.cqn.CqnAnalyzer; +import com.sap.cds.ql.cqn.CqnSelect; +import com.sap.cds.ql.cqn.CqnUpdate; + +import com.sap.cds.services.handler.EventHandler; +import com.sap.cds.services.handler.annotations.On; +import com.sap.cds.services.handler.annotations.ServiceName; +import com.sap.cds.services.persistence.PersistenceService; + +import cds.gen.adminservice.AdminService_; +import cds.gen.adminservice.GenreHierarchy; +import cds.gen.adminservice.GenreHierarchyMoveSiblingActionContext; +import cds.gen.adminservice.GenreHierarchy_; + + +@Component +@ServiceName(AdminService_.CDS_NAME) +/** + * Example of a custom handler for nextSiblingAction + */ +@Profile("hybrid") +public class HierarchySiblingActionHandler implements EventHandler { + + private final PersistenceService db; + + HierarchySiblingActionHandler(PersistenceService db) { + this.db = db; + } + + @On(entity = GenreHierarchy_.CDS_NAME) + void onMoveSiblingAction(GenreHierarchyMoveSiblingActionContext event) { + CqnSelect select = event.getCqn(); + String toMoveId = (String) CqnAnalyzer.create(event.getModel()).analyze(select).targetKeys().get(GenreHierarchy_.ID); + CqnSelect parentCQN = Select.from(GenreHierarchy_.class).columns(c -> c.parent_ID()).where(c -> c.ID().eq(toMoveId)); + GenreHierarchy parentNode = db.run(parentCQN).single(GenreHierarchy.class); + CqnSelect childrenCQN = Select.from(GenreHierarchy_.class).columns(c -> c.ID(), c -> c.siblingRank()).where(c -> c.parent_ID().eq(parentNode.getParentId())); + List siblingNodes = db.run(childrenCQN).listOf(GenreHierarchy.class); + + List siblingRanks = siblingNodes.stream().map(ch -> ch.getSiblingRank()).toList(); + + GenreHierarchy nodeToMove = siblingNodes.stream().filter(el -> toMoveId.equals(el.getId())).findFirst().get(); + String nextSiblingId = event.getNextSibling() == null ? null : event.getNextSibling().getId(); + Optional nextSibling = siblingNodes.stream().filter(el -> el.getId().equals(nextSiblingId)).findFirst(); + + GenreHierarchy moved = siblingNodes.remove(siblingNodes.indexOf(nodeToMove)); + nextSibling.ifPresentOrElse(n -> siblingNodes.add(siblingNodes.indexOf(n), moved), () -> siblingNodes.addLast(moved)); + + for (int i=0; i < siblingRanks.size(); i++) { + siblingNodes.get(i).setSiblingRank(siblingRanks.get(i)); + } + + CqnUpdate updateCQN = Update.entity(GenreHierarchy_.class).entries(siblingNodes); + db.run(updateCQN); + event.setCompleted(); + } +} From bd620003019f5fa33bdc25254d8e1650b7e577d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrian=20G=C3=B6rler?= Date: Tue, 25 Feb 2025 22:50:22 +0100 Subject: [PATCH 06/22] Update pom.xml --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 2c536a3b..e4f17ba6 100644 --- a/pom.xml +++ b/pom.xml @@ -23,7 +23,7 @@ 21 - 3.7.1 + 3.8.0-m2509 5.15.0 3.5.7 3.8.4 From 01ff79f7ea8b8132676f8edfa044bb213e7be239 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrian=20G=C3=B6rler?= Date: Tue, 25 Feb 2025 22:51:53 +0100 Subject: [PATCH 07/22] Update HierarchyExpandHandler.java don't use HierarchyExpandHandler --- .../main/java/my/bookshop/handlers/HierarchyExpandHandler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/srv/src/main/java/my/bookshop/handlers/HierarchyExpandHandler.java b/srv/src/main/java/my/bookshop/handlers/HierarchyExpandHandler.java index 667b53bd..fa746cd4 100644 --- a/srv/src/main/java/my/bookshop/handlers/HierarchyExpandHandler.java +++ b/srv/src/main/java/my/bookshop/handlers/HierarchyExpandHandler.java @@ -30,7 +30,7 @@ @Profile("hybrid") public class HierarchyExpandHandler implements EventHandler { - @On(entity = GenreHierarchy_.CDS_NAME) +// @On(entity = GenreHierarchy_.CDS_NAME) void removeExpand(CdsReadEventContext event) { List trafos = event.getCqn().transformations(); if (trafos.size() < 1) { From f9f6d175f7c805d010c4aa5b2a86d95dff753a11 Mon Sep 17 00:00:00 2001 From: D070615 Date: Thu, 27 Feb 2025 09:38:46 +0100 Subject: [PATCH 08/22] review and ui fix --- app/admin/webapp/manifest.json | 4 +- app/common.cds | 2 - db/books.cds | 2 +- db/data/my.bookshop-Contents.csv | 43 ++++++++++++++- db/data/my.bookshop-Genres.csv | 49 +++++++++++------ srv/admin-service.cds | 8 +-- .../handlers/HierarchyExpandHandler.java | 53 ------------------- 7 files changed, 82 insertions(+), 79 deletions(-) delete mode 100644 srv/src/main/java/my/bookshop/handlers/HierarchyExpandHandler.java diff --git a/app/admin/webapp/manifest.json b/app/admin/webapp/manifest.json index 41009a9d..6278d154 100644 --- a/app/admin/webapp/manifest.json +++ b/app/admin/webapp/manifest.json @@ -74,7 +74,7 @@ "target": "BooksDetails" }, { - "pattern": "Books({key}/contents({key2}):?query:", + "pattern": "Books({key})/contents({key2}):?query:", "name": "ContentsDetails", "target": "ContentsDetails" } @@ -138,7 +138,7 @@ "name": "sap.fe.templates.ObjectPage", "options": { "settings": { - "entitySet": "ContentsHierarchy" + "contextPath": "/Books/contents" } } } diff --git a/app/common.cds b/app/common.cds index ba638052..f9df40b2 100644 --- a/app/common.cds +++ b/app/common.cds @@ -206,8 +206,6 @@ annotate my.Genres with // Genres Elements // annotate my.Genres with { - ID - @title : '{i18n>ID}'; name @title : '{i18n>Genre}'; } diff --git a/db/books.cds b/db/books.cds index 8bc090ea..61d4bd5f 100644 --- a/db/books.cds +++ b/db/books.cds @@ -64,6 +64,6 @@ entity Contents { name : String; page : Integer; parent : Association to Contents @odata.draft.enclosed; - book : Association to Books @odata.draft.enclosed; + book : Association to Books; } diff --git a/db/data/my.bookshop-Contents.csv b/db/data/my.bookshop-Contents.csv index bd522c22..93700a76 100644 --- a/db/data/my.bookshop-Contents.csv +++ b/db/data/my.bookshop-Contents.csv @@ -20,4 +20,45 @@ f846b0b9-01d4-4f6d-82a4-d79204f62531;f846b0b9-01d4-4f6d-82a4-d79204f62530;Sectio f846b0b9-01d4-4f6d-82a4-d79204f62532;f846b0b9-01d4-4f6d-82a4-d79204f62530;Section 2.2;25;51061ce3-ddde-4d70-a2dc-6314afbcc73e f846b0b9-01d4-4f6d-82a4-d79204f62533;f846b0b9-01d4-4f6d-82a4-d79204f62530;Section 2.3;27;51061ce3-ddde-4d70-a2dc-6314afbcc73e f846b0b9-01d4-4f6d-82a4-d79204f62534;;Chapter 3;30;51061ce3-ddde-4d70-a2dc-6314afbcc73e -f846b0b9-01d4-4f6d-82a4-d79204f62535;;Endnotes;41;51061ce3-ddde-4d70-a2dc-6314afbcc73e \ No newline at end of file +f846b0b9-01d4-4f6d-82a4-d79204f62535;;Endnotes;41;51061ce3-ddde-4d70-a2dc-6314afbcc73e +f846b0b9-01d4-4f6d-82a4-d79204f62551;;Acknowledgements;1;4a519e61-3c3a-4bd9-ab12-d7e0c5329933 +f846b0b9-01d4-4f6d-82a4-d79204f62552;;The Flight;2;4a519e61-3c3a-4bd9-ab12-d7e0c5329933 +f846b0b9-01d4-4f6d-82a4-d79204f62553;;Hexwood Farm;8;4a519e61-3c3a-4bd9-ab12-d7e0c5329933 +f846b0b9-01d4-4f6d-82a4-d79204f62554;f846b0b9-01d4-4f6d-82a4-d79204f62553;Castle Saburac;13;4a519e61-3c3a-4bd9-ab12-d7e0c5329933 +f846b0b9-01d4-4f6d-82a4-d79204f62555;f846b0b9-01d4-4f6d-82a4-d79204f62553;The Curse of Rapkyn;27;4a519e61-3c3a-4bd9-ab12-d7e0c5329933 +f846b0b9-01d4-4f6d-82a4-d79204f62556;f846b0b9-01d4-4f6d-82a4-d79204f62553;The Mannikin;35;4a519e61-3c3a-4bd9-ab12-d7e0c5329933 +f846b0b9-01d4-4f6d-82a4-d79204f62557;;The Eye of Time;44;4a519e61-3c3a-4bd9-ab12-d7e0c5329933 +f846b0b9-01d4-4f6d-82a4-d79204f62558;;The Enchanting;59;4a519e61-3c3a-4bd9-ab12-d7e0c5329933 +f846b0b9-01d4-4f6d-82a4-d79204f62559;f846b0b9-01d4-4f6d-82a4-d79204f62558;The Telling Bone;73;4a519e61-3c3a-4bd9-ab12-d7e0c5329933 +f846b0b9-01d4-4f6d-82a4-d79204f62560;f846b0b9-01d4-4f6d-82a4-d79204f62558;The Power of Adamcos;86;4a519e61-3c3a-4bd9-ab12-d7e0c5329933 +f846b0b9-01d4-4f6d-82a4-d79204f62561;f846b0b9-01d4-4f6d-82a4-d79204f62558;The House of the Sorcerer;98;4a519e61-3c3a-4bd9-ab12-d7e0c5329933 +f846b0b9-01d4-4f6d-82a4-d79204f62562;;The Changeling;105;4a519e61-3c3a-4bd9-ab12-d7e0c5329933 +f846b0b9-01d4-4f6d-82a4-d79204f62563;f846b0b9-01d4-4f6d-82a4-d79204f62562;The Flying Broomsticks;118;4a519e61-3c3a-4bd9-ab12-d7e0c5329933 +f846b0b9-01d4-4f6d-82a4-d79204f62564;f846b0b9-01d4-4f6d-82a4-d79204f62563;The Fish Out of Water;126;4a519e61-3c3a-4bd9-ab12-d7e0c5329933 +f846b0b9-01d4-4f6d-82a4-d79204f62565;;The Final Magic;138;4a519e61-3c3a-4bd9-ab12-d7e0c5329933 +f846b0b9-01d4-4f6d-82a4-d79204f62566;;Copyright;159;4a519e61-3c3a-4bd9-ab12-d7e0c5329933 +f846b0b9-01d4-4f6d-82a4-d79204f62567;;Editor's Note;1;f846b0b9-01d4-4f6d-82a4-d79204f62278 +f846b0b9-01d4-4f6d-82a4-d79204f62568;;Chapter I;2;f846b0b9-01d4-4f6d-82a4-d79204f62278 +f846b0b9-01d4-4f6d-82a4-d79204f62569;;Chapter II;31;f846b0b9-01d4-4f6d-82a4-d79204f62278 +f846b0b9-01d4-4f6d-82a4-d79204f62570;f846b0b9-01d4-4f6d-82a4-d79204f62569;Section II.I;47;f846b0b9-01d4-4f6d-82a4-d79204f62278 +f846b0b9-01d4-4f6d-82a4-d79204f62571;f846b0b9-01d4-4f6d-82a4-d79204f62569;Section II.II;62;f846b0b9-01d4-4f6d-82a4-d79204f62278 +f846b0b9-01d4-4f6d-82a4-d79204f62572;f846b0b9-01d4-4f6d-82a4-d79204f62569;Section II.III;75;f846b0b9-01d4-4f6d-82a4-d79204f62278 +f846b0b9-01d4-4f6d-82a4-d79204f62573;f846b0b9-01d4-4f6d-82a4-d79204f62569;Section II.IV;87;f846b0b9-01d4-4f6d-82a4-d79204f62278 +f846b0b9-01d4-4f6d-82a4-d79204f62574;;Chapter III;105;f846b0b9-01d4-4f6d-82a4-d79204f62278 +f846b0b9-01d4-4f6d-82a4-d79204f62575;f846b0b9-01d4-4f6d-82a4-d79204f62574;Section III.I;128;f846b0b9-01d4-4f6d-82a4-d79204f62278 +f846b0b9-01d4-4f6d-82a4-d79204f62576;f846b0b9-01d4-4f6d-82a4-d79204f62575;Subsection III.I.I;156;f846b0b9-01d4-4f6d-82a4-d79204f62278 +f846b0b9-01d4-4f6d-82a4-d79204f62577;f846b0b9-01d4-4f6d-82a4-d79204f62575;Subsection III.I.II;173;f846b0b9-01d4-4f6d-82a4-d79204f62278 +f846b0b9-01d4-4f6d-82a4-d79204f62578;f846b0b9-01d4-4f6d-82a4-d79204f62574;Section III.II;185;f846b0b9-01d4-4f6d-82a4-d79204f62278 +f846b0b9-01d4-4f6d-82a4-d79204f62579;;Chapter IV;203;f846b0b9-01d4-4f6d-82a4-d79204f62278 +f846b0b9-01d4-4f6d-82a4-d79204f62580;;Acknowledgments;250;f846b0b9-01d4-4f6d-82a4-d79204f62278 +f846b0b9-01d4-4f6d-82a4-d79204f62582;;Foreword;1;aebdfc8a-0dfa-4468-bd36-48aabd65e663 +f846b0b9-01d4-4f6d-82a4-d79204f62583;;Chapter 1;3;aebdfc8a-0dfa-4468-bd36-48aabd65e663 +f846b0b9-01d4-4f6d-82a4-d79204f62584;f846b0b9-01d4-4f6d-82a4-d79204f62583;Section 1.1;5;aebdfc8a-0dfa-4468-bd36-48aabd65e663 +f846b0b9-01d4-4f6d-82a4-d79204f62585;f846b0b9-01d4-4f6d-82a4-d79204f62583;Section 1.2;8;aebdfc8a-0dfa-4468-bd36-48aabd65e663 +f846b0b9-01d4-4f6d-82a4-d79204f62586;;Chapter 2;10;aebdfc8a-0dfa-4468-bd36-48aabd65e663 +f846b0b9-01d4-4f6d-82a4-d79204f62587;f846b0b9-01d4-4f6d-82a4-d79204f62586;Section 2.1;12;aebdfc8a-0dfa-4468-bd36-48aabd65e663 +f846b0b9-01d4-4f6d-82a4-d79204f62588;f846b0b9-01d4-4f6d-82a4-d79204f62587;Subsection 2.1.1;14;aebdfc8a-0dfa-4468-bd36-48aabd65e663 +f846b0b9-01d4-4f6d-82a4-d79204f62589;f846b0b9-01d4-4f6d-82a4-d79204f62587;Subsection 2.1.2;16;aebdfc8a-0dfa-4468-bd36-48aabd65e663 +f846b0b9-01d4-4f6d-82a4-d79204f62590;f846b0b9-01d4-4f6d-82a4-d79204f62586;Section 2.2;18;aebdfc8a-0dfa-4468-bd36-48aabd65e663 +f846b0b9-01d4-4f6d-82a4-d79204f62591;f846b0b9-01d4-4f6d-82a4-d79204f62590;Subsection 2.2.1;21;aebdfc8a-0dfa-4468-bd36-48aabd65e663 +f846b0b9-01d4-4f6d-82a4-d79204f62592;;Endnotes;25;aebdfc8a-0dfa-4468-bd36-48aabd65e663 diff --git a/db/data/my.bookshop-Genres.csv b/db/data/my.bookshop-Genres.csv index 67653cae..963aad8d 100644 --- a/db/data/my.bookshop-Genres.csv +++ b/db/data/my.bookshop-Genres.csv @@ -1,18 +1,35 @@ ID;parent_ID;name;siblingRank f846b0b9-01d4-4f6d-82a4-d79204f62369;;Fiction;1000 -f846b0b9-01d4-4f6d-82a4-d79204f62570;f846b0b9-01d4-4f6d-82a4-d79204f62369;Drama;2000 -f846b0b9-01d4-4f6d-82a4-d79204f62571;f846b0b9-01d4-4f6d-82a4-d79204f62369;Poetry;3000 -f846b0b9-01d4-4f6d-82a4-d79204f62572;f846b0b9-01d4-4f6d-82a4-d79204f62369;Fantasy;4000 -f846b0b9-01d4-4f6d-82a4-d79204f62592;f846b0b9-01d4-4f6d-82a4-d79204f62572;Epic fantasy;5000 -f846b0b9-01d4-4f6d-82a4-d79204f62593;f846b0b9-01d4-4f6d-82a4-d79204f62572;High fantasy;6000 -f846b0b9-01d4-4f6d-82a4-d79204f62573;f846b0b9-01d4-4f6d-82a4-d79204f62369;Science Fiction;7000 -f846b0b9-01d4-4f6d-82a4-d79204f62574;f846b0b9-01d4-4f6d-82a4-d79204f62369;Romance;8000 -f846b0b9-01d4-4f6d-82a4-d79204f62575;f846b0b9-01d4-4f6d-82a4-d79204f62369;Mystery;9000 -f846b0b9-01d4-4f6d-82a4-d79204f62576;f846b0b9-01d4-4f6d-82a4-d79204f62369;Thriller;10000 -f846b0b9-01d4-4f6d-82a4-d79204f62577;f846b0b9-01d4-4f6d-82a4-d79204f62369;Dystopia;11000 -f846b0b9-01d4-4f6d-82a4-d79204f62578;f846b0b9-01d4-4f6d-82a4-d79204f62369;Fairy Tale;12000 -d846b0b9-01d4-4f6d-82a4-d79204f62487;;Non-Fiction;13000 -d846b0b9-01d4-4f6d-82a4-d79204f62588;d846b0b9-01d4-4f6d-82a4-d79204f62487;Biography;14000 -d846b0b9-01d4-4f6d-82a4-d79204f62589;d846b0b9-01d4-4f6d-82a4-d79204f62588;Autobiography;15000 -d846b0b9-01d4-4f6d-82a4-d79204f62590;d846b0b9-01d4-4f6d-82a4-d79204f62487;Essay;16000 -d846b0b9-01d4-4f6d-82a4-d79204f62591;d846b0b9-01d4-4f6d-82a4-d79204f62487;Speech;17000 \ No newline at end of file +f846b0b9-01d4-4f6d-82a4-d79204f62360;f846b0b9-01d4-4f6d-82a4-d79204f62369;Action;2000 +f846b0b9-01d4-4f6d-82a4-d79204f62359;f846b0b9-01d4-4f6d-82a4-d79204f62369;Adventure;3000 +f846b0b9-01d4-4f6d-82a4-d79204f62570;f846b0b9-01d4-4f6d-82a4-d79204f62369;Drama;4000 +f846b0b9-01d4-4f6d-82a4-d79204f62571;f846b0b9-01d4-4f6d-82a4-d79204f62369;Poetry;5000 +f846b0b9-01d4-4f6d-82a4-d79204f62572;f846b0b9-01d4-4f6d-82a4-d79204f62369;Fantasy;6000 +f846b0b9-01d4-4f6d-82a4-d79204f62592;f846b0b9-01d4-4f6d-82a4-d79204f62572;Epic fantasy;7000 +f846b0b9-01d4-4f6d-82a4-d79204f62593;f846b0b9-01d4-4f6d-82a4-d79204f62572;High fantasy;8000 +f846b0b9-01d4-4f6d-82a4-d79204f62573;f846b0b9-01d4-4f6d-82a4-d79204f62369;Science Fiction;9000 +f846b0b9-01d4-4f6d-82a4-d79204f62357;f846b0b9-01d4-4f6d-82a4-d79204f62573;Utopian and Dystopian;10000 +f846b0b9-01d4-4f6d-82a4-d79204f62355;f846b0b9-01d4-4f6d-82a4-d79204f62357;Dystopia;11000 +f846b0b9-01d4-4f6d-82a4-d79204f62353;f846b0b9-01d4-4f6d-82a4-d79204f62355;Cyberpunk;12000 +f846b0b9-01d4-4f6d-82a4-d79204f62351;f846b0b9-01d4-4f6d-82a4-d79204f62353;Steampunk;13000 +f846b0b9-01d4-4f6d-82a4-d79204f62574;f846b0b9-01d4-4f6d-82a4-d79204f62369;Romance;14000 +f846b0b9-01d4-4f6d-82a4-d79204f62575;f846b0b9-01d4-4f6d-82a4-d79204f62369;Mystery;15000 +f846b0b9-01d4-4f6d-82a4-d79204f62515;f846b0b9-01d4-4f6d-82a4-d79204f62369;Graphic Novel;16000 +f846b0b9-01d4-4f6d-82a4-d79204f62576;f846b0b9-01d4-4f6d-82a4-d79204f62369;Thriller;17000 +f846b0b9-01d4-4f6d-82a4-d79204f62520;f846b0b9-01d4-4f6d-82a4-d79204f62576;Suspense;18000 +f846b0b9-01d4-4f6d-82a4-d79204f62523;f846b0b9-01d4-4f6d-82a4-d79204f62576;Crime thriller;19000 +f846b0b9-01d4-4f6d-82a4-d79204f62525;f846b0b9-01d4-4f6d-82a4-d79204f62576;Spy thriller;20000 +f846b0b9-01d4-4f6d-82a4-d79204f62527;f846b0b9-01d4-4f6d-82a4-d79204f62576;Political thriller;21000 +f846b0b9-01d4-4f6d-82a4-d79204f62577;f846b0b9-01d4-4f6d-82a4-d79204f62369;Dystopia;22000 +f846b0b9-01d4-4f6d-82a4-d79204f62512;f846b0b9-01d4-4f6d-82a4-d79204f62369;Short Story;23000 +f846b0b9-01d4-4f6d-82a4-d79204f62578;f846b0b9-01d4-4f6d-82a4-d79204f62369;Fairy Tale;24000 +f846b0b9-01d4-4f6d-82a4-d79204f62529;f846b0b9-01d4-4f6d-82a4-d79204f62369;Horror;25000 +f846b0b9-01d4-4f6d-82a4-d79204f62531;f846b0b9-01d4-4f6d-82a4-d79204f62369;Historical Fiction;26000 +f846b0b9-01d4-4f6d-82a4-d79204f62533;f846b0b9-01d4-4f6d-82a4-d79204f62369;Contemporary Fiction;27000 +f846b0b9-01d4-4f6d-82a4-d79204f62535;f846b0b9-01d4-4f6d-82a4-d79204f62369;Magical Realism;28000 +f846b0b9-01d4-4f6d-82a4-d79204f62537;f846b0b9-01d4-4f6d-82a4-d79204f62369;Literary Fiction;29000 +d846b0b9-01d4-4f6d-82a4-d79204f62487;;Non-Fiction;30000 +d846b0b9-01d4-4f6d-82a4-d79204f62588;d846b0b9-01d4-4f6d-82a4-d79204f62487;Biography;31000 +d846b0b9-01d4-4f6d-82a4-d79204f62589;d846b0b9-01d4-4f6d-82a4-d79204f62588;Autobiography;32000 +d846b0b9-01d4-4f6d-82a4-d79204f62590;d846b0b9-01d4-4f6d-82a4-d79204f62487;Essay;33000 +d846b0b9-01d4-4f6d-82a4-d79204f62591;d846b0b9-01d4-4f6d-82a4-d79204f62487;Speech;34000 \ No newline at end of file diff --git a/srv/admin-service.cds b/srv/admin-service.cds index 0bed6930..3927926e 100644 --- a/srv/admin-service.cds +++ b/srv/admin-service.cds @@ -1,4 +1,4 @@ -using {sap.common.Languages as CommonLanguages} from '@sap/cds/common'; +using {sap.common.Languages as CommonLanguages, cuid} from '@sap/cds/common'; using {my.bookshop as my} from '../db/index'; using {sap.changelog as changelog} from 'com.sap.cds/change-tracking'; using {my.common.Hierarchy as Hierarchy} from './hierarchy'; @@ -22,12 +22,12 @@ service AdminService @(requires: 'admin') { entity Orders as select from my.Orders; extend my.Genres with Hierarchy; - type NextSibling { - ID : UUID; - } + type NextSibling : cuid { }; entity GenreHierarchy as projection on my.Genres excluding {children} order by siblingRank actions { + // Experimental UI feature, see: + // https://github.com/SAP/odata-vocabularies/blob/main/vocabularies/Hierarchy.md#template_changenextsiblingaction-experimental action moveSiblingAction(NextSibling : NextSibling); }; diff --git a/srv/src/main/java/my/bookshop/handlers/HierarchyExpandHandler.java b/srv/src/main/java/my/bookshop/handlers/HierarchyExpandHandler.java deleted file mode 100644 index fa746cd4..00000000 --- a/srv/src/main/java/my/bookshop/handlers/HierarchyExpandHandler.java +++ /dev/null @@ -1,53 +0,0 @@ -package my.bookshop.handlers; -import java.util.List; - -import org.springframework.context.annotation.Profile; -import org.springframework.stereotype.Component; - -import com.sap.cds.ql.CQL; -import com.sap.cds.ql.cqn.CqnSelect; -import com.sap.cds.ql.cqn.CqnSelectListItem; -import com.sap.cds.ql.cqn.Modifier; -import com.sap.cds.ql.cqn.transformation.CqnAncestorsTransformation; -import com.sap.cds.ql.cqn.transformation.CqnTransformation; -import com.sap.cds.services.cds.CdsReadEventContext; - -import com.sap.cds.services.handler.EventHandler; -import com.sap.cds.services.handler.annotations.On; -import com.sap.cds.services.handler.annotations.ServiceName; - -import cds.gen.adminservice.AdminService_; -import cds.gen.adminservice.GenreHierarchy_; - - - -@Component -@ServiceName(AdminService_.CDS_NAME) -/** - * For testing purposes of modifying requests for hierarchies - * Remove when generic solution for $apply and $expand is available - */ -@Profile("hybrid") -public class HierarchyExpandHandler implements EventHandler { - -// @On(entity = GenreHierarchy_.CDS_NAME) - void removeExpand(CdsReadEventContext event) { - List trafos = event.getCqn().transformations(); - if (trafos.size() < 1) { - return; - } - if (trafos.get(0) instanceof CqnAncestorsTransformation) { - CqnSelect original = event.getCqn(); - Boolean isExpand = original.items().stream().filter(CqnSelectListItem::isExpand).findAny().isPresent(); - if (isExpand) { - CqnSelect copy = CQL.copy(original, new Modifier() { - public List items(List items) { - return items.stream().filter(i -> !i.isExpand()).toList(); - } - }); - event.setCqn(copy); - } - } - event.proceed(); - } -} From 0fb97de7f76a834f0e809db489ffe5c6a6298799 Mon Sep 17 00:00:00 2001 From: D070615 Date: Thu, 27 Feb 2025 12:12:42 +0100 Subject: [PATCH 09/22] readme --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index b652330c..62544a83 100644 --- a/README.md +++ b/README.md @@ -90,9 +90,10 @@ User Interface related Features: - [Model Localization](https://cap.cloud.sap/docs/guides/i18n) for [English](app/_i18n/i18n.properties) and [German](app/_i18n/i18n_de.properties) language for static texts - [Custom File Upload extension](app/admin/webapp/extension/Upload.js) which provides a button for uploading `CSV` files - A simple Swagger UI for the CatalogService API at -- UI5 [Tree Table](app/genres/webapp/manifest.json) with Value Help for [GenreHierarchy](app/admin/fiori-service.cds) +- UI5 [Tree Table](app/genres/webapp/manifest.json) with CRUD requests and Value Help for [GenreHierarchy](app/admin/fiori-service.cds) - [Custom event handlers](https://cap.cloud.sap/docs/java/provisioning-api) for Tree Table such as the [Custom business logic for GenreHierarchy](srv/src/main/java/my/bookshop/handlers/HierarchyHandler.java). Please note, that Tree Tables must be used with HANA. Custom event handler in this case provides a limited support ment for local testing. +- [Custom actions](https://cap.cloud.sap/docs/cds/cdl#actions) such as `moveSiblingAction` in the [Admin Service](srv/admin-service.cds). This action demonstrates experimental UI feature [ChangeNextSiblingAction](https://github.com/SAP/odata-vocabularies/blob/main/vocabularies/Hierarchy.md#template_changenextsiblingaction-experimental). The Action implementation is in the [Hierarchy Sibling Action Handler](srv/src/main/java/my/bookshop/handlers/HierarchySiblingActionHandler.java) CDS Maven Plugin Features: From a84c110fa862f6d237d6f78459ca257c1e8b883d Mon Sep 17 00:00:00 2001 From: D070615 Date: Mon, 3 Mar 2025 16:40:52 +0100 Subject: [PATCH 10/22] new ui and change in h2 handler --- app/appconfig/fioriSandboxConfig.json | 29 +++- app/browse-genres/fiori-service.cds | 22 +++ app/browse-genres/package.json | 12 ++ app/browse-genres/webapp/Component.js | 3 + app/browse-genres/webapp/i18n/i18n.properties | 2 + .../webapp/i18n/i18n_de.properties | 2 + app/browse-genres/webapp/index.html | 35 +++++ app/browse-genres/webapp/manifest.json | 129 ++++++++++++++++++ app/browse/fiori-service.cds | 13 ++ app/fiori.html | 4 +- app/genres/fiori-service.cds | 2 +- app/genres/webapp/i18n/i18n.properties | 4 +- app/genres/webapp/i18n/i18n_de.properties | 4 +- app/index.cds | 1 + app/xs-app.json | 5 + db/data/my.bookshop-Genres.csv | 1 - srv/cat-service.cds | 6 + .../bookshop/handlers/HierarchyHandler.java | 38 +++--- .../java/my/bookshop/GenreHierarchyTest.java | 49 +++---- .../handlers/HierarchyHandlerSorterTest.java | 5 +- .../handlers/HierarchyHandlerTest.java | 64 --------- 21 files changed, 304 insertions(+), 126 deletions(-) create mode 100644 app/browse-genres/fiori-service.cds create mode 100644 app/browse-genres/package.json create mode 100644 app/browse-genres/webapp/Component.js create mode 100644 app/browse-genres/webapp/i18n/i18n.properties create mode 100644 app/browse-genres/webapp/i18n/i18n_de.properties create mode 100644 app/browse-genres/webapp/index.html create mode 100644 app/browse-genres/webapp/manifest.json delete mode 100644 srv/src/test/java/my/bookshop/handlers/HierarchyHandlerTest.java diff --git a/app/appconfig/fioriSandboxConfig.json b/app/appconfig/fioriSandboxConfig.json index 48b2ef6a..5c030843 100644 --- a/app/appconfig/fioriSandboxConfig.json +++ b/app/appconfig/fioriSandboxConfig.json @@ -20,7 +20,8 @@ "title": "Browse Books", "description": "Find your favorite book" } - }, { + }, + { "id": "browse-genres", "tileType": "sap.ushell.ui.tile.StaticTile", "properties": { @@ -64,6 +65,15 @@ "title": "Manage Reviews", "description": "Add/edit/delete reviews" } + }, + { + "id": "manage-genres", + "tileType": "sap.ushell.ui.tile.StaticTile", + "properties": { + "targetURL": "#Genres-manage", + "title": "Manage Genres", + "description": "Add/edit/delete genres" + } } ] }, @@ -129,8 +139,8 @@ }, "resolutionResult": { "applicationType": "SAPUI5", - "additionalInformation": "SAPUI5.Component=genres", - "url": "/genres/webapp" + "additionalInformation": "SAPUI5.Component=browse-genres", + "url": "/browse-genres/webapp" } }, "manage-books": { @@ -146,6 +156,19 @@ "url": "/admin/webapp" } }, + "manage-genres": { + "semanticObject": "Genres", + "action": "manage", + "signature": { + "parameters": {}, + "additionalParameters": "allowed" + }, + "resolutionResult": { + "applicationType": "SAPUI5", + "additionalInformation": "SAPUI5.Component=genres", + "url": "/genres/webapp" + } + }, "manage-orders": { "semanticObject": "Orders", "action": "manage", diff --git a/app/browse-genres/fiori-service.cds b/app/browse-genres/fiori-service.cds new file mode 100644 index 00000000..be8a99ca --- /dev/null +++ b/app/browse-genres/fiori-service.cds @@ -0,0 +1,22 @@ +/* + UI annotations for the Browse GenreHierarchy App +*/ + +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.RecursiveHierarchy#GenreHierarchy: { + $Type: 'Hierarchy.RecursiveHierarchyType', + LimitedDescendantCount: LimitedDescendantCount, + DistanceFromRoot: DistanceFromRoot, + DrillState: DrillState, + Matched: Matched, + MatchedDescendantCount: MatchedDescendantCount, + LimitedRank: LimitedRank +}; diff --git a/app/browse-genres/package.json b/app/browse-genres/package.json new file mode 100644 index 00000000..2809c302 --- /dev/null +++ b/app/browse-genres/package.json @@ -0,0 +1,12 @@ +{ + "name": "browse-genres", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC" +} diff --git a/app/browse-genres/webapp/Component.js b/app/browse-genres/webapp/Component.js new file mode 100644 index 00000000..5fa2ad5d --- /dev/null +++ b/app/browse-genres/webapp/Component.js @@ -0,0 +1,3 @@ +sap.ui.define(["sap/fe/core/AppComponent"], ac => ac.extend("browse-genres.Component", { + metadata:{ manifest:'json' } +})) diff --git a/app/browse-genres/webapp/i18n/i18n.properties b/app/browse-genres/webapp/i18n/i18n.properties new file mode 100644 index 00000000..d2792ee9 --- /dev/null +++ b/app/browse-genres/webapp/i18n/i18n.properties @@ -0,0 +1,2 @@ +appTitle=Browse Genres +appDescription=Genres as Tree View diff --git a/app/browse-genres/webapp/i18n/i18n_de.properties b/app/browse-genres/webapp/i18n/i18n_de.properties new file mode 100644 index 00000000..e8714e92 --- /dev/null +++ b/app/browse-genres/webapp/i18n/i18n_de.properties @@ -0,0 +1,2 @@ +appTitle=Zeige Genres +appDescription=Genres als Baumansicht diff --git a/app/browse-genres/webapp/index.html b/app/browse-genres/webapp/index.html new file mode 100644 index 00000000..167596a7 --- /dev/null +++ b/app/browse-genres/webapp/index.html @@ -0,0 +1,35 @@ + + + + + + + Browse Genres + + + + +
+ + diff --git a/app/browse-genres/webapp/manifest.json b/app/browse-genres/webapp/manifest.json new file mode 100644 index 00000000..6beeef1c --- /dev/null +++ b/app/browse-genres/webapp/manifest.json @@ -0,0 +1,129 @@ +{ + "_version": "1.8.0", + "sap.app": { + "id": "browse-genres", + "type": "application", + "title": "{{appTitle}}", + "description": "{{appDescription}}", + "applicationVersion": { + "version": "1.0.0" + }, + "dataSources": { + "CatalogService": { + "uri": "/api/browse/", + "type": "OData", + "settings": { + "odataVersion": "4.0" + } + } + }, + "-sourceTemplate": { + "id": "ui5template.basicSAPUI5ApplicationProject", + "-id": "ui5template.smartTemplate", + "-version": "1.40.12" + }, + "crossNavigation": { + "inbounds": { + "Genres-show": { + "signature": { + "parameters": {}, + "additionalParameters": "allowed" + }, + "semanticObject": "GenreHierarchy", + "action": "show" + } + } + } + }, + "sap.ui5": { + "dependencies": { + "minUI5Version": "1.122.0", + "libs": { + "sap.fe.templates": {} + } + }, + "models": { + "i18n": { + "type": "sap.ui.model.resource.ResourceModel", + "uri": "i18n/i18n.properties" + }, + "": { + "dataSource": "CatalogService", + "settings": { + "synchronizationMode": "None", + "operationMode": "Server", + "autoExpandSelect" : true, + "earlyRequests": true, + "groupProperties": { + "default": { + "submit": "Auto" + } + } + } + } + }, + "routing": { + "routes": [ + { + "pattern": ":?query:", + "name": "GenreHierarchyList", + "target": "GenreHierarchyList" + }, + { + "pattern": "GenreHierarchy({key}):?query:", + "name": "GenreHierarchyDetails", + "target": "GenreHierarchyDetails" + } + ], + "targets": { + "GenreHierarchyList": { + "type": "Component", + "id": "GenreHierarchyList", + "name": "sap.fe.templates.ListReport", + "options": { + "settings" : { + "entitySet" : "GenreHierarchy", + "navigation" : { + "GenreHierarchy" : { + "detail" : { + "route" : "GenreHierarchyDetails" + } + } + }, + "controlConfiguration": { + "@com.sap.vocabularies.UI.v1.LineItem": { + "tableSettings": { + "hierarchyQualifier": "GenreHierarchy", + "type": "TreeTable" + } + } + } + } + } + }, + "GenreHierarchyDetails": { + "type": "Component", + "id": "GenreHierarchyDetails", + "name": "sap.fe.templates.ObjectPage", + "options": { + "settings" : { + "entitySet": "GenreHierarchy" + } + } + } + } + }, + "contentDensities": { + "compact": true, + "cozy": true + } + }, + "sap.ui": { + "technology": "UI5", + "fullWidth": false + }, + "sap.fiori": { + "registrationIds": [], + "archeType": "transactional" + } +} diff --git a/app/browse/fiori-service.cds b/app/browse/fiori-service.cds index 953c39dc..436480e6 100644 --- a/app/browse/fiori-service.cds +++ b/app/browse/fiori-service.cds @@ -161,6 +161,19 @@ annotate CatalogService.Reviews with @(UI : { ]} }); +annotate CatalogService.GenreHierarchy with @UI: { + PresentationVariant : { + $Type : 'UI.PresentationVariantType', + RequestAtLeast: [name], + Visualizations: ['@UI.LineItem', ], + }, + LineItem : [{ + $Type: 'UI.DataField', + Value: name, + Label : 'Genre' + }], +}; + annotate CatalogService.Books actions { @( Common.SideEffects : { diff --git a/app/fiori.html b/app/fiori.html index 7fa46503..514a26cd 100644 --- a/app/fiori.html +++ b/app/fiori.html @@ -14,8 +14,8 @@ }; - - + - - +