Compare commits

..

27 Commits

Author SHA1 Message Date
Daniel
ae0cfc8c1e Adding querying tests 2020-07-30 18:45:19 +02:00
Daniel Hutzel
5c5afd2790 Merge pull request #118 from SAP-samples/fixed-tests
Fxed cwd in tests
2020-07-23 02:02:01 +02:00
Daniel
74ee6f34e4 Fixed jest mocha 2020-07-23 02:00:34 +02:00
Daniel
9a9b7aeb86 Fxed cwd in tests 2020-07-23 01:56:56 +02:00
Daniel Hutzel
cfc01bbc4f Merge pull request #117 from SAP-samples/fixed-tests
Secured cwd in test
2020-07-23 01:26:27 +02:00
Daniel
aaac6cc678 Secured cwd in test 2020-07-23 01:24:39 +02:00
Daniel Hutzel
99fdf0c038 Merge pull request #116 from SAP-samples/using-dot-env
Doing bindings in .env
2020-07-17 16:05:34 +02:00
Daniel
6ccecfecae Doing bindings in .env 2020-07-17 16:04:03 +02:00
Daniel Hutzel
e5f0a7ef73 Merge pull request #115 from SAP-samples/prepare-4
Prepare tests for upcomming rel 4
2020-07-17 01:52:02 +02:00
Daniel
de54f70570 Prepare tests for upcomming rel 4 2020-07-17 01:45:43 +02:00
Daniel Hutzel
e1c6118cb4 Merge pull request #114 from SAP-samples/fixed-messaging-test
Fixed messaging test
2020-07-17 01:05:14 +02:00
Daniel
a3e4865d97 Fixed messaging test 2020-07-17 01:03:30 +02:00
Daniel Hutzel
641df50422 Merge pull request #111 from SAP-samples/Recommending-VSCode-extension-for-CDS
Recommending vscode extension for CDS
2020-07-09 10:40:30 +02:00
Daniel
0f026ed56c It's finally out... 2020-07-09 10:37:35 +02:00
Daniel Hutzel
57a3c5f178 Merge pull request #110 from SAP-samples/simplified-debugging
Simplified debugging
2020-07-07 17:31:34 +02:00
Daniel
522ec8e071 Simplified debugging 2020-07-07 17:29:30 +02:00
Christian Georgi
7b1c3d8b3a More UI annotations
Make sure all entities can be properly displayed in Fiori preview.
Especially UI.LineItems seems to be mandatory in newer Fiori versions.
2020-07-02 19:35:04 +02:00
Christian Georgi
5ba69b5021 Update issue templates 2020-06-30 09:05:27 +02:00
Christian Georgi
fd796b54ef Redirect to community 2020-06-25 09:25:11 +02:00
ianquigley-sap
660344b623 Delete .npmrc
The @sap scoped registry should now point to registry.npmjs.org rather than npm.sap.com.  Please see https://jam4.sapjam.com/feed/item/eOtNGT1RSxVzknBtpSy2My for full details on the migration.  npm.sap.com will not be updated going forward for the CDS library
2020-06-24 12:07:40 +02:00
Daniel Hutzel
d20c29a758 Merge pull request #101 from SAP-samples/mocha-parallel
Running all mocha tests in parallel
2020-06-19 11:40:20 +02:00
Daniel
604cc0514c Running all mocha tests in parallel 2020-06-19 09:51:37 +02:00
I051442
ff351455dd Update for sap.fe usage starting with UI5 1.78 2020-06-18 17:19:02 +02:00
Daniel Hutzel
8f74bd32a9 Merge pull request #91 from SAP-samples/fix-for-mocha
Stick to standard .skip
2020-06-02 11:02:01 +02:00
Daniel
c181afe8c6 Stick to standard .skip 2020-06-02 10:57:57 +02:00
Daniel Hutzel
9ade3e6b6a Merge pull request #90 from SAP-samples/reviewed
Separated reviewed and reviews
2020-05-29 16:58:37 +02:00
Daniel
6f9737ae38 Separated reviewed and reviews 2020-05-29 16:56:03 +02:00
37 changed files with 381 additions and 252 deletions

