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'
+];