Compare commits

..

2 Commits

Author SHA1 Message Date
Daniel
98577c1ecc re cap 2020-05-15 09:00:56 +02:00
Daniel
e8cd0e3231 Samples for re:cap 2020-05-15 08:37:40 +02:00
38 changed files with 235 additions and 383 deletions

9
.env
View File

@@ -1 +1,8 @@
cds.features.snapi = y
cds.features.snapi = true
cds.odata.version = v4
cds.odata.containment = true
cds.odata.proxies = true
cds.odata.format = structured
cds.cdsc.beta.uniqueconstraints = true
cds.cdsc.beta.aspectCompositions = true
cds.cdsc.severities.unexpected-key = info

View File

@@ -1,10 +0,0 @@
---
name: Question, feedback or bug?
about: Use our community!
title: ''
labels: ''
assignees: ''
---
Please use our community on https://answers.sap.com/tags/9f13aee1-834c-4105-8e43-ee442775e5ce

View File

@@ -1 +0,0 @@
parallel: true

1
.npmrc Normal file
View File

@@ -0,0 +1 @@
@sap:registry=https://npm.sap.com

View File

@@ -4,7 +4,7 @@
// List of extensions which should be recommended for users of this workspace.
"recommendations": [
"SAPSE.vscode-cds",
// "CDS Editor !",
"dbaeumer.vscode-eslint",
"esbenp.prettier-vscode",
"mechatroner.rainbow-csv",

18
.vscode/launch.json vendored
View File

@@ -6,16 +6,24 @@
"configurations": [
{
"name": "bookshop",
"command": "cds watch bookshop",
"cwd": "${workspaceFolder}/bookshop",
"request": "launch",
"type": "node-terminal",
"type": "node",
"runtimeExecutable": "npx",
"runtimeArgs": ["-n"],
"args": ["--", "cds", "run", "--with-mocks", "--in-memory?"],
"console": "integratedTerminal",
"skipFiles": ["<node_internals>/**"]
},
{
"name": "Fiori app",
"command": "cds watch fiori",
"name": "Fiori App",
"cwd": "${workspaceFolder}/fiori",
"request": "launch",
"type": "node-terminal",
"type": "node",
"runtimeExecutable": "npx",
"runtimeArgs": ["-n"],
"args": ["--", "cds", "run", "--with-mocks", "--in-memory?"],
"console": "integratedTerminal",
"skipFiles": ["<node_internals>/**"]
}
],

View File

@@ -50,7 +50,7 @@ npx jest
## Get Support
Check out the documentation at [https://cap.cloud.sap](https://cap.cloud.sap). <br>
In case you have a question, find a bug, or otherwise need support, please use our [community](https://answers.sap.com/tags/9f13aee1-834c-4105-8e43-ee442775e5ce).
In case you find a bug or need support, please [open an issue in here](https://github.com/SAP-samples/cloud-cap-samples/issues/new).
## License

View File

@@ -11,6 +11,6 @@ ID;parent_ID;name
19;10;Fairy Tale
20;;Non-Fiction
21;20;Biography
22;21;Autobiography
22;20;Autobiography
23;20;Essay
24;20;Speech
1 ID parent_ID name
11 19 10 Fairy Tale
12 20 Non-Fiction
13 21 20 Biography
14 22 21 20 Autobiography
15 23 20 Essay
16 24 20 Speech

View File

@@ -2,7 +2,7 @@ code;symbol;name;descr;numcode;minor;exponent
EUR;€;Euro;European Euro;978;Cent;2
USD;$;US Dollar;United States Dollar;840;Cent;2
CAD;$;Canadian Dollar;Canadian Dollar;124;Cent;2
AUD;$;Australian Dollar;Australian Dollar;036;Cent;2
AUD;$;Australian Dollar;Canadian Dollar;036;Cent;2
GBP;£;British Pound;Great Britain Pound;826;Penny;2
ILS;₪;Shekel;Israeli New Shekel;376;Agorat;2
INR;₹;Rupee;Indian Rupee;356;Paise;2
1 code symbol name descr numcode minor exponent
2 EUR Euro European Euro 978 Cent 2
3 USD $ US Dollar United States Dollar 840 Cent 2
4 CAD $ Canadian Dollar Canadian Dollar 124 Cent 2
5 AUD $ Australian Dollar Australian Dollar Canadian Dollar 036 Cent 2
6 GBP £ British Pound Great Britain Pound 826 Penny 2
7 ILS Shekel Israeli New Shekel 376 Agorat 2
8 INR Rupee Indian Rupee 356 Paise 2

View File

@@ -7,19 +7,7 @@ AuthorID = Author ID
Stock = Stock
Name = Name
AuthorName = Author's Name
DateOfBirth = Date of Birth
DateOfDeath = Date of Death
PlaceOfBirth = Place of Birth
PlaceOfDeath = Place of Death
Authors = Authors
Order = Order
Orders = Orders
Price = Price
Genre = Genre
Genres = Genres
SubGenres = Sub Genres
NumCode = Numeric Code
MinorUnit = Minor Unit
Exponent = Exponent

View File

@@ -1,8 +1,5 @@
sap.ui.define(["sap/fe/core/AppComponent"], function(AppComponent) {
"use strict";
return AppComponent.extend("admin.Component", {
metadata: { manifest: "json" }
});
});
sap.ui.define(["sap/fe/core/AppComponent"], ac => ac.extend("admin.Component", {
metadata:{ manifest:'json' }
}))
/* eslint no-undef:0 */

View File

@@ -24,7 +24,7 @@
"sap.ui5": {
"dependencies": {
"libs": {
"sap.fe.templates": {}
"sap.fe": {}
}
},
"models": {

View File

@@ -1,7 +1,5 @@
sap.ui.define(["sap/fe/core/AppComponent"], function(AppComponent) {
"use strict";
return AppComponent.extend("bookshop.Component", {
metadata: { manifest: "json" }
});
});
sap.ui.define(["sap/fe/core/AppComponent"], ac => ac.extend("bookshop.Component", {
metadata:{ manifest:'json' }
}))
/* eslint no-undef:0 */

View File

@@ -24,7 +24,7 @@
"sap.ui5": {
"dependencies": {
"libs": {
"sap.fe.templates": {}
"sap.fe": {}
}
},
"models": {

View File

@@ -3,7 +3,7 @@
*/
using { sap.capire.bookshop as my } from '@capire/bookshop';
using { sap.common } from '@capire/common';
////////////////////////////////////////////////////////////////////////////
//
@@ -28,6 +28,13 @@ annotate my.Books with @(
author @ValueList.entity:'Authors';
};
annotate my.Authors with @(
UI: {
Identification: [{Value:name}],
}
);
////////////////////////////////////////////////////////////////////////////
//
// Books Details
@@ -59,199 +66,15 @@ annotate my.Books with {
descr @UI.MultiLineText;
}
////////////////////////////////////////////////////////////////////////////
//
// Genres List
//
annotate my.Genres with @(
Common.SemanticKey: [name],
UI: {
SelectionFields: [ name ],
LineItem:[
{Value: name},
{Value: parent.name, Label: 'Main Genre'},
],
}
);
////////////////////////////////////////////////////////////////////////////
//
// Genre Details
//
annotate my.Genres with @(
UI: {
Identification: [{Value:name}],
HeaderInfo: {
TypeName: '{i18n>Genre}',
TypeNamePlural: '{i18n>Genres}',
Title: {Value: name},
Description: {Value: ID}
},
Facets: [
{$Type: 'UI.ReferenceFacet', Label: '{i18n>SubGenres}', Target: 'children/@UI.LineItem'},
],
}
);
////////////////////////////////////////////////////////////////////////////
//
// Genres Elements
//
annotate my.Genres with {
ID @title: '{i18n>ID}';
name @title: '{i18n>Genre}';
}
////////////////////////////////////////////////////////////////////////////
//
// Authors List
//
annotate my.Authors with @(
Common.SemanticKey: [name],
UI: {
Identification: [{Value:name}],
SelectionFields: [ name ],
LineItem:[
{Value: ID},
{Value: name},
{Value: dateOfBirth},
{Value: dateOfDeath},
{Value: placeOfBirth},
{Value: placeOfDeath},
],
}
);
////////////////////////////////////////////////////////////////////////////
//
// Author Details
//
annotate my.Authors with @(
UI: {
HeaderInfo: {
TypeName: '{i18n>Author}',
TypeNamePlural: '{i18n>Authors}',
Title: {Value: name},
Description: {Value: dateOfBirth}
},
Facets: [
{$Type: 'UI.ReferenceFacet', Target: 'books/@UI.LineItem'},
],
}
);
////////////////////////////////////////////////////////////////////////////
//
// Authors Elements
//
annotate my.Authors with {
ID @title:'{i18n>ID}' @UI.HiddenFilter;
name @title:'{i18n>Name}';
dateOfBirth @title:'{i18n>DateOfBirth}';
dateOfDeath @title:'{i18n>DateOfDeath}';
placeOfBirth @title:'{i18n>PlaceOfBirth}';
placeOfDeath @title:'{i18n>PlaceOfDeath}';
}
////////////////////////////////////////////////////////////////////////////
//
// Languages List
//
annotate common.Languages with @(
Common.SemanticKey: [code],
Identification: [{Value:code}],
UI: {
SelectionFields: [ name, descr ],
LineItem:[
{Value: code},
{Value: name},
],
}
);
////////////////////////////////////////////////////////////////////////////
//
// Language Details
//
annotate common.Languages with @(
UI: {
HeaderInfo: {
TypeName: '{i18n>Language}',
TypeNamePlural: '{i18n>Languages}',
Title: {Value: name},
Description: {Value: descr}
},
Facets: [
{$Type: 'UI.ReferenceFacet', Label: '{i18n>Details}', Target: '@UI.FieldGroup#Details'},
],
FieldGroup#Details: {
Data: [
{Value: code},
{Value: name},
{Value: descr}
]
},
}
);
////////////////////////////////////////////////////////////////////////////
//
// Currencies List
//
annotate common.Currencies with @(
Common.SemanticKey: [code],
Identification: [{Value:code}],
UI: {
SelectionFields: [ name, descr ],
LineItem:[
{Value: descr},
{Value: symbol},
{Value: code},
],
}
);
////////////////////////////////////////////////////////////////////////////
//
// Currency Details
//
annotate common.Currencies with @(
UI: {
HeaderInfo: {
TypeName: '{i18n>Currency}',
TypeNamePlural: '{i18n>Currencies}',
Title: {Value: descr},
Description: {Value: code}
},
Facets: [
{$Type: 'UI.ReferenceFacet', Label: '{i18n>Details}', Target: '@UI.FieldGroup#Details'},
{$Type: 'UI.ReferenceFacet', Label: '{i18n>Extended}', Target: '@UI.FieldGroup#Extended'},
],
FieldGroup#Details: {
Data: [
{Value: name},
{Value: symbol},
{Value: code},
{Value: descr}
]
},
FieldGroup#Extended: {
Data: [
{Value: numcode},
{Value: minor},
{Value: exponent}
]
},
}
);
////////////////////////////////////////////////////////////////////////////
//
// Currencies Elements
//
annotate common.Currencies with {
numcode @title:'{i18n>NumCode}';
minor @title:'{i18n>MinorUnit}';
exponent @title:'{i18n>Exponent}';
name @title:'{i18n>AuthorName}';
}

View File

@@ -40,8 +40,7 @@
</script>
<script src="https://sapui5.hana.ondemand.com/test-resources/sap/ushell/bootstrap/sandbox.js"></script>
<!-- <script src="https://sapui5.hana.ondemand.com/resources/sap-ui-core.js" -->
<script src="https://sapui5.hana.ondemand.com/1.78.6/resources/sap-ui-core.js"
<script src="https://sapui5.hana.ondemand.com/resources/sap-ui-core.js"
data-sap-ui-libs="sap.m, sap.ushell, sap.collaboration, sap.ui.layout"
data-sap-ui-compatVersion="edge"
data-sap-ui-theme="sap_fiori_3"

View File

@@ -1,8 +1,5 @@
sap.ui.define(["sap/fe/core/AppComponent"], function(AppComponent) {
"use strict";
return AppComponent.extend("orders.Component", {
metadata: { manifest: "json" }
});
});
sap.ui.define(["sap/fe/core/AppComponent"], ac => ac.extend("orders.Component", {
metadata:{ manifest:'json' }
}))
/* eslint no-undef:0 */

View File

@@ -24,7 +24,7 @@
"sap.ui5": {
"dependencies": {
"libs": {
"sap.fe.templates": {}
"sap.fe": {}
}
},
"models": {

8
orders/.env Normal file
View File

@@ -0,0 +1,8 @@
cds.features.snapi = true
cds.odata.version = v4
cds.odata.containment = true
cds.odata.proxies = true
cds.odata.format = structured
cds.cdsc.beta.uniqueconstraints = true
cds.cdsc.beta.aspectCompositions = true
cds.cdsc.severities.unexpected-key = info

View File

@@ -6,14 +6,14 @@ module.exports = cds.service.impl(function() {
// Reduce stock of ordered books if available stock suffices
this.before ('CREATE', 'Orders', (req) => {
const { Items: items } = req.data
return cds.transaction(req) .run (items.map (item =>
UPDATE (Books) .where ('ID =', item.book_ID)
.and ('stock >=', item.amount)
.set ('stock -=', item.amount)
const { Items: OrderItems } = req.data
return cds.transaction(req) .run (()=> OrderItems.map (order =>
UPDATE (Books) .where ('ID =', order.book_ID)
.and ('stock >=', order.amount)
.set ('stock -=', order.amount)
)) .then (all => all.forEach ((affectedRows,i) => {
if (affectedRows === 0) req.error (409,
`${items[i].amount} exceeds stock for book #${items[i].book_ID}`
`${OrderItems[i].amount} exceeds stock for book #${OrderItems[i].book_ID}`
)
}))
})

View File

@@ -19,8 +19,6 @@
"sqlite3": "^4"
},
"scripts": {
"bookshop": "cds watch bookshop",
"fiori": "cds watch fiori",
"mocha": "npx mocha || echo",
"jest": "npx jest --verbose",
"test": "npm run jest -s"

View File

@@ -0,0 +1,5 @@
using { sap.capire.bookshop.Authors } from '@capire/bookshop';
define view Foo as select from Authors {
ID, name, books[where title like 'Cat%'].currency.code
};

View File

@@ -0,0 +1,27 @@
using { User, cuid, managed } from '@sap/cds/common';
// Looks like inheritance, but isn't
entity Foo @bar : cuid, managed { bar:Car; }
// It's just syntactical sugar for Aspects
entity Boo {}
extend Boo with cuid;
extend Boo with managed;
extend Boo with { bar:Car; }
annotate Boo with @bar;
// There's close to no limits
entity Moo : Foo {}
entity Zoo {}; extend Zoo with Foo;
// This one will apply to all uses above
type Car : String;
annotate Car with @car;
// And these to all uses here and wherever else
extend managed with {
notes : String;
}
// CDS is built with CDS
annotate cds.UUID with @odata.Type: 'Edm.Integer';

10
recap/extending-views.cds Normal file
View File

@@ -0,0 +1,10 @@
using { CatalogService } from '@capire/bookshop';
extend sap.capire.bookshop.Books with {
ISBN : String;
}
/** your docs go here */
extend projection CatalogService.Books with {
ISBN
}

View File

@@ -1 +0,0 @@
cds.requires.messaging.kind = file-based-messaging

View File

@@ -1,43 +0,0 @@
////////////////////////////////////////////////////////////////////////////
//
// This is an example of using a project-local server.js to intercept
// the default bootstrapping process.
//
const cds = require ('@sap/cds')
// Connect CatalogService and ReviewsService when all are served...
cds.once('served', async ({CatalogService}) => {
// reflect entity definitions used below...
const { Books } = cds.entities('sap.capire.bookshop')
const { Reviews } = cds.entities('ReviewsService')
// prepend the following handler so it overrides the default handler
CatalogService.prepend (srv => srv.on ('READ', 'Books/reviews', (req) => {
console.debug ('> delegating request to ReviewsService')
const [id] = req.params, { columns, limit } = req.query.SELECT
return SELECT(columns).from(Reviews).limit(limit).where({subject:String(id)})
}))
// subscribe to events emitted by ReviewsService
const ReviewsService = await cds.connect.to ('ReviewsService')
ReviewsService.on ('reviewed', (msg) => {
console.debug ('> received:', msg.event, msg.data)
const { subject, rating } = msg.data
return UPDATE(Books,subject).with({rating})
})
})
// Other bootstrapping events you could hook in to...
/* eslint-disable no-unused-vars */
cds.on('bootstrap',(app) => {/* ... */})
cds.on('loaded', (model) => {/* ... */})
cds.on('connect', (srv) => {/* ... */})
cds.on('serving', (srv) => {/* ... */})
cds.once('served', (all) => {/* ... */})
cds.once('listening', ({server,url}) => {/* ... */})
// Delegate bootstrapping to built-in server.js
module.exports = cds.server

View File

@@ -1,3 +1 @@
cds.requires.messaging.kind = file-based-messaging
cds.odata.skipValidation = true
PORT = 5005

View File

@@ -12,12 +12,15 @@
},
"scripts": {
"reviews-service": "cds watch",
"books-reviewed": "cds watch ../reviewed"
"bookshop": "cds watch test/bookshop"
},
"cds": {
"requires": {
"db": {
"kind": "sql"
},
"messaging": {
"kind": "file-based-messaging"
}
}
}

View File

@@ -1,16 +1,13 @@
#################################################
#
# To ReviewsService
# To ReviewsService mocked in bookshop process
#
# move the right down:
@reviews-service = http://localhost:4004/reviews
@reviews-service = http://localhost:5005/reviews
### Get all reviews
GET {{reviews-service}}/Reviews
GET http://localhost:4004/reviews/Reviews?
### Add a new review (with random rating)
POST {{reviews-service}}/Reviews
###
POST http://localhost:4004/reviews/Reviews
Content-Type: application/json;IEEE754Compatible=true
{"subject":"201", "title":"boo"}
@@ -23,19 +20,17 @@ Content-Type: application/json;IEEE754Compatible=true
# (both in-process as well as separate one)
#
@bookshop = http://localhost:4004
### Request to CatalogService > delegated to ReviewsService
GET {{bookshop}}/browse/Books(201)/reviews?
GET http://localhost:4004/browse/Books(201)/reviews?
&$select=rating,date,reviewer,title
### Alternative OData URL
GET {{bookshop}}/browse/Books/201/reviews?
&$select=rating,date,title
&$top=3
GET http://localhost:4004/browse/Books/201/reviews?
&$select=rating,date,reviewer,title
###
GET {{bookshop}}/browse/Books(201)?
GET http://localhost:4004/browse/Books(201)?
&$select=ID,title,rating
&$expand=reviews
# Note: the $expand only works in case of ReviewsService in same process

13
reviews/test/5005.http Normal file
View File

@@ -0,0 +1,13 @@
#################################################
#
# To ReviewsService running as separate process
#
GET http://localhost:5005/reviews/Reviews?
###
POST http://localhost:5005/reviews/Reviews
Content-Type: application/json;IEEE754Compatible=true
{"subject":"201", "title":"boo"}

View File

@@ -2,8 +2,8 @@
"name": "@capire/bookshop-with-reviews",
"version": "1.0.0",
"dependencies": {
"@capire/bookshop": "../bookshop",
"@capire/reviews": "../reviews",
"@capire/bookshop": "../../../bookshop",
"@capire/reviews": "..",
"@sap/cds": "^3.33.1",
"express": "^4.17.1"
},
@@ -14,6 +14,9 @@
},
"ReviewsService": {
"kind": "odata", "model": "@capire/reviews"
},
"messaging": {
"kind": "file-based-messaging"
}
}
}

View File

@@ -0,0 +1,51 @@
////////////////////////////////////////////////////////////////////////////
//
// This is an example of using a project-local server.js to intercept
// the default bootstrapping process.
//
const cds = require ('@sap/cds')
// Mashup services after all are served...
cds.once('served', async()=>{
// react on event messages from reviews service
const ReviewsService = await cds.connect.to ('ReviewsService')
const db = await cds.connect.to ('db')
ReviewsService.on ('reviewed', (msg) => {
console.debug ('> received:', msg.event, msg.data)
const { Books } = db.entities('sap.capire.bookshop')
const { subject, rating } = msg.data
const tx = db.tx (msg) // TODO: db.tx(msg) fully implemented?
return tx.update (Books,subject) .with ({rating})
})
// delegate requests to read reviews to ReviewsService
const CatalogService = await cds.connect.to ('CatalogService')
CatalogService.impl (srv => srv.on ('READ', 'Books/reviews', (req) => {
console.debug ('> delegating to ReviewsService')
const { Reviews } = ReviewsService.entities
const [ subject ] = req.params
const tx = ReviewsService.tx (req)
return tx.read (Reviews) .where({subject}) .columns (req.query.SELECT.columns)
}))
})
// Other bootstrapping events you could hook in to...
/* eslint-disable no-unused-vars */
cds.on('bootstrap',(app) => {/* ... */})
cds.on('loaded', (model) => {/* ... */})
cds.on('connect', (srv) => {/* ... */})
cds.on('serving', (srv) => {/* ... */})
cds.once('listening', ({server,url}) => {/* ... */})
// Delegate bootstrapping to built-in server.js
module.exports = cds.server
// Monkey-patching older releases
if (cds.version < '3.33.4') cds.once('listening', ()=> cds.emit('served'))
// Launch server if started directly from command-line
if (!module.parent) cds.server()

View File

@@ -261,7 +261,7 @@ describe('cds.ql → cqn', () => {
// same for works distinct
})
test('where ( ... cql | {x:y} )', () => {
xtest('where ( ... cql | {x:y} )', () => {
const args = [`foo`, "'bar'", 3]
const ID = 11
@@ -278,6 +278,7 @@ describe('cds.ql → cqn', () => {
from: { ref: ['Foo'] },
where: cdr
? [
// '(', //> this one is not required
{ ref: ['ID'] },
'=',
{ val: ID },
@@ -286,7 +287,7 @@ describe('cds.ql → cqn', () => {
'in',
{ val: args },
'and',
'(',
'(', //> this one is missing, and that's changing the logic -> that's a BUG
{ ref: ['x'] },
'like',
{ val: '%x%' },
@@ -297,6 +298,7 @@ describe('cds.ql → cqn', () => {
')',
]
: [
'(', //> this one is not required
{ ref: ['ID'] },
'=',
{ val: ID },
@@ -305,7 +307,7 @@ describe('cds.ql → cqn', () => {
'in',
{ val: args },
'and',
'(',
// '(', //> this one is missing, and that's changing the logic -> that's a BUG
{ ref: ['x'] },
'like',
{ val: '%x%' },
@@ -335,31 +337,11 @@ describe('cds.ql → cqn', () => {
{ val: 'bar' },
',',
{ val: 3 },
')'
]
}
')',
],
},
})
const cqnFluent = {
SELECT: {
from: { ref: ['Foo'] },
where: [
{ ref: ['ID'] },
'=',
{ val: ID },
'and',
{ ref: ['x'] },
'in',
{ list: [
{ val: 'foo' },
{ val: 'bar' },
{ val: 3 }
] }
]
}
}
expect(SELECT.from(Foo).where(`ID=`, ID, `and x in`, args)).to.eql(cqnFluent)
expect(SELECT.from(Foo).where(`ID=`, ID, `and x in`, args)).to.eql(cqn)
expect(SELECT.from(Foo).where(`ID=${ID} and x in (${args})`)).to.eql(cqn)
expect(
@@ -484,38 +466,47 @@ describe('cds.ql → cqn', () => {
UPDATE.with allows to pass in plain data payloads, e.g. as obtained from REST clients.
In addition, UPDATE.with supports specifying expressions, either in CQL fragements
notation or as simple expression objects.
UPDATE.data allows to pass in plain data payloads, e.g. as obtained from REST clients.
The passed in object can be modified subsequently, e.g. by adding or modifying values
before the query is finally executed.
*/
test('with + data', () => {
if (cds.version < '4.1.0') return
const o = {}
const q = UPDATE(Foo).data(o).with(`bar-=`, 22)
o.foo = 11
expect(q)
.to.eql(UPDATE(Foo).with(`foo=`, 11, `bar-=`, 22))
test('with', () => {
expect(UPDATE(Foo).with(`foo=`, 11, `bar-=`, 22))
.to.eql(UPDATE(Foo).with({ foo: 11, bar: { '-=': 22 } }))
.to.eql({
UPDATE: {
entity: 'Foo',
data: { foo: 11 },
with: {
foo: { val: 11 },
bar: { xpr: [{ ref: ['bar'] }, '-', { val: 22 }] },
},
},
})
// some more
expect(UPDATE(Foo).with(`bar = coalesce(x,y), car = 'foo''s bar, car'`)).to.eql({
// expect(UPDATE(Foo).with(`bar = coalesce(x,y), car = 'foo''s bar, car'`)).to.eql({
// UPDATE: {
// entity: 'Foo',
// with: {
// bar: { func: 'coalesce', args: [{ ref: ['x'] }, { ref: ['y'] }] },
// car: { val: "foo's bar, car" },
// },
// },
// })
})
/*
UPDATE.data allows to pass in plain data payloads, e.g. as obtained from REST clients.
The passed in object can be modified subsequently, e.g. by adding or modifying values
before the query is finally executed.
*/
test('data', () => {
const o = {}
const q = UPDATE(Foo).data(o).with(`bar-=`, 22)
o.foo = 11
expect(q).to.eql({
UPDATE: {
entity: 'Foo',
data: {
car: "foo's bar, car",
},
data: { foo: 11 },
with: {
bar: { func: 'coalesce', args: [{ ref: ['x'] }, { ref: ['y'] }] },
bar: { xpr: [{ ref: ['bar'] }, '-', { val: 22 }] },
},
},
})

View File

@@ -1,10 +1,6 @@
const { expect } = require('./capire')
const cds = require('@sap/cds')
const cwd = process.cwd()
before (()=> process.chdir(__dirname))
after(()=> process.chdir(cwd))
describe('Consuming Services locally', () => {
//
before('bootstrap db and services', async () => {

View File

@@ -1,12 +1,3 @@
const cwd = process.cwd()
const is_jest = !!global.test
if (is_jest) { // it's jest
global.before = (msg,fn) => global.beforeAll(fn||msg)
global.after = (msg,fn) => global.afterAll(fn||msg)
}
before (()=> process.chdir(__dirname))
after (()=> process.chdir(cwd))
describe('Custom Handlers', () => {
const { GET, POST, expect } = require('./capire').launch('bookshop')

View File

@@ -72,7 +72,7 @@ describe('Localized Data', () => {
])
})
it('supports @cds.localized:false', async ()=>{
xit('supports @cds.localized:false', async ()=>{
const { data } = await GET(`/browse/BooksSans?&$select=title,localized_title&$expand=currency&$filter=locale eq 'de' or locale eq null`, {
headers: { 'Accept-Language': 'de' },
})

View File

@@ -21,7 +21,7 @@ describe('Messaging', ()=>{
let N=0, received=[], M=0
it ('should add messaging event handlers', ()=>{
srv.on('reviewed', (msg)=> {received.push(msg)})
srv.on('reviewed', (msg)=> received.push(msg))
})
it ('should add more messaging event handlers', ()=>{