View File

@@ -0,0 +1,10 @@
---
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

1
.mocharc.yml Normal file
View File

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

1
.npmrc
View File

@@ -1 +0,0 @@
@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": [
// "CDS Editor !",
"SAPSE.vscode-cds",
"dbaeumer.vscode-eslint",
"esbenp.prettier-vscode",
"mechatroner.rainbow-csv",

18
.vscode/launch.json vendored
View File

@@ -6,24 +6,16 @@
"configurations": [
{
"name": "bookshop",
"cwd": "${workspaceFolder}/bookshop",
"command": "cds watch bookshop",
"request": "launch",
"type": "node",
"runtimeExecutable": "npx",
"runtimeArgs": ["-n"],
"args": ["--", "cds", "run", "--with-mocks", "--in-memory?"],
"console": "integratedTerminal",
"type": "node-terminal",
"skipFiles": ["<node_internals>/**"]
},
{
"name": "Fiori App",
"cwd": "${workspaceFolder}/fiori",
"name": "Fiori app",
"command": "cds watch fiori",
"request": "launch",
"type": "node",
"runtimeExecutable": "npx",
"runtimeArgs": ["-n"],
"args": ["--", "cds", "run", "--with-mocks", "--in-memory?"],
"console": "integratedTerminal",
"type": "node-terminal",
"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 find a bug or need support, please [open an issue in here](https://github.com/SAP-samples/cloud-cap-samples/issues/new).
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).
## License

View File

@@ -11,6 +11,6 @@ ID;parent_ID;name
19;10;Fairy Tale
20;;Non-Fiction
21;20;Biography
22;20;Autobiography
22;21;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 20 21 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;Canadian Dollar;036;Cent;2
AUD;$;Australian Dollar;Australian 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 Canadian Dollar Australian 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

@@ -1,24 +0,0 @@
{
"extends": "eslint:recommended",
"env": {
"node": true,
"es6": true,
"jest": true
},
"parserOptions": {
"ecmaVersion": 2017
},
"globals": {
"SELECT": true,
"INSERT": true,
"UPDATE": true,
"DELETE": true,
"CREATE": true,
"DROP": true,
"cds": true
},
"rules": {
"no-console": "off",
"require-atomic-updates": "off"
}
}

30
ext/.gitignore vendored
View File

@@ -1,30 +0,0 @@
# CAP ext
_out
*.db
connection.properties
default-*.json
gen/
node_modules/
package-lock.json
target/
# Web IDE, App Studio
.che/
.gen/
# MTA
*_mta_build_tmp
*.mtar
mta_archives/
# Other
.DS_Store
*.orig
*.log
*.iml
*.flattened-pom.xml
# IDEs
.vscode
.idea

View File

@@ -1,6 +0,0 @@
using { sap.capire.bookshop.Books } from '@capire/bookshop';
extend Books with {
ISBN : String;
discount : Decimal @assert.range:[0,1];
}

View File

@@ -1,19 +0,0 @@
{
"name": "ext",
"version": "1.0.0",
"description": "A simple CAP project.",
"repository": "<Add your repository here>",
"license": "UNLICENSED",
"private": true,
"dependencies": {
"@capire/bookshop": "../bookshop",
"@sap/cds": "^4",
"express": "^4"
},
"devDependencies": {
"sqlite3": "^4"
},
"scripts": {
"start": "npx cds run"
}
}

View File

@@ -1,40 +0,0 @@
const cds = require ('@sap/cds')
const path = require ('path')
const fs = require ('fs')
const protected = {db:1,messaging:1,auth:1}
const { isfile } = cds.utils
cds.on('served', ()=>{
for (let each of cds.services) {
if (each.name in protected) continue
// search for local srv/<each>.js files and if exist
// activate them in a service extension sandbox
const impl = isfile (path.resolve('srv/'+each.name+'.js'))
if (impl) activate_sandboxed (each,impl)
}
})
function activate_sandboxed (srv,impl) {
console.log (`[cds] - extending ${srv.name} with sandboxed:`, {impl:path.relative(process.cwd(),impl)})
const src = fs.readFileSync (impl)
const sandbox = Object.keys(global).filter(name => name !== 'cds')
const fn = new Function (
'module','cds','global','process', ...sandbox,
src
)
// restricted sandboxed variant of 'module' and 'cds'
const module = {}
const cds = {
service: {
impl: fn=>fn
}
}
fn (module,cds,undefined,undefined, ...sandbox.map((()=>(undefined))))
if (typeof module.exports === 'function') try {
module.exports.call (srv,srv)
} catch (e) {
console.log (e)
}
}
module.exports = cds.server

View File

@@ -1,10 +0,0 @@
module.exports = cds.service.impl(function(){
this.before(['CREATE','UPDATE'],'Books', req => { //> ....
const book = req.data
if (book.stock < 10 && book.discount > 0.5) {
req.error('Hey, da sind so wenig übrig, die wollen wir nicht zu billig verticken')
}
})
})

View File

@@ -1,9 +0,0 @@
console.log ('Böses Zeug', global, process, cds)
// process.exit()
// const fs = require('fs')
// cds.run('Böses Zeugs')
// SELECT.from ('Foo')
module.exports = cds.service.impl(function(){
this.after('READ','Books', each => each.title += ' (served through sandbox)')
})

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,9 +1,9 @@
/*
Common Annotations shared by all apps
Common Annotations shared by all apps
*/
using { sap.capire.bookshop as my } from '@capire/bookshop';
using { sap.common } from '@capire/common';
////////////////////////////////////////////////////////////////////////////
//
@@ -13,7 +13,7 @@ annotate my.Books with @(
Common.SemanticKey: [title],
UI: {
Identification: [{Value:title}],
SelectionFields: [ ID, author_ID, price, currency_code ],
SelectionFields: [ ID, author_ID, price, currency_code ],
LineItem: [
{Value: ID},
{Value: title},
@@ -28,25 +28,18 @@ annotate my.Books with @(
author @ValueList.entity:'Authors';
};
annotate my.Authors with @(
UI: {
Identification: [{Value:name}],
}
);
////////////////////////////////////////////////////////////////////////////
//
// Books Details
//
annotate my.Books with @(
UI: {
HeaderInfo: {
TypeName: '{i18n>Book}',
TypeNamePlural: '{i18n>Books}',
Title: {Value: title},
Description: {Value: author.name}
},
HeaderInfo: {
TypeName: '{i18n>Book}',
TypeNamePlural: '{i18n>Books}',
Title: {Value: title},
Description: {Value: author.name}
},
}
);
@@ -66,15 +59,199 @@ 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 {
name @title: '{i18n>Genre}';
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>AuthorName}';
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}';
}

View File

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

View File

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

View File

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

1
reviewed/.env Normal file
View File

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

View File

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

View File

@@ -12,9 +12,13 @@ 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')
// reflect entities required below...
const { Books } = db.entities('sap.capire.bookshop')
const { Reviews } = ReviewsService.entities
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})
@@ -24,10 +28,10 @@ cds.once('served', async()=>{
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)
const [ id ] = req.params
const tx = ReviewsService.tx(req)
return tx.read (Reviews) .where ({ subject: String(id) })
.columns (req.query.SELECT.columns)
}))
})

