Compare commits

..

2 Commits

Author SHA1 Message Date
Daniel
b629f8faf3 Merge branch 'master' into temporal 2021-04-10 13:08:31 +02:00
Daniel
9479aa4cad . 2021-04-08 19:17:48 +02:00
27 changed files with 119 additions and 143 deletions

1
.env Normal file
View File

@@ -0,0 +1 @@
# cdsc

View File

@@ -5,7 +5,7 @@ const app = express()
const { PORT=4444 } = process.env
const [,,port=PORT] = process.argv
const cwd = __dirname
process.chdir(__dirname)
app.use('/-/:tarball', (req,res,next) => {
console.debug ('GET', req.params)
@@ -13,7 +13,7 @@ app.use('/-/:tarball', (req,res,next) => {
const { tarball } = req.params
const [, pkg ] = /^capire-(\w+)/.exec(tarball)
fs.lstat(tarball,(err => {
if (err) exec(`npm pack ../${pkg}`,{cwd},next)
if (err) exec(`npm pack ../${pkg}`,next)
else next()
}))
} catch (e) {

View File

@@ -13,5 +13,5 @@
"**/cds/lib/req/cls.js",
"**/odata-v4/okra/**"
]
}
},
}

View File

@@ -35,7 +35,7 @@ const books = new Vue ({
try {
const res = await POST(`/submitOrder`, { amount, book: book.ID })
book.stock = res.data.stock
books.order = { amount, succeeded: `Successfully ordered ${amount} item(s).` }
books.order = { amount, succeeded: `Successfully orderd ${amount} item(s).` }
} catch (e) {
books.order = { amount, failed: e.response.data.error.message }
}

View File

@@ -3,4 +3,4 @@ ID;title;descr;author_ID;stock;price;currency_code;genre_ID
207;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.";107;11;12.34;GBP;11
251;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.";150;333;13.13;USD;16
252;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.";150;555;14;USD;16
271;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.;170;22;150;JPY;13
271;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.;170;22;15;EUR;13
1 ID title descr author_ID stock price currency_code genre_ID
3 207 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. 107 11 12.34 GBP 11
4 251 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. 150 333 13.13 USD 16
5 252 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. 150 555 14 USD 16
6 271 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. 170 22 150 15 JPY EUR 13

View File

@@ -1,4 +1,4 @@
using { Currency, managed, sap } from '@sap/cds/common';
using { Currency, managed, temporal, sap } from '@sap/cds/common';
namespace sap.capire.bookshop;
entity Books : managed {

View File

@@ -1,5 +1,27 @@
using { sap.capire.bookshop as my } from '../db/schema';
service AdminService @(requires:'admin') {
entity Books as projection on my.Books;
entity Books as SELECT from my.Books { *,
// key ID, key validFrom
};
entity Authors as projection on my.Authors;
// @cds.redirection.target:false
// entity Books.history @(cds.temporal:false) as projection on Books {
// *,
// key ID,
// key validFrom @(cds.valid.from:false),
// validTo @(cds.valid.to:false),
// };
}
// entity NonTemporalBook as projection on my.Books {
// *,
// key ID,
// key validFrom @(cds.valid.from:false),
// validTo @(cds.valid.to:false),
// };
extend my.Books with {
history : Composition of many my.Books on history.ID = $self.ID;
}

View File

@@ -1,15 +1,13 @@
using { sap.capire.bookshop as my } from '../db/schema';
service CatalogService @(path:'/browse') {
/** For displaying lists of Books */
@readonly entity ListOfBooks as projection on Books
excluding { descr };
/** For display in details pages */
@readonly entity Books as projection on my.Books { *,
@readonly entity Books as SELECT from my.Books { *,
author.name as author
} excluding { createdBy, modifiedBy };
@readonly entity ListOfBooks as SELECT from Books
excluding { descr };
@requires: 'authenticated-user'
action submitOrder ( book: Books:ID, amount: Integer ) returns { stock: Integer };
event OrderedBook : { book: Books:ID; amount: Integer; buyer: String };

View File

@@ -5,11 +5,9 @@ CAD;de;Kanadischer Dollar;Kanadischer Dollar
AUD;de;Australischer Dollar;Australischer Dollar
GBP;de;Pfund;Britische Pfund
ILS;de;Schekel;Israelische Schekel
JPY;de;Yen;Japanische Yen
EUR;fr;euro;de la Zone euro
USD;fr;dollar;dollar des États-Unis
CAD;fr;dollar canadien;dollar canadien
AUD;fr;dollar australien;dollar australien
GBP;fr;livre sterling;pound sterling
ILS;fr;Shekel;shekel israelien
JPY;fr;Yen;Yen japonais
ILS;fr;Shekel;shekel israelien
1 code locale name descr
5 AUD de Australischer Dollar Australischer Dollar
6 GBP de Pfund Britische Pfund
7 ILS de Schekel Israelische Schekel
JPY de Yen Japanische Yen
8 EUR fr euro de la Zone euro
9 USD fr dollar dollar des États-Unis
10 CAD fr dollar canadien dollar canadien
11 AUD fr dollar australien dollar australien
12 GBP fr livre sterling pound sterling
13 ILS fr Shekel shekel israelien
JPY fr Yen Yen japonais

View File

@@ -70,7 +70,7 @@ annotate AdminService.Authors with @(
annotate sap.capire.bookshop.Books with @fiori.draft.enabled;
annotate AdminService.Books with @odata.draft.enabled;
annotate AdminService.Books.texts with @(
annotate AdminService.Books_texts with @(
UI: {
Identification: [{Value:title}],
SelectionFields: [ locale, title ],
@@ -83,7 +83,7 @@ annotate AdminService.Books.texts with @(
);
// Add Value Help for Locales
annotate AdminService.Books.texts {
annotate AdminService.Books_texts {
locale @ValueList:{entity:'Languages',type:#fixed}
}
// In addition we need to expose Languages through AdminService

View File

@@ -54,7 +54,7 @@ annotate my.Books with {
title @title:'{i18n>Title}';
genre @title:'{i18n>Genre}' @Common: { Text: genre.name, TextArrangement: #TextOnly };
author @title:'{i18n>Author}' @Common: { Text: author.name, TextArrangement: #TextOnly };
price @title:'{i18n>Price}' @Measures.ISOCurrency: currency_code;
price @title:'{i18n>Price}';
stock @title:'{i18n>Stock}';
descr @UI.MultiLineText;
}

View File

@@ -6,7 +6,7 @@
"@capire/reviews": "*",
"@capire/orders": "*",
"@capire/common": "*",
"@sap/cds": "^5",
"@sap/cds": ">=4",
"express": "^4.17.1",
"passport": "^0.4.1"
},

View File

@@ -19,7 +19,7 @@ extend Books with {
// Extend Orders with Books as Products
//
using { sap.capire.orders.Orders } from '@capire/orders';
extend Orders.Items with {
using { sap.capire.orders.Orders_Items } from '@capire/orders';
extend Orders_Items with {
book : Association to Books on product.ID = book.ID
}

View File

@@ -38,11 +38,7 @@ GET {{bookshop}}/browse/Books(201)?
&$select=ID,title,rating
&$expand=reviews
###
GET {{bookshop}}/browse/Books?
&$select=title,author&$expand=currency
Accept-Language: de
#################################################
#

View File

@@ -1,3 +1,2 @@
cds.requires.messaging.kind = file-based-messaging
PORT = 4006
# cds.odata.flavor = x4
PORT = 4006

View File

@@ -11,7 +11,6 @@
using { OrdersService } from '../../srv/orders-service';
using { sap.common } from '@capire/common';
@odata.draft.enabled
@@ -69,7 +68,7 @@ annotate OrdersService.Orders with @(
annotate OrdersService.Orders.Items with @(
annotate OrdersService.Orders_Items with @(
UI: {
LineItem: [
{Value: product_ID, Label:'Product ID'},
@@ -78,22 +77,13 @@ annotate OrdersService.Orders.Items with @(
{Value: amount, Label:'Quantity'},
],
Identification: [ //Is the main field group
{Value: product_ID, Label:'Product ID'},
{Value: title, Label:'Product Title'},
{Value: amount, Label:'Amount'},
{Value: title, Label:'Product'},
{Value: price, Label:'Unit Price'},
],
Facets: [
{$Type: 'UI.ReferenceFacet', Label: '{i18n>OrderItems}', Target: '@UI.Identification'},
],
HeaderInfo: {
TypeName: 'Order Item', TypeNamePlural: 'Order Items',
Title: {
Label: 'Product ID ', //A label is possible but it is not considered on the ObjectPage yet
Value: product_ID
},
Description: {Value: createdBy}
},
},
) {
amount @(

View File

@@ -3,17 +3,17 @@ namespace sap.capire.orders;
entity Orders : cuid, managed {
OrderNo : String @title:'Order Number'; //> readable key
Items : Composition of many Orders.Items on Items.up_ = $self;
Items : Composition of many Orders_Items on Items.up_ = $self;
buyer : User;
currency : Currency;
}
entity Orders.Items {
/*key*/ up_ : Association to Orders; // REVISIT: 'key' doesn't work due to bug in runtime
entity Orders_Items {
key ID : UUID;
product : Association to Products;
up_ : Association to Orders;
product : Association to Products @assert.integrity:false; // REVISIT: this is a temporary workaround for a glitch in cds-runtime
amount : Integer;
title : String; //> intentionally replicated as snapshot from product.title or alike
title : String; //> intentionally replicated as snapshot from product.title
price : Double;
}

View File

@@ -2,7 +2,6 @@
"name": "@capire/orders",
"version": "1.0.0",
"dependencies": {
"@capire/common": "*",
"@sap/cds": "^5"
"@sap/cds": ">=4.3.0"
}
}

View File

@@ -1,31 +0,0 @@
@Orders = http://localhost:4006/orders/Orders
### Read Orders
GET {{Orders}}?
###
GET {{Orders}}?$expand=Items
###
GET {{Orders}}(ID={{Order1}},IsActiveEntity=true)/Items
@Order1 = 64e718c9-ff99-47f1-8ca3-950c850777d4
@Order3 = e939604c-ab83-4d4f-bdb6-95fe30b3773e
### Create order, still inactive
POST {{Orders}}
Content-Type: application/json
{"ID": "{{Order3}}"}
### Get inactive order. We have to specify `IsActiveEntity`.
GET {{Orders}}(ID={{Order3}},IsActiveEntity=false)
### Activate order using `.../<servicename>.draftActivate`
POST {{Orders}}(ID={{Order3}},IsActiveEntity=false)/OrdersService.draftActivate
Content-Type: application/json
### Get active order
GET {{Orders}}(ID={{Order3}},IsActiveEntity=true)

View File

@@ -3,7 +3,7 @@ class OrdersService extends cds.ApplicationService {
/** register custom handlers */
init(){
const { 'Orders.Items':OrderItems } = this.entities
const { Orders_Items:OrderItems } = this.entities
this.before ('UPDATE', 'Orders', async function(req) {
const { ID, Items } = req.data

View File

@@ -26,7 +26,7 @@
"fiori": "cds watch fiori",
"media": "cds watch media",
"mocha": "npx mocha || echo",
"jest": "npx jest@^26",
"jest": "npx jest",
"test": "npm run jest --silent"
},
"mocha": {

View File

@@ -7,7 +7,7 @@
"index.cds"
],
"dependencies": {
"@sap/cds": "^5",
"@sap/cds": ">=4",
"express": "^4.17.1"
},
"scripts": {

View File

@@ -152,69 +152,69 @@ describe('cds.ql → cqn', () => {
expect(CQL`SELECT *,a,b from Foo`).to.eql(CQL`SELECT from Foo{*,a,b}`)
//> .to.eql... FIXME: see skipped 'should handle * correctly' below
expect(SELECT.from(Foo, ['a', 'b', '*']))
.to.eql(SELECT.from(Foo).columns('a', 'b', '*'))
.to.eql(SELECT.from(Foo).columns(['a', 'b', '*']))
.to.eql(
SELECT.from(Foo, (foo) => {
foo.a, foo.b, foo('*')
.to.eql(SELECT.from(Foo).columns('a', 'b', '*'))
.to.eql(SELECT.from(Foo).columns(['a', 'b', '*']))
.to.eql(
SELECT.from(Foo, (foo) => {
foo.a, foo.b, foo('*')
})
)
.to.eql({
SELECT: {
from: { ref: ['Foo'] },
columns: [{ ref: ['a'] }, { ref: ['b'] }, cdr ? '*' : { ref: ['*'] }],
},
})
)
.to.eql({
SELECT: {
from: { ref: ['Foo'] },
columns: [{ ref: ['a'] }, { ref: ['b'] }, cdr ? '*' : { ref: ['*'] }],
},
})
})
test('from ( ..., => _.expand ( x=>{...}))', () => {
// SELECT from Foo { *, x, bar.*, car{*}, boo { *, moo.zoo } }
expect(
SELECT.from(Foo, (foo) => {
foo('*'),
foo.x,
foo.car('*'),
foo.boo((b) => {
b('*'), b.moo.zoo((x) => x.y.z)
})
test('from ( ..., => _.expand ( x=>{...}))', () => {
// SELECT from Foo { *, x, bar.*, car{*}, boo { *, moo.zoo } }
expect(
SELECT.from(Foo, (foo) => {
foo('*'),
foo.x,
foo.car('*'),
foo.boo((b) => {
b('*'), b.moo.zoo((x) => x.y.z)
})
})
).to.eql({
SELECT: {
from: { ref: ['Foo'] },
columns: [
cdr ? '*' : { ref: ['*'] },
{ ref: ['x'] },
{ ref: ['car'], expand: ['*'] },
{
ref: ['boo'],
expand: ['*', { ref: ['moo', 'zoo'], expand: [{ ref: ['y', 'z'] }] }],
},
],
},
})
).to.eql({
SELECT: {
from: { ref: ['Foo'] },
columns: [
cdr ? '*' : { ref: ['*'] },
{ ref: ['x'] },
{ ref: ['car'], expand: ['*'] },
{
ref: ['boo'],
expand: ['*', { ref: ['moo', 'zoo'], expand: [{ ref: ['y', 'z'] }] }],
},
],
},
})
})
test('from ( ..., => _.inline ( _=>{...}))', () => {
// SELECT from Foo { *, x, bar.*, car{*}, boo { *, moo.zoo } }
expect(
SELECT.from(Foo, (foo) => {
foo.bar('*'),
foo.bar('.*'), //> leading dot indicates inline
foo.boo((x) => x.moo.zoo),
foo.boo((_) => _.moo.zoo) //> underscore arg name indicates inline
test('from ( ..., => _.inline ( _=>{...}))', () => {
// SELECT from Foo { *, x, bar.*, car{*}, boo { *, moo.zoo } }
expect(
SELECT.from(Foo, (foo) => {
foo.bar('*'),
foo.bar('.*'), //> leading dot indicates inline
foo.boo((x) => x.moo.zoo),
foo.boo((_) => _.moo.zoo) //> underscore arg name indicates inline
})
).to.eql({
SELECT: {
from: { ref: ['Foo'] },
columns: [
{ ref: ['bar'], expand: ['*'] },
{ ref: ['bar'], inline: ['*'] },
{ ref: ['boo'], expand: [{ ref: ['moo', 'zoo'] }] },
{ ref: ['boo'], inline: [{ ref: ['moo', 'zoo'] }] },
],
},
})
).to.eql({
SELECT: {
from: { ref: ['Foo'] },
columns: [
{ ref: ['bar'], expand: ['*'] },
{ ref: ['bar'], inline: ['*'] },
{ ref: ['boo'], expand: [{ ref: ['moo', 'zoo'] }] },
{ ref: ['boo'], inline: [{ ref: ['moo', 'zoo'] }] },
],
},
})
})
test('one / distinct ...', () => {
expect(SELECT.distinct.from(Foo).SELECT)
@@ -280,7 +280,7 @@ describe('cds.ql → cqn', () => {
).to.eql({
SELECT: {
from: { ref: ['Foo'] },
where: cds.version >= '5.3.0'
where: cdr
? [
// '(', //> this one is not required
{ ref: ['ID'] },
@@ -289,7 +289,7 @@ describe('cds.ql → cqn', () => {
'and',
{ ref: ['args'] },
'in',
{ list: args.map(val => ({ val })) },
{ val: args },
'and',
'(', //> this one is missing, and that's changing the logic -> that's a BUG
{ ref: ['x'] },

View File

@@ -6,9 +6,13 @@ else cds.User = cds.User.Privileged // hard core monkey patch for older cds rele
describe('Custom Handlers', () => {
it('should reject out-of-stock orders', async () => {
await POST('/browse/submitOrder', { book: 201, amount: 5 })
await POST('/browse/submitOrder', { book: 201, amount: 5 })
await expect(POST('/browse/submitOrder', { book: 201, amount: 5 })).to.be.rejectedWith(/409 - 5 exceeds stock for book #201/)
await expect(
Promise.all([
POST('/browse/submitOrder', { book: 201, amount: 5 }),
POST('/browse/submitOrder', { book: 201, amount: 5 }),
POST('/browse/submitOrder', { book: 201, amount: 5 }),
])
).to.be.rejectedWith(/409 - 5 exceeds stock for book #201/)
const { data } = await GET`/admin/Books/201/stock/$value`
expect(data).to.equal(2)
})

View File

@@ -43,7 +43,7 @@ describe('Localized Data', () => {
{ title: 'Jane Eyre', author: 'Charlotte Brontë', currency: { name: 'Pfund' } },
{ title: 'The Raven', author: 'Edgar Allen Poe', currency: { name: 'US-Dollar' } },
{ title: 'Eleonora', author: 'Edgar Allen Poe', currency: { name: 'US-Dollar' } },
{ title: 'Catweazle', author: 'Richard Carpenter', currency: { name: 'Yen' } },
{ title: 'Catweazle', author: 'Richard Carpenter', currency: { name: 'Euro' } },
])
})
@@ -85,7 +85,7 @@ describe('Localized Data', () => {
{ title: 'Jane Eyre', currency: { name: 'British Pound' } },
{ title: 'The Raven', currency: { name: 'US Dollar' } },
{ title: 'Eleonora', currency: { name: 'US Dollar' } },
{ title: 'Catweazle', currency: { name: 'Yen' } },
{ title: 'Catweazle', currency: { name: 'Euro' } },
])
})
})