From dab98a12abc98e738cdf903d773150800a566ce1 Mon Sep 17 00:00:00 2001 From: D070615 Date: Wed, 30 Oct 2024 16:43:03 +0100 Subject: [PATCH] wip: genres hierarchy --- app/appconfig/fioriSandboxConfig.json | 21 +++ app/common.cds | 2 +- app/genres/fiori-service.cds | 22 ++++ app/genres/package.json | 12 ++ app/genres/webapp/Component.js | 3 + app/genres/webapp/i18n/i18n.properties | 2 + app/genres/webapp/i18n/i18n_de.properties | 2 + app/genres/webapp/index.html | 35 +++++ app/genres/webapp/manifest.json | 129 +++++++++++++++++++ app/index.cds | 1 + app/xs-app.json | 149 +++++++++++----------- db/books.cds | 8 +- db/data/my.bookshop-Genres.csv | 32 ++--- db/undeploy.json | 9 ++ srv/admin-service.cds | 19 ++- srv/genres.cds | 26 ++++ 16 files changed, 374 insertions(+), 98 deletions(-) create mode 100644 app/genres/fiori-service.cds create mode 100644 app/genres/package.json create mode 100644 app/genres/webapp/Component.js create mode 100644 app/genres/webapp/i18n/i18n.properties create mode 100644 app/genres/webapp/i18n/i18n_de.properties create mode 100644 app/genres/webapp/index.html create mode 100644 app/genres/webapp/manifest.json create mode 100644 db/undeploy.json create mode 100644 srv/genres.cds diff --git a/app/appconfig/fioriSandboxConfig.json b/app/appconfig/fioriSandboxConfig.json index 1dc45ef8..48b2ef6a 100644 --- a/app/appconfig/fioriSandboxConfig.json +++ b/app/appconfig/fioriSandboxConfig.json @@ -20,6 +20,14 @@ "title": "Browse Books", "description": "Find your favorite book" } + }, { + "id": "browse-genres", + "tileType": "sap.ushell.ui.tile.StaticTile", + "properties": { + "targetURL": "#Genres-display", + "title": "Browse Genres", + "description": "Find your favorite genre" + } } ] }, @@ -112,6 +120,19 @@ "url": "/browse/webapp" } }, + "browse-genres": { + "semanticObject": "Genres", + "action": "display", + "signature": { + "parameters": {}, + "additionalParameters": "allowed" + }, + "resolutionResult": { + "applicationType": "SAPUI5", + "additionalInformation": "SAPUI5.Component=genres", + "url": "/genres/webapp" + } + }, "manage-books": { "semanticObject": "Books", "action": "manage", diff --git a/app/common.cds b/app/common.cds index a8607057..6f8bbe89 100644 --- a/app/common.cds +++ b/app/common.cds @@ -203,7 +203,7 @@ annotate my.Genres with Facets : [{ $Type : 'UI.ReferenceFacet', Label : '{i18n>SubGenres}', - Target : 'children/@UI.LineItem' + Target : 'parent/@UI.LineItem' }, ], }); diff --git a/app/genres/fiori-service.cds b/app/genres/fiori-service.cds new file mode 100644 index 00000000..701631d3 --- /dev/null +++ b/app/genres/fiori-service.cds @@ -0,0 +1,22 @@ +/* + Annotations for the Browse GenreHierarchy App +*/ + +using AdminService from '../../srv/admin-service'; + + +annotate AdminService.GenreHierarchy with @Aggregation.RecursiveHierarchy#AdminHierarchy: { + $Type: 'Aggregation.RecursiveHierarchyType', + NodeProperty: node_id, // identifies a node + ParentNavigationProperty: parent // navigates to a node's parent + }; + + annotate AdminService.GenreHierarchy with @Hierarchy.RecursiveHierarchy#AdminHierarchy: { + $Type: 'Hierarchy.RecursiveHierarchyType', + // ExternalKey : null, + LimitedDescendantCount: LimitedDescendantCount, + DistanceFromRoot: DistanceFromRoot, + DrillState: DrillState, + Matched: Matched, + MatchedDescendantCount: MatchedDescendantCount +}; \ No newline at end of file diff --git a/app/genres/package.json b/app/genres/package.json new file mode 100644 index 00000000..9b81674d --- /dev/null +++ b/app/genres/package.json @@ -0,0 +1,12 @@ +{ + "name": "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/genres/webapp/Component.js b/app/genres/webapp/Component.js new file mode 100644 index 00000000..6b352417 --- /dev/null +++ b/app/genres/webapp/Component.js @@ -0,0 +1,3 @@ +sap.ui.define(["sap/fe/core/AppComponent"], ac => ac.extend("genres.Component", { + metadata:{ manifest:'json' } +})) diff --git a/app/genres/webapp/i18n/i18n.properties b/app/genres/webapp/i18n/i18n.properties new file mode 100644 index 00000000..a13aa132 --- /dev/null +++ b/app/genres/webapp/i18n/i18n.properties @@ -0,0 +1,2 @@ +appTitle=Browse Genres +appDescription=Browse Genres - Sample Application diff --git a/app/genres/webapp/i18n/i18n_de.properties b/app/genres/webapp/i18n/i18n_de.properties new file mode 100644 index 00000000..3c42c005 --- /dev/null +++ b/app/genres/webapp/i18n/i18n_de.properties @@ -0,0 +1,2 @@ +appTitle=Zeige Genre +appDescription=Zeige Genre - Beispielanwendung diff --git a/app/genres/webapp/index.html b/app/genres/webapp/index.html new file mode 100644 index 00000000..e6ea04d8 --- /dev/null +++ b/app/genres/webapp/index.html @@ -0,0 +1,35 @@ + + + + + + + Browse Books + + + + +
+ + diff --git a/app/genres/webapp/manifest.json b/app/genres/webapp/manifest.json new file mode 100644 index 00000000..a8ee2b84 --- /dev/null +++ b/app/genres/webapp/manifest.json @@ -0,0 +1,129 @@ +{ + "_version": "1.8.0", + "sap.app": { + "id": "genres", + "type": "application", + "title": "{{appTitle}}", + "description": "{{appDescription}}", + "applicationVersion": { + "version": "1.0.0" + }, + "dataSources": { + "AdminService": { + "uri": "/api/admin/", + "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": "AdminService", + "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": "AdminHierarchy", + "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/index.cds b/app/index.cds index dcbf40e5..0c07c888 100644 --- a/app/index.cds +++ b/app/index.cds @@ -8,4 +8,5 @@ using from './orders/fiori-service'; using from './reviews/fiori-service'; using from './notes/fiori-service'; using from './addresses/fiori-service'; +using from './genres/fiori-service'; using from './common'; diff --git a/app/xs-app.json b/app/xs-app.json index f6adf5a4..4facc67d 100644 --- a/app/xs-app.json +++ b/app/xs-app.json @@ -2,77 +2,82 @@ "welcomeFile": "/app/fiori.html", "authenticationMethod": "route", "routes": [ - { - "source": "^/app/(.*)$", - "cacheControl": "no-cache, no-store, must-revalidate", - "target": "$1", - "localDir": "./", - "authenticationType": "xsuaa" - }, - { - "source": "^/appconfig/(.*)$", - "localDir": "./", - "authenticationType": "xsuaa" - }, - { - "source": "^/browse/webapp/(.*)$", - "localDir": "./", - "authenticationType": "xsuaa" - }, - { - "source": "^/admin/webapp/(.*)$", - "localDir": "./", - "authenticationType": "xsuaa" - }, - { - "source": "^/orders/webapp/(.*)$", - "localDir": "./", - "authenticationType": "xsuaa" - }, - { - "source": "^/reviews/webapp/(.*)$", - "localDir": "./", - "authenticationType": "xsuaa" - }, - { - "source": "^/notes/webapp/(.*)$", - "localDir": "./", - "authenticationType": "xsuaa" - }, - { - "source": "^/addresses/webapp/(.*)$", - "localDir": "./", - "authenticationType": "xsuaa" - }, - { - "source": "^/vue/(.*)$", - "localDir": "./", - "authenticationType": "xsuaa" - }, - { - "source": "^/api/admin/(.*)", - "authenticationType": "xsuaa", - "destination": "backend" - }, - { - "source": "^/api/browse/(.*)", - "authenticationType": "xsuaa", - "destination": "backend" - }, - { - "source": "^/api/review/(.*)", - "authenticationType": "xsuaa", - "destination": "backend" - }, - { - "source": "^/api/notes/(.*)", - "authenticationType": "xsuaa", - "destination": "backend" - }, - { - "source": "^/api/(.*)$", - "authenticationType": "none", - "destination": "backend" - } + { + "source": "^/app/(.*)$", + "cacheControl": "no-cache, no-store, must-revalidate", + "target": "$1", + "localDir": "./", + "authenticationType": "xsuaa" + }, + { + "source": "^/appconfig/(.*)$", + "localDir": "./", + "authenticationType": "xsuaa" + }, + { + "source": "^/browse/webapp/(.*)$", + "localDir": "./", + "authenticationType": "xsuaa" + }, + { + "source": "^/admin/webapp/(.*)$", + "localDir": "./", + "authenticationType": "xsuaa" + }, + { + "source": "^/orders/webapp/(.*)$", + "localDir": "./", + "authenticationType": "xsuaa" + }, + { + "source": "^/genres/webapp/(.*)$", + "localDir": "./", + "authenticationType": "xsuaa" + }, + { + "source": "^/reviews/webapp/(.*)$", + "localDir": "./", + "authenticationType": "xsuaa" + }, + { + "source": "^/notes/webapp/(.*)$", + "localDir": "./", + "authenticationType": "xsuaa" + }, + { + "source": "^/addresses/webapp/(.*)$", + "localDir": "./", + "authenticationType": "xsuaa" + }, + { + "source": "^/vue/(.*)$", + "localDir": "./", + "authenticationType": "xsuaa" + }, + { + "source": "^/api/admin/(.*)", + "authenticationType": "xsuaa", + "destination": "backend" + }, + { + "source": "^/api/browse/(.*)", + "authenticationType": "xsuaa", + "destination": "backend" + }, + { + "source": "^/api/review/(.*)", + "authenticationType": "xsuaa", + "destination": "backend" + }, + { + "source": "^/api/notes/(.*)", + "authenticationType": "xsuaa", + "destination": "backend" + }, + { + "source": "^/api/(.*)$", + "authenticationType": "none", + "destination": "backend" + } ] } diff --git a/db/books.cds b/db/books.cds index fe2e5e0a..b93ee3ef 100644 --- a/db/books.cds +++ b/db/books.cds @@ -47,8 +47,8 @@ annotate Authors with * Hierarchically organized Code List for Genres */ entity Genres : sap.common.CodeList { - key ID : Integer; - parent : Association to Genres; - children : Composition of many Genres - on children.parent = $self; + key ID : Integer; + node : Integer not null; + parent_node : Integer default 0; + parent : Association to one Genres on parent.node = parent_node; } diff --git a/db/data/my.bookshop-Genres.csv b/db/data/my.bookshop-Genres.csv index 88e73bdd..044b2b47 100644 --- a/db/data/my.bookshop-Genres.csv +++ b/db/data/my.bookshop-Genres.csv @@ -1,16 +1,16 @@ -ID;parent_ID;name -10;;Fiction -11;10;Drama -12;10;Poetry -13;10;Fantasy -14;10;Science Fiction -15;10;Romance -16;10;Mystery -17;10;Thriller -18;10;Dystopia -19;10;Fairy Tale -20;;Non-Fiction -21;20;Biography -22;21;Autobiography -23;20;Essay -24;20;Speech \ No newline at end of file +ID;parent_node;name;node +10;;Fiction;10 +11;10;Drama;11 +12;10;Poetry;12 +13;10;Fantasy;13 +14;10;Science Fiction;14 +15;10;Romance;15 +16;10;Mystery;16 +17;10;Thriller;17 +18;10;Dystopia;18 +19;10;Fairy Tale;19 +20;;Non-Fiction;20 +21;20;Biography;21 +22;21;Autobiography;22 +23;20;Essay;23 +24;20;Speech;24 \ No newline at end of file diff --git a/db/undeploy.json b/db/undeploy.json new file mode 100644 index 00000000..bdea5664 --- /dev/null +++ b/db/undeploy.json @@ -0,0 +1,9 @@ +[ + "src/gen/**/*.hdbview", + "src/gen/**/*.hdbindex", + "src/gen/**/*.hdbconstraint", + "src/gen/**/*_drafts.hdbtable", + "src/gen/**/*.hdbcalculationview", + "src/gen/**/*.hdbtabledata" + ] + \ No newline at end of file diff --git a/srv/admin-service.cds b/srv/admin-service.cds index 0f0e1a9b..a1a4305f 100644 --- a/srv/admin-service.cds +++ b/srv/admin-service.cds @@ -1,17 +1,26 @@ using {sap.common.Languages as CommonLanguages} 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.admin.bookshop.Hierarchy as Hierarchy} from './genres'; extend my.Orders with changelog.changeTracked; @path : 'admin' +@odata.apply.transformations service AdminService @(requires : 'admin') { entity Books as projection on my.Books excluding { reviews } actions { - action addToOrder(order_ID : UUID, quantity : Integer) returns Orders; - } + action addToOrder(order_ID : UUID, quantity : Integer) returns Orders; + } 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 { + node as node_id, + parent_node as parent_id, + * + } excluding { node, parent_node } @cds.persistence.skip entity Upload @odata.singleton { @@ -60,6 +69,6 @@ annotate AdminService.OrderItems { // Assign identifiers to the tracked entities annotate AdminService.Orders with @changelog: [OrderNo]; annotate AdminService.OrderItems with @changelog: [ - parent.OrderNo, - book.title, - ]; \ No newline at end of file + parent.OrderNo, + book.title, +]; \ No newline at end of file diff --git a/srv/genres.cds b/srv/genres.cds new file mode 100644 index 00000000..49bff134 --- /dev/null +++ b/srv/genres.cds @@ -0,0 +1,26 @@ +namespace my.admin.bookshop; + +aspect Hierarchy { + virtual LimitedDescendantCount : Integer64; + virtual DistanceFromRoot : Integer64; + virtual DrillState : String; + virtual Matched : Boolean; + virtual MatchedDescendantCount : Integer64; +} + + +annotate Hierarchy with @Capabilities.FilterRestrictions.NonFilterableProperties: [ + 'LimitedDescendantCount', + 'DistanceFromRoot', + 'DrillState', + 'Matched', + 'MatchedDescendantCount' +]; + +annotate Hierarchy with @Capabilities.SortRestrictions.NonSortableProperties: [ + 'LimitedDescendantCount', + 'DistanceFromRoot', + 'DrillState', + 'Matched', + 'MatchedDescendantCount' +];