First push
This commit is contained in:
2
bookshop/db/handlers/sandbox.cds
Normal file
2
bookshop/db/handlers/sandbox.cds
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
using from '..\..\schema';
|
||||||
|
|
||||||
@@ -1,43 +1,49 @@
|
|||||||
using { Currency, managed, sap, extensible } from '@sap/cds/common';
|
using {
|
||||||
|
Currency,
|
||||||
|
managed,
|
||||||
|
sap,
|
||||||
|
extensible
|
||||||
|
} from '@sap/cds/common';
|
||||||
|
|
||||||
namespace sap.capire.bookshop;
|
namespace sap.capire.bookshop;
|
||||||
|
|
||||||
@Extensibility.Any.Enabled : true
|
@Extensibility.Any.Enabled : true
|
||||||
entity Books : managed, extensible {
|
entity Books : managed, extensible {
|
||||||
key ID : Integer;
|
key ID : Integer;
|
||||||
title : localized String(111);
|
title : localized String(111);
|
||||||
descr : localized String(1111);
|
descr : localized String(1111);
|
||||||
author : Association to Authors;
|
author : Association to Authors;
|
||||||
genre : Association to Genres;
|
genre : Association to Genres;
|
||||||
stock : Integer;
|
stock : Integer;
|
||||||
price : Decimal;
|
price : Decimal;
|
||||||
currency : Currency;
|
currency : Currency;
|
||||||
image : LargeBinary @Core.MediaType : 'image/png';
|
image : LargeBinary @Core.MediaType : 'image/png';
|
||||||
authorName: String;
|
authorName : String;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Extensibility : {
|
|
||||||
Fields.Enabled : true,
|
|
||||||
Relations.Enabled : false,
|
|
||||||
Annotations.Enabled : true,
|
|
||||||
Logic.Enabled : true,
|
|
||||||
Logic.constraints: true,
|
|
||||||
Logic.calculations: true,
|
|
||||||
Logic.Handler : [create, update, delete, read]
|
|
||||||
}
|
|
||||||
entity Authors : managed, extensible {
|
entity Authors : managed, extensible {
|
||||||
key ID : Integer;
|
key ID : Integer;
|
||||||
name : String(111);
|
name : String(111);
|
||||||
dateOfBirth : Date;
|
dateOfBirth : Date;
|
||||||
dateOfDeath : Date;
|
dateOfDeath : Date;
|
||||||
placeOfBirth : String;
|
placeOfBirth : String;
|
||||||
placeOfDeath : String;
|
placeOfDeath : String;
|
||||||
virtual age: Integer;
|
|
||||||
books : Association to many Books on books.author = $self;
|
books : Association to many Books
|
||||||
|
on books.author = $self;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Hierarchically organized Code List for Genres */
|
extend Authors with {
|
||||||
entity Genres : sap.common.CodeList {
|
virtual age : Integer;
|
||||||
key ID : Integer;
|
}
|
||||||
parent : Association to Genres;
|
|
||||||
children : Composition of many Genres on children.parent = $self;
|
/**
|
||||||
|
* 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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,16 @@
|
|||||||
|
|
||||||
async function run() {
|
async function run() {
|
||||||
//debugger
|
//debugger
|
||||||
//while (true) {}
|
//while (true) {}
|
||||||
//process.exit()
|
//process.exit()
|
||||||
//1.substring()
|
//1.substring()
|
||||||
let res = await cds.read(SELECT.one`title`.from(`Books`).where(`ID=201`))
|
// let res = await specialselect
|
||||||
|
let res = await SELECT.one`title`.from(`Books`).where(`ID=201`)
|
||||||
let { title } = res
|
let { title } = res
|
||||||
const data = req.data
|
let Author = req.data
|
||||||
data.modifiedBy = "Custom Event handler read changed this!"
|
Author.modifiedBy = "Custom Event handler changed this!"
|
||||||
data.placeOfDeath = " --- Somewhere over " + title + " --- create in Sandbox"
|
Author.placeOfDeath = " --- Somewhere over " + title + " --- create in Sandbox"
|
||||||
return data
|
//await this.emit("createdAuthor", { Author })
|
||||||
|
return Author
|
||||||
}
|
}
|
||||||
output = run()
|
run()
|
||||||
|
|||||||
@@ -1,5 +1,28 @@
|
|||||||
|
function getYear(v) {
|
||||||
|
return parseInt(v.substr(0, 4))
|
||||||
|
}
|
||||||
|
function getMonth(v) {
|
||||||
|
return parseInt(v.substr(5, 2))
|
||||||
|
}
|
||||||
|
function getDay(v) {
|
||||||
|
return parseInt(v.substr(8, 2))
|
||||||
|
}
|
||||||
|
|
||||||
|
function getAge(from, to) {
|
||||||
|
if (from === undefined || from == null) return 0
|
||||||
|
if (to === undefined || to == null) to = new Date().toISOString()
|
||||||
|
let year = getYear(to) - getYear(from) - 1
|
||||||
|
if (
|
||||||
|
getMonth(to) > getMonth(from) ||
|
||||||
|
(getMonth(to) === getMonth(from) && getDay(to) >= getDay(from))
|
||||||
|
) {
|
||||||
|
year++
|
||||||
|
}
|
||||||
|
return year
|
||||||
|
}
|
||||||
|
|
||||||
const result_ = Array.isArray(result) ? result : [result]
|
const result_ = Array.isArray(result) ? result : [result]
|
||||||
for (const row of result_) {
|
for (const row of result_) {
|
||||||
row.modifiedBy += " --- read in sandbox"
|
row.modifiedBy += " --- read in sandbox"
|
||||||
row.age = 27
|
row.age = getAge(row.dateOfBirth, row.dateOfDeath)
|
||||||
}
|
}
|
||||||
|
|||||||
9
bookshop/handlers/AdminService.renameAuthor.ON.js
Normal file
9
bookshop/handlers/AdminService.renameAuthor.ON.js
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
async function run() {
|
||||||
|
const {author, newName} = req.data
|
||||||
|
let a = await SELECT `name`.from(`Authors`).where({ID: author})
|
||||||
|
if(!a) return req.error (404, `Can't rename a non-existing author`)
|
||||||
|
await UPDATE (`Authors`,author).with ({ name: newName })
|
||||||
|
//await this.emit ('renamedAuthor', { author, newName })
|
||||||
|
output.msg = 'Success'
|
||||||
|
}
|
||||||
|
run()
|
||||||
5
bookshop/handlers/createdAuthor.ON.js
Normal file
5
bookshop/handlers/createdAuthor.ON.js
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
async function run() {
|
||||||
|
let {Author} = req.data
|
||||||
|
Author.placeOfBirth += ' --- modified in custom event'
|
||||||
|
}
|
||||||
|
run()
|
||||||
76
bookshop/notebook.md
Normal file
76
bookshop/notebook.md
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
```swift
|
||||||
|
using { Currency, managed, sap } from '@sap/cds/common';
|
||||||
|
namespace sap.capire.bookshop;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Annotations available:
|
||||||
|
|
||||||
|
Entity level
|
||||||
|
@expression.constraint : [{if: 'expression evaluates to bool'}, on: ['INSERT, UPDATE, DELETE'], error: 'Transaction Rollback and error message', warning: 'Transaction proceeds and warning message']
|
||||||
|
@expression.computed : [{expression: 'ability to access request payload and modify it', on: ['INSERT, UPDATE']}]
|
||||||
|
@event : [{if: 'expression evaluates to bool', on: ['INSERT, UPDATE, DELETE, READ'], when: 'before or after, default before', emit: 'Event Name', to: 'Messaging target, optional'}]
|
||||||
|
@expresion.code :[{file: 'file name', on:['insert', 'update'], when: 'before or after, default before'},
|
||||||
|
{source: 'each => { if (each.stock > 111) {each.title += ` -- 11% discount!`; each.price= each.price*0.9}', on:['insert', 'update'], when: 'before or after'}]
|
||||||
|
Atribute Level
|
||||||
|
@assert.constraint : {if: 'stock>=0 OR stock <1000', error: 'i18n/error102'};
|
||||||
|
@event : {if: 'expression evaluates to bool', on: ['INSERT, UPDATE, DELETE, READ'], when: 'before or after', emit: 'Event Name', to: 'Messaging target, optional' }
|
||||||
|
|
||||||
|
Functions available:
|
||||||
|
EXISTS(association target)
|
||||||
|
COUNT,AVG,MIN,MAX,SUM: Composition items, arrays etc
|
||||||
|
OLD: before image
|
||||||
|
EACH: loop over composition items
|
||||||
|
|
||||||
|
Events covered:
|
||||||
|
CRUD --> Longhand and Shorthand supported?
|
||||||
|
Upsert as one event?
|
||||||
|
Before and after:
|
||||||
|
Before can change change request payload and stop transaction
|
||||||
|
After should trigger only asynchronous messages
|
||||||
|
Specific Events for status changes? I think expression based event emitter suffices
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//Entity level annotations
|
||||||
|
@expression.constraint : [{if: 'stock>100 AND price>15)', on: ['INSERT', 'UPDATE'], error: 'No Book over price 15 should have more than 100 stock' }, // error, rollback transactions
|
||||||
|
{if: 'stock>90 AND price>15)', on: ['I', 'U'], warning: 'No Book over price 15 should have more than 100 stock' }] //warning, proceed with transaction but report warning back to UI
|
||||||
|
@expression.computed : {expression: 'if(stock>100) then price=price*0.9', on: ['INSERT']} //ability to modify the payload of the request, but nothing beyond it
|
||||||
|
@expresion.code :[{file: 'sap.capire.bookshop-Books-beforeInsert', on:['insert', 'update'], when: 'before'}, //naming can be arbitrary?
|
||||||
|
{source: 'each => { if (each.stock > 111) {each.title += ` -- 11% discount!`; each.price= each.price*0.9}', on:['insert', 'update'], when: 'before'}] //alternative
|
||||||
|
@event : { if:'price>200', emit: 'Expensive Book', to: 'RulesEngine'}
|
||||||
|
entity Books : managed {
|
||||||
|
key ID : Integer;
|
||||||
|
title : localized String(111); @event : {if: 'old.title="Hello"', emit: 'Hello changed' } //old refers to before Image. No "to" clause means message is emitted to any subscriber interested
|
||||||
|
descr : localized String(1111);
|
||||||
|
author : Association to Authors @assert.constraint: 'exists(author)'; //function calls need to evaluate to bool
|
||||||
|
genre : Association to Genres;
|
||||||
|
stock : Integer @assert.constraint : {if: 'stock>=0 OR stock <1000', error: 'Stock not within permitted parameters'}; //when operand is used, no auto-insert
|
||||||
|
price : Decimal(9,2) @assert.constraint : '>0'; //insert operand on left side by default
|
||||||
|
currency : Currency;
|
||||||
|
image : LargeBinary @Core.MediaType : 'image/png';
|
||||||
|
stockWorth: Decimal(9,2) @expression.computed : 'stock*price'; //persisted on write. Overhead in runtime, but performance benefit on read. Payload ignored?
|
||||||
|
// stockWorth2 = stock*price; -- long term goal from compiler team, not persisted on write, but calculated on read
|
||||||
|
stockWorth3 : Decimal @expression.computed: 'if (stock*price>1000) then stockWorth3=stock.price else stockworth3=1000'; //which altenative?
|
||||||
|
stockWorth4 : Decimal @expression.computed: {if: '(stock*price>1000)', then: 'stockWorth3=stock.price', else: 'stockworth3=1000'};
|
||||||
|
}
|
||||||
|
//@assert.expression: 'dateOfBirth<dateOfDeath'
|
||||||
|
entity Authors : managed {
|
||||||
|
key ID : Integer;
|
||||||
|
name : String(111);
|
||||||
|
dateOfBirth : Date ;
|
||||||
|
dateOfDeath : Date @expression.constraint: '>dateOfBirth';
|
||||||
|
placeOfBirth : String;
|
||||||
|
placeOfDeath : String;
|
||||||
|
books : Association to many Books on books.author = $self;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
Binary file not shown.
@@ -2,10 +2,27 @@ using {sap.capire.bookshop as my} from '../db/schema';
|
|||||||
|
|
||||||
service AdminService // @(requires : 'admin')
|
service AdminService // @(requires : 'admin')
|
||||||
{
|
{
|
||||||
entity Books as projection on my.Books;
|
entity Books as projection on my.Books actions {
|
||||||
entity Authors as projection on my.Authors;
|
action increaseStock(count : Integer);
|
||||||
action renameAuthor(author : Authors:ID, newName : String);
|
function stock() returns Integer;
|
||||||
|
};
|
||||||
|
|
||||||
|
@Extensibility : {
|
||||||
|
Fields.Enabled : true,
|
||||||
|
Fields.Quota: 100,
|
||||||
|
Relations.Enabled : false,
|
||||||
|
Annotations.Enabled : true,
|
||||||
|
Logic.Enabled : true,
|
||||||
|
Logic.constraints: true,
|
||||||
|
Logic.calculations: true,
|
||||||
|
Logic.Handler : [create, update, delete, read]
|
||||||
|
}
|
||||||
|
entity Authors as projection on my.Authors;
|
||||||
|
|
||||||
|
action renameAuthor(author : Authors:ID, newName : String) returns {
|
||||||
|
msg : String
|
||||||
|
};
|
||||||
|
function getStock(book: Books:ID) returns Integer;
|
||||||
event newBook : {
|
event newBook : {
|
||||||
book : Books:ID;
|
book : Books:ID;
|
||||||
name : Books:title
|
name : Books:title
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ annotate AdminService.Authors with @(UI : {
|
|||||||
|
|
||||||
// Workaround to avoid errors for unknown db-specific calculated fields above
|
// Workaround to avoid errors for unknown db-specific calculated fields above
|
||||||
extend sap.capire.bookshop.Authors with {
|
extend sap.capire.bookshop.Authors with {
|
||||||
virtual age : Integer;
|
//virtual age : Integer;
|
||||||
virtual lifetime : String;
|
virtual lifetime : String;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -50,6 +50,10 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sap.ui5": {
|
"sap.ui5": {
|
||||||
|
"flexEnabled": true,
|
||||||
|
"config": {
|
||||||
|
"experimentalCAPScenario": true
|
||||||
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"minUI5Version": "1.81.0",
|
"minUI5Version": "1.81.0",
|
||||||
"libs": {
|
"libs": {
|
||||||
|
|||||||
@@ -22,6 +22,10 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sap.ui5": {
|
"sap.ui5": {
|
||||||
|
"flexEnabled": true,
|
||||||
|
"config": {
|
||||||
|
"experimentalCAPScenario": true
|
||||||
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"libs": {
|
"libs": {
|
||||||
"sap.fe.templates": {}
|
"sap.fe.templates": {}
|
||||||
|
|||||||
@@ -53,6 +53,10 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sap.ui5": {
|
"sap.ui5": {
|
||||||
|
"flexEnabled": true,
|
||||||
|
"config": {
|
||||||
|
"experimentalCAPScenario": true
|
||||||
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"minUI5Version": "1.81.0",
|
"minUI5Version": "1.81.0",
|
||||||
"libs": {
|
"libs": {
|
||||||
|
|||||||
@@ -10,7 +10,21 @@
|
|||||||
<script>
|
<script>
|
||||||
window["sap-ushell-config"] = {
|
window["sap-ushell-config"] = {
|
||||||
defaultRenderer: "fiori2",
|
defaultRenderer: "fiori2",
|
||||||
applications: {}
|
applications: {},
|
||||||
|
bootstrapPlugins: {
|
||||||
|
RuntimeAuthoringPlugin: {
|
||||||
|
component: "sap.ushell.plugins.rta",
|
||||||
|
config: {
|
||||||
|
validateAppVersion: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
PersonalizePlugin: {
|
||||||
|
component: "sap.ushell.plugins.rta-personalize",
|
||||||
|
config: {
|
||||||
|
validateAppVersion: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -22,7 +36,11 @@
|
|||||||
data-sap-ui-frameOptions="allow"
|
data-sap-ui-frameOptions="allow"
|
||||||
></script>
|
></script>
|
||||||
<script>
|
<script>
|
||||||
sap.ui.getCore().attachInit(()=> sap.ushell.Container.createRenderer().placeAt("content"))
|
sap.ui.getCore().attachInit(()=> sap.ushell.Container.createRenderer().placeAt("content"));
|
||||||
|
sap.ui
|
||||||
|
.getCore()
|
||||||
|
.getConfiguration()
|
||||||
|
.setFlexibilityServices([{ connector: "SessionStorageConnector" }]);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
|
|||||||
@@ -36,7 +36,10 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"db": {
|
"db": {
|
||||||
"kind": "sql"
|
"kind": "sqlite",
|
||||||
|
"credentials": {
|
||||||
|
"database": "sqlite.db"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"db-ext": {
|
"db-ext": {
|
||||||
"[development]": {
|
"[development]": {
|
||||||
|
|||||||
10582
package-lock.json
generated
10582
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user