Merge branch 'main' into hierarch-draft-crud
This commit is contained in:
@@ -14,25 +14,24 @@ GET http://localhost:4004/odata/v4/test/Genres?
|
|||||||
POST http://localhost:4004/odata/v4/test/Genres?
|
POST http://localhost:4004/odata/v4/test/Genres?
|
||||||
Content-Type: application/json
|
Content-Type: application/json
|
||||||
|
|
||||||
{ "ID":100, "name":"Some Sample Genres...", "children":[
|
{ "ID":"100aaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", "name":"Some Sample Genres...", "children":[
|
||||||
{ "ID":101, "name":"Cat", "children":[
|
{ "ID":"101aaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", "name":"Cat", "children":[
|
||||||
{ "ID":102, "name":"Kitty", "children":[
|
{ "ID":"102aaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", "name":"Kitty", "children":[
|
||||||
{ "ID":103, "name":"Kitty Cat", "children":[
|
{ "ID":"103aaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", "name":"Aristocat" },
|
||||||
{ "ID":104, "name":"Aristocat" } ]},
|
{ "ID":"104aaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", "name":"Kitty Bat" } ]},
|
||||||
{ "ID":105, "name":"Kitty Bat" } ]},
|
{ "ID":"105aaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", "name":"Catwoman", "children":[
|
||||||
{ "ID":106, "name":"Catwoman", "children":[
|
{ "ID":"106aaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", "name":"Catalina" } ]} ]},
|
||||||
{ "ID":107, "name":"Catalina" } ]} ]},
|
{ "ID":"107aaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", "name":"Catweazle" }
|
||||||
{ "ID":108, "name":"Catweazle" }
|
|
||||||
]}
|
]}
|
||||||
###
|
###
|
||||||
|
|
||||||
GET http://localhost:4004/odata/v4/test/Genres(100)?
|
GET http://localhost:4004/odata/v4/test/Genres(100aaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa)?
|
||||||
# &$expand=children
|
&$expand=children
|
||||||
# &$expand=children($expand=children($expand=children($expand=children)))
|
&$expand=children($expand=children($expand=children($expand=children)))
|
||||||
###
|
###
|
||||||
|
|
||||||
DELETE http://localhost:4004/odata/v4/test/Genres(103)
|
DELETE http://localhost:4004/odata/v4/test/Genres(103aaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa)
|
||||||
###
|
###
|
||||||
|
|
||||||
DELETE http://localhost:4004/odata/v4/test/Genres(100)
|
DELETE http://localhost:4004/odata/v4/test/Genres(100aaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa)
|
||||||
###
|
###
|
||||||
|
|||||||
@@ -40,8 +40,7 @@ Authorization: Basic alice:
|
|||||||
|
|
||||||
{
|
{
|
||||||
"ID": 112,
|
"ID": 112,
|
||||||
"name": "Shakespeeeeere",
|
"name": "Shakespeeeeere"
|
||||||
"age": 22
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -56,7 +55,7 @@ Authorization: Basic alice:
|
|||||||
"title": "Poems : Pocket Poets",
|
"title": "Poems : Pocket Poets",
|
||||||
"descr": "The Everyman's Library Pocket Poets hardcover series is popular for its compact size and reasonable price which does not compromise content. Poems: Bronte contains poems that demonstrate a sensibility elemental in its force with an imaginative discipline and flexibility of the highest order. Also included are an Editor's Note and an index of first lines.",
|
"descr": "The Everyman's Library Pocket Poets hardcover series is popular for its compact size and reasonable price which does not compromise content. Poems: Bronte contains poems that demonstrate a sensibility elemental in its force with an imaginative discipline and flexibility of the highest order. Also included are an Editor's Note and an index of first lines.",
|
||||||
"author": { "ID": 101 },
|
"author": { "ID": 101 },
|
||||||
"genre": { "ID": 12 },
|
"genre": { "ID": "12aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa" },
|
||||||
"stock": 5,
|
"stock": 5,
|
||||||
"price": "12.05",
|
"price": "12.05",
|
||||||
"currency": { "code": "USD" }
|
"currency": { "code": "USD" }
|
||||||
|
|||||||
13
common/currencies.cds
Normal file
13
common/currencies.cds
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
using { sap } from '@sap/cds/common';
|
||||||
|
|
||||||
|
extend sap.common.Currencies with {
|
||||||
|
// Currencies.code = ISO 4217 alphabetic three-letter code
|
||||||
|
// with the first two letters being equal to ISO 3166 alphabetic country codes
|
||||||
|
// See also:
|
||||||
|
// [1] https://www.iso.org/iso-4217-currency-codes.html
|
||||||
|
// [2] https://www.currency-iso.org/en/home/tables/table-a1.html
|
||||||
|
// [3] https://www.ibm.com/support/knowledgecenter/en/SSZLC2_7.0.0/com.ibm.commerce.payments.developer.doc/refs/rpylerl2mst97.htm
|
||||||
|
numcode : Integer;
|
||||||
|
exponent : Integer; //> e.g. 2 --> 1 Dollar = 10^2 Cent
|
||||||
|
minor : String; //> e.g. 'Cent'
|
||||||
|
}
|
||||||
@@ -1,45 +1,2 @@
|
|||||||
using { sap } from '@sap/cds/common';
|
using from './currencies';
|
||||||
|
using from './regions';
|
||||||
extend sap.common.Currencies with {
|
|
||||||
// Currencies.code = ISO 4217 alphabetic three-letter code
|
|
||||||
// with the first two letters being equal to ISO 3166 alphabetic country codes
|
|
||||||
// See also:
|
|
||||||
// [1] https://www.iso.org/iso-4217-currency-codes.html
|
|
||||||
// [2] https://www.currency-iso.org/en/home/tables/table-a1.html
|
|
||||||
// [3] https://www.ibm.com/support/knowledgecenter/en/SSZLC2_7.0.0/com.ibm.commerce.payments.developer.doc/refs/rpylerl2mst97.htm
|
|
||||||
numcode : Integer;
|
|
||||||
exponent : Integer; //> e.g. 2 --> 1 Dollar = 10^2 Cent
|
|
||||||
minor : String; //> e.g. 'Cent'
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The Code Lists below are designed as optional extensions to
|
|
||||||
* the base schema. Switch them on by adding an Association to
|
|
||||||
* one of the code list entities in your models or by:
|
|
||||||
* annotate sap.common.Countries with @cds.persistence.skip:false;
|
|
||||||
*/
|
|
||||||
|
|
||||||
context sap.common.countries {
|
|
||||||
|
|
||||||
extend sap.common.Countries {
|
|
||||||
regions : Composition of many Regions on regions._parent = $self.code;
|
|
||||||
}
|
|
||||||
|
|
||||||
entity Regions : sap.common.CodeList {
|
|
||||||
key code : String(5); // ISO 3166-2 alpha5 codes, e.g. DE-BW
|
|
||||||
children : Composition of many Regions on children._parent = $self.code;
|
|
||||||
cities : Composition of many Cities on cities.region = $self;
|
|
||||||
_parent : String(11);
|
|
||||||
}
|
|
||||||
entity Cities : sap.common.CodeList {
|
|
||||||
key code : String(11);
|
|
||||||
region : Association to Regions;
|
|
||||||
districts : Composition of many Districts on districts.city = $self;
|
|
||||||
}
|
|
||||||
entity Districts : sap.common.CodeList {
|
|
||||||
key code : String(11);
|
|
||||||
city : Association to Cities;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|||||||
22
common/regions.cds
Normal file
22
common/regions.cds
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
using { sap.common } from '@sap/cds/common';
|
||||||
|
namespace sap.common.countries;
|
||||||
|
|
||||||
|
extend common.Countries {
|
||||||
|
regions : Composition of many Regions on regions._parent = $self.code;
|
||||||
|
}
|
||||||
|
|
||||||
|
entity Regions : common.CodeList {
|
||||||
|
key code : String(5); // ISO 3166-2 alpha5 codes, e.g. DE-BW
|
||||||
|
children : Composition of many Regions on children._parent = $self.code;
|
||||||
|
cities : Composition of many Cities on cities.region = $self;
|
||||||
|
_parent : String(11);
|
||||||
|
}
|
||||||
|
entity Cities : common.CodeList {
|
||||||
|
key code : String(11);
|
||||||
|
region : Association to Regions;
|
||||||
|
districts : Composition of many Districts on districts.city = $self;
|
||||||
|
}
|
||||||
|
entity Districts : common.CodeList {
|
||||||
|
key code : String(11);
|
||||||
|
city : Association to Cities;
|
||||||
|
}
|
||||||
@@ -66,24 +66,10 @@ annotate AdminService.Books with {
|
|||||||
ValueListProperty: 'ID',
|
ValueListProperty: 'ID',
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
PresentationVariantQualifier: 'VH',
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
annotate AdminService.Genres with @UI: {
|
|
||||||
PresentationVariant #VH: {
|
|
||||||
$Type : 'UI.PresentationVariantType',
|
|
||||||
Visualizations : ['@UI.LineItem'],
|
|
||||||
RecursiveHierarchyQualifier: 'GenreHierarchy'
|
|
||||||
},
|
|
||||||
LineItem : [{
|
|
||||||
$Type: 'UI.DataField',
|
|
||||||
Value: name,
|
|
||||||
Label :'{i18n>Name}'
|
|
||||||
}],
|
|
||||||
};
|
|
||||||
|
|
||||||
// Hide ID because of the ValueHelp
|
// Hide ID because of the ValueHelp
|
||||||
annotate AdminService.Genres with {
|
annotate AdminService.Genres with {
|
||||||
ID @UI.Hidden;
|
ID @UI.Hidden;
|
||||||
@@ -129,4 +115,3 @@ extend service AdminService {
|
|||||||
|
|
||||||
// Workaround for Fiori popup for asking user to enter a new UUID on Create
|
// Workaround for Fiori popup for asking user to enter a new UUID on Create
|
||||||
annotate AdminService.Books with { ID @Core.Computed; }
|
annotate AdminService.Books with { ID @Core.Computed; }
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
|
|
||||||
using { sap.capire.bookshop as my } from '@capire/bookstore';
|
using { sap.capire.bookshop as my } from '@capire/bookstore';
|
||||||
using { sap.common } from '@capire/common';
|
using { sap.common } from '@capire/common';
|
||||||
using { sap.common.Currencies } from '@sap/cds/common';
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
@@ -38,7 +37,7 @@ annotate my.Books with @(
|
|||||||
author @ValueList.entity : 'Authors';
|
author @ValueList.entity : 'Authors';
|
||||||
};
|
};
|
||||||
|
|
||||||
annotate Currencies with {
|
annotate common.Currencies with {
|
||||||
symbol @Common.Label : '{i18n>Currency}';
|
symbol @Common.Label : '{i18n>Currency}';
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -69,129 +68,6 @@ annotate my.Books with {
|
|||||||
image @title: '{i18n>Image}';
|
image @title: '{i18n>Image}';
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// Computed Fields for Tree Tables
|
|
||||||
//
|
|
||||||
// DISCLAIMER: The below are an alpha version implementation and will change in final release !!!
|
|
||||||
//
|
|
||||||
aspect Hierarchy {
|
|
||||||
LimitedDescendantCount : Integer64 = null;
|
|
||||||
DistanceFromRoot : Integer64 = null;
|
|
||||||
DrillState : String = null;
|
|
||||||
Matched : Boolean = null;
|
|
||||||
MatchedDescendantCount : Integer64 = null;
|
|
||||||
LimitedRank : Integer64 = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
annotate Hierarchy with @Capabilities.FilterRestrictions.NonFilterableProperties: [
|
|
||||||
'LimitedDescendantCount',
|
|
||||||
'DistanceFromRoot',
|
|
||||||
'DrillState',
|
|
||||||
'Matched',
|
|
||||||
'MatchedDescendantCount',
|
|
||||||
'LimitedRank'
|
|
||||||
];
|
|
||||||
|
|
||||||
annotate Hierarchy with @Capabilities.SortRestrictions.NonSortableProperties: [
|
|
||||||
'LimitedDescendantCount',
|
|
||||||
'DistanceFromRoot',
|
|
||||||
'DrillState',
|
|
||||||
'Matched',
|
|
||||||
'MatchedDescendantCount',
|
|
||||||
'LimitedRank'
|
|
||||||
];
|
|
||||||
|
|
||||||
extend my.Genres with Hierarchy;
|
|
||||||
extend my.Contents with Hierarchy;
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// Genres Tree Table Annotations
|
|
||||||
//
|
|
||||||
// DISCLAIMER: The below are an alpha version implementation and will change in final release !!!
|
|
||||||
//
|
|
||||||
annotate my.Genres with @Aggregation.RecursiveHierarchy #GenreHierarchy: {
|
|
||||||
$Type : 'Aggregation.RecursiveHierarchyType',
|
|
||||||
NodeProperty : ID, // identifies a node
|
|
||||||
ParentNavigationProperty: parent // navigates to a node's parent
|
|
||||||
};
|
|
||||||
|
|
||||||
annotate my.Genres with @Hierarchy.RecursiveHierarchy #GenreHierarchy: {
|
|
||||||
$Type : 'Hierarchy.RecursiveHierarchyType',
|
|
||||||
LimitedDescendantCount: LimitedDescendantCount,
|
|
||||||
DistanceFromRoot : DistanceFromRoot,
|
|
||||||
DrillState : DrillState,
|
|
||||||
Matched : Matched,
|
|
||||||
MatchedDescendantCount: MatchedDescendantCount,
|
|
||||||
LimitedRank : LimitedRank
|
|
||||||
};
|
|
||||||
|
|
||||||
annotate my.Genres with @(
|
|
||||||
cds.search: {name}
|
|
||||||
);
|
|
||||||
////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// Genres List
|
|
||||||
//
|
|
||||||
annotate my.Genres with @(
|
|
||||||
Common.SemanticKey : [name],
|
|
||||||
UI : {
|
|
||||||
SelectionFields : [name],
|
|
||||||
LineItem : [
|
|
||||||
{ Value : name, Label : '{i18n>Name}' },
|
|
||||||
],
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// Genre Details
|
|
||||||
//
|
|
||||||
annotate my.Genres with @(UI : {
|
|
||||||
Identification : [{ Value: name}],
|
|
||||||
HeaderInfo : {
|
|
||||||
TypeName : '{i18n>Genre}',
|
|
||||||
TypeNamePlural : '{i18n>Genres}',
|
|
||||||
Title : { Value: name },
|
|
||||||
Description : { Value: ID }
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// Genres Elements
|
|
||||||
//
|
|
||||||
annotate my.Genres with {
|
|
||||||
name @title: '{i18n>Genre}';
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// Contents Tree Table Annotations
|
|
||||||
//
|
|
||||||
// DISCLAIMER: The below are an alpha version implementation and will change in final release !!!
|
|
||||||
//
|
|
||||||
annotate my.Contents with @Aggregation.RecursiveHierarchy #ContentsHierarchy: {
|
|
||||||
$Type: 'Aggregation.RecursiveHierarchyType',
|
|
||||||
NodeProperty: ID, // identifies a node
|
|
||||||
ParentNavigationProperty: parent // navigates to a node's parent
|
|
||||||
};
|
|
||||||
|
|
||||||
annotate my.Contents with @Hierarchy.RecursiveHierarchy #ContentsHierarchy: {
|
|
||||||
$Type: 'Hierarchy.RecursiveHierarchyType',
|
|
||||||
LimitedDescendantCount: LimitedDescendantCount,
|
|
||||||
DistanceFromRoot: DistanceFromRoot,
|
|
||||||
DrillState: DrillState,
|
|
||||||
Matched: Matched,
|
|
||||||
MatchedDescendantCount: MatchedDescendantCount,
|
|
||||||
LimitedRank: LimitedRank
|
|
||||||
};
|
|
||||||
|
|
||||||
annotate my.Contents with @(
|
|
||||||
cds.search: {name}
|
|
||||||
);
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Contents List
|
// Contents List
|
||||||
|
|||||||
@@ -1,3 +1,33 @@
|
|||||||
/*
|
using { sap.capire.bookshop.Genres } from '@capire/bookstore';
|
||||||
All annotations needed for UI5 Tree Table View are located in '../common'
|
|
||||||
*/
|
annotate Genres with @cds.search: {name};
|
||||||
|
annotate Genres with @readonly;
|
||||||
|
annotate Genres with {
|
||||||
|
name @title: '{i18n>Genre}';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lists
|
||||||
|
annotate Genres with @(
|
||||||
|
Common.SemanticKey : [name],
|
||||||
|
UI.SelectionFields : [name],
|
||||||
|
UI.LineItem : [
|
||||||
|
{ Value: name, Label: '{i18n>Name}' },
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
// Details
|
||||||
|
annotate Genres with @(UI : {
|
||||||
|
Identification : [{ Value: name }],
|
||||||
|
HeaderInfo : {
|
||||||
|
TypeName : '{i18n>Genre}',
|
||||||
|
TypeNamePlural : '{i18n>Genres}',
|
||||||
|
Title : { Value: name },
|
||||||
|
Description : { Value: ID }
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// Tree Views
|
||||||
|
// annotate AdminService.Genres with @hierarchy; // upcomming simplification
|
||||||
|
using from './tree-view';
|
||||||
|
using from './value-help';
|
||||||
|
|||||||
42
fiori/app/genres/tree-view.cds
Normal file
42
fiori/app/genres/tree-view.cds
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
using { AdminService } from '@capire/bookstore';
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// Genres Tree View
|
||||||
|
//
|
||||||
|
|
||||||
|
// Tell Fiori about the structure of the hierarchy
|
||||||
|
annotate AdminService.Genres with @Aggregation.RecursiveHierarchy #GenresHierarchy : {
|
||||||
|
ParentNavigationProperty : parent, // navigates to a node's parent
|
||||||
|
NodeProperty : ID, // identifies a node, usually the key
|
||||||
|
};
|
||||||
|
|
||||||
|
// Fiori expects the following to be defined explicitly, even though they're always the same
|
||||||
|
extend AdminService.Genres with @(
|
||||||
|
// The columns expected by Fiori to be present in hierarchy entities
|
||||||
|
Hierarchy.RecursiveHierarchy #GenresHierarchy : {
|
||||||
|
LimitedDescendantCount : LimitedDescendantCount,
|
||||||
|
DistanceFromRoot : DistanceFromRoot,
|
||||||
|
DrillState : DrillState,
|
||||||
|
LimitedRank : LimitedRank
|
||||||
|
},
|
||||||
|
// Disallow filtering on these properties from Fiori UIs
|
||||||
|
Capabilities.FilterRestrictions.NonFilterableProperties: [
|
||||||
|
'LimitedDescendantCount',
|
||||||
|
'DistanceFromRoot',
|
||||||
|
'DrillState',
|
||||||
|
'LimitedRank'
|
||||||
|
],
|
||||||
|
// Disallow sorting on these properties from Fiori UIs
|
||||||
|
Capabilities.SortRestrictions.NonSortableProperties : [
|
||||||
|
'LimitedDescendantCount',
|
||||||
|
'DistanceFromRoot',
|
||||||
|
'DrillState',
|
||||||
|
'LimitedRank'
|
||||||
|
],
|
||||||
|
) columns { // Ensure we can query these fields from database
|
||||||
|
null as LimitedDescendantCount : Int16,
|
||||||
|
null as DistanceFromRoot : Int16,
|
||||||
|
null as DrillState : String,
|
||||||
|
null as LimitedRank : Int16,
|
||||||
|
};
|
||||||
6
fiori/app/genres/value-help.cds
Normal file
6
fiori/app/genres/value-help.cds
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
// Value help with Tree View
|
||||||
|
using from '../admin-books/fiori-service';
|
||||||
|
annotate AdminService.Books:genre with @Common.ValueList.PresentationVariantQualifier: 'VH';
|
||||||
|
annotate AdminService.Genres with @UI.PresentationVariant #VH: {
|
||||||
|
RecursiveHierarchyQualifier : 'GenresHierarchy',
|
||||||
|
};
|
||||||
@@ -51,7 +51,7 @@
|
|||||||
"earlyRequests": true,
|
"earlyRequests": true,
|
||||||
"groupProperties": {
|
"groupProperties": {
|
||||||
"default": {
|
"default": {
|
||||||
"submit": "Auto"
|
"submit": "Auto"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -82,17 +82,17 @@
|
|||||||
"Genres": {
|
"Genres": {
|
||||||
"detail": {
|
"detail": {
|
||||||
"route": "GenresDetails"
|
"route": "GenresDetails"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"controlConfiguration": {
|
"controlConfiguration": {
|
||||||
"@com.sap.vocabularies.UI.v1.LineItem": {
|
"@com.sap.vocabularies.UI.v1.LineItem": {
|
||||||
"tableSettings": {
|
"tableSettings": {
|
||||||
"hierarchyQualifier": "GenreHierarchy",
|
"hierarchyQualifier": "GenresHierarchy",
|
||||||
"type": "TreeTable"
|
"type": "TreeTable"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -121,4 +121,4 @@
|
|||||||
"registrationIds": [],
|
"registrationIds": [],
|
||||||
"archeType": "transactional"
|
"archeType": "transactional"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -5,6 +5,7 @@
|
|||||||
using from './admin-authors/fiori-service';
|
using from './admin-authors/fiori-service';
|
||||||
using from './admin-books/fiori-service';
|
using from './admin-books/fiori-service';
|
||||||
using from './browse/fiori-service';
|
using from './browse/fiori-service';
|
||||||
|
using from './genres/fiori-service';
|
||||||
|
|
||||||
using from './common';
|
using from './common';
|
||||||
using from '@capire/bookstore/srv/mashup';
|
using from '@capire/bookstore/srv/mashup';
|
||||||
|
|||||||
50
fiori/server.js
Normal file
50
fiori/server.js
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
const cds = require('@sap/cds/lib')
|
||||||
|
|
||||||
|
// PoC for simplified Fiori Tree Views
|
||||||
|
cds.on('compile.for.runtime', csn => {
|
||||||
|
for (let each of cds.linked(csn).definitions) {
|
||||||
|
if (each.is_entity && each._service && each['@hierarchy']) _hierarchy (each)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
const _hierarchy = entity => {
|
||||||
|
|
||||||
|
// Add annotations explaining the hierarchy structure to Fiori
|
||||||
|
const Qualifier = entity.name.slice (entity._service.name.length+1) + 'Hierarchy'
|
||||||
|
const parent = _parent4(entity)
|
||||||
|
entity[`@Aggregation.RecursiveHierarchy#${Qualifier}.ParentNavigationProperty`] ??= {'=': parent.name }
|
||||||
|
entity[`@Aggregation.RecursiveHierarchy#${Qualifier}.NodeProperty`] ??= {'=': parent.keys[0].ref[0] }
|
||||||
|
|
||||||
|
// Add expected hierarchy elements to the entity
|
||||||
|
const columns = entity.projection.columns ??= ['*']
|
||||||
|
const elements = entity.elements
|
||||||
|
for (let e of Hierarchy.elements) {
|
||||||
|
entity[`@Hierarchy.RecursiveHierarchy#${Qualifier}.${e.name}`] = {'=': e.name }
|
||||||
|
if (e.name in elements) continue
|
||||||
|
const { name, value, ...rest } = e
|
||||||
|
elements[e.name] = Object.defineProperty ({ __proto__:e, ...rest }, 'parent', { value: entity })
|
||||||
|
columns.push ({ ...value, as: name, cast: { type: e.type } })
|
||||||
|
}
|
||||||
|
|
||||||
|
// Disable filter and sort for hierarchy elements
|
||||||
|
entity['@Capabilities.FilterRestrictions.NonFilterableProperties'] =
|
||||||
|
entity['@Capabilities.SortRestrictions.NonSortableProperties'] =
|
||||||
|
Object.keys (Hierarchy.elements)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const _parent4 = entity => {
|
||||||
|
const parent = entity['@hierarchy.parent'] || entity['@hierarchy.via']
|
||||||
|
if (parent) return entity.elements [parent['=']||parent]
|
||||||
|
else for (let e of entity.elements) // use first recursive uplink association
|
||||||
|
if (e.is2one && e._target === entity) return e
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const { Hierarchy } = cds.linked `aspect Hierarchy {
|
||||||
|
LimitedDescendantCount : Int16 = null;
|
||||||
|
DistanceFromRoot : Int16 = null;
|
||||||
|
DrillState : String = null;
|
||||||
|
LimitedRank : Int16 = null;
|
||||||
|
}`.definitions
|
||||||
Reference in New Issue
Block a user