View File

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

View File

@@ -1 +1,2 @@
cds.requires.messaging.kind = file-based-messaging
PORT = 5005

View File

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

View File

@@ -1,13 +0,0 @@
#################################################
#
# 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

@@ -261,7 +261,7 @@ describe('cds.ql → cqn', () => {
// same for works distinct
})
xtest('where ( ... cql | {x:y} )', () => {
test.skip('where ( ... cql | {x:y} )', () => {
const args = [`foo`, "'bar'", 3]
const ID = 11
@@ -466,47 +466,38 @@ 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.
*/
test('with', () => {
expect(UPDATE(Foo).with(`foo=`, 11, `bar-=`, 22))
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))
.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({
// 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({
expect(UPDATE(Foo).with(`bar = coalesce(x,y), car = 'foo''s bar, car'`)).to.eql({
UPDATE: {
entity: 'Foo',
data: { foo: 11 },
data: {
car: "foo's bar, car",
},
with: {
bar: { xpr: [{ ref: ['bar'] }, '-', { val: 22 }] },
bar: { func: 'coalesce', args: [{ ref: ['x'] }, { ref: ['y'] }] },
},
},
})

View File

@@ -1,6 +1,10 @@
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,3 +1,12 @@
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

@@ -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', ()=>{

71
test/querying.test.js Normal file
View File

@@ -0,0 +1,71 @@
describe('Querying', () => {
const { GET, expect } = require('./capire').launch('bookshop')
const Authors = {name:'sap.capire.bookshop.Authors'}
it('should SELECT from Authors', async () => {
const authors = await SELECT.from (Authors, a => {
a.name
})
expect(authors).to.eql([
{ name: 'Emily Brontë' },
{ name: 'Charlotte Brontë' },
{ name: 'Edgar Allen Poe' },
{ name: 'Richard Carpenter' },
])
})
it('should SELECT name from Authors', async () => {
const authors = await SELECT.from (Authors, a => {
a.name
})
expect(authors).to.eql([
{ name: 'Emily Brontë' },
{ name: 'Charlotte Brontë' },
{ name: 'Edgar Allen Poe' },
{ name: 'Richard Carpenter' },
])
})
it('should GET /Authors?$select=name&$expand=books($select=title)', async () => {
const {data:{value:authors}} = await GET ('/admin/Authors?$select=name&$expand=books($select=title)')
expect(authors).to.containSubset([
{ name: 'Emily Brontë', books: [{ title: 'Wuthering Heights' }] },
{ name: 'Charlotte Brontë', books: [{ title: 'Jane Eyre' }] },
{ name: 'Edgar Allen Poe', books: [{ title: 'The Raven' }, { title: 'Eleonora' }] },
{ name: 'Richard Carpenter', books: [{ title: 'Catweazle' }] },
])
})
it('should SELECT from Authors { name, books { title } }', async () => {
const authors = await SELECT.from(Authors, a => { a.name, a.books(b=>{ b.title }) })
expect(authors).to.eql([
{ name: 'Emily Brontë', books: [{ title: 'Wuthering Heights' }] },
{ name: 'Charlotte Brontë', books: [{ title: 'Jane Eyre' }] },
{ name: 'Edgar Allen Poe', books: [{ title: 'The Raven' }, { title: 'Eleonora' }] },
{ name: 'Richard Carpenter', books: [{ title: 'Catweazle' }] },
])
})
it('should GET /Authors?$expand=books', async () => {
const {data:{value:authors}} = await GET ('/admin/Authors?$select=name&$expand=books($select=title)')
expect(authors).to.containSubset([
{ name: 'Emily Brontë', books: [{ title: 'Wuthering Heights' }] },
{ name: 'Charlotte Brontë', books: [{ title: 'Jane Eyre' }] },
{ name: 'Edgar Allen Poe', books: [{ title: 'The Raven' }, { title: 'Eleonora' }] },
{ name: 'Richard Carpenter', books: [{ title: 'Catweazle' }] },
])
})
it('should SELECT from Authors { *, books{*} }', async () => {
const authors = await SELECT.from(Authors, a => { a('*'), a.books(b => b('*')) })
// const authors = await SELECT.from(Authors, ['*',{ref:['books'], expand:['*']}])
// const authors = await SELECT.from(Authors, [{ref:['*']},{ref:['books'], expand:[{ref:['*']}]}])
expect(authors).to.eql([
{ name: 'Emily Brontë', books: [{ title: 'Wuthering Heights' }] },
{ name: 'Charlotte Brontë', books: [{ title: 'Jane Eyre' }] },
{ name: 'Edgar Allen Poe', books: [{ title: 'The Raven' }, { title: 'Eleonora' }] },
{ name: 'Richard Carpenter', books: [{ title: 'Catweazle' }] },
])
})
})