Compare commits

..

16 Commits

Author SHA1 Message Date
Daniel
d073403010 cosmetics 2021-02-26 09:30:56 +01:00
Daniel
2552269cec Samples for build-tasks 2021-02-26 09:19:26 +01:00
Daniel
d368eb2ff5 fixed dependency -> never use "latest" 2021-02-19 16:34:15 +01:00
Christian Georgi
3cbb199870 Recommend CodeTours extension 2021-02-19 12:55:35 +01:00
Daniel
b51a08bf4e fixed .registry 2021-02-19 12:40:26 +01:00
Iwona Hahn
b31efc8083 minor edits 2021-02-17 14:07:42 +01:00
Christian Georgi
6669b983b1 First code tour with an overview of the sample repo 2021-02-17 14:07:42 +01:00
Daniel
b6e5a2fced Removed demo 2021-02-17 12:32:27 +01:00
Daniel
6de09e0940 Make usage of @capire/common optional 2021-02-17 12:32:27 +01:00
Daniel
b6f3914d79 Minor cleanup to npm registry mock 2021-02-17 12:32:27 +01:00
Daniel
28402c58b3 Simplified redirects to imported apps 2021-02-17 12:32:27 +01:00
Daniel
77de0e445e demo -> done 2021-02-17 12:32:27 +01:00
Daniel
a037d92c97 demo for bhagat 2021-02-17 12:32:27 +01:00
Christian Georgi
b5031588ce Remove Node 10 for now 2021-02-09 17:15:24 +01:00
Christian Georgi
85319d9e8d Pin sqlite3 to 5.0.0
5.0.1 is broken at the moment
2021-02-09 17:15:24 +01:00
Iwona Hahn
39872200ae Update README.md 2021-02-01 14:54:55 +01:00
25 changed files with 229 additions and 2740 deletions

View File

@@ -16,7 +16,7 @@ jobs:
strategy:
matrix:
node-version: [10.x, 12.x, 14.x]
node-version: [12.x, 14.x]
steps:
- uses: actions/checkout@v2

1
.registry/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
*.tgz

View File

@@ -5,9 +5,9 @@ const app = express()
const { PORT=4444 } = process.env
const [,,port=PORT] = process.argv
process.chdir(__dirname)
app.use('/-/:tarball', (req,res,next) => {
const url = decodeURIComponent(req.url)
console.debug ('GET', req.params)
try {
const { tarball } = req.params

136
.tours/samples.tour Normal file
View File

@@ -0,0 +1,136 @@
{
"$schema": "https://aka.ms/codetour-schema",
"title": "CAP Samples",
"steps": [
{
"title": "Welcome",
"file": "README.md",
"description": "### Welcome to CAP Samples!\n\nThis tour leads you through a collection of samples for the [SAP Cloud Application Programming Model](https://cap.cloud.sap)\nYou will learn which features of the programming models are demonstrated in which sample.\n\nLet's start!",
"line": 2,
"selection": {
"start": {
"line": 1,
"character": 1
},
"end": {
"line": 3,
"character": 108
}
}
},
{
"file": "hello/world.cds",
"description": "### Hello World!\n\nThis is a simplistic [Hello World](https://cap.cloud.sap/docs/get-started/hello-world) service using [CDS](https://cap.cloud.sap/docs/cds/) and [cds.services](https://cap.cloud.sap/docs/node.js/api#services-api).",
"line": 2,
"selection": {
"start": {
"line": 1,
"character": 1
},
"end": {
"line": 4,
"character": 1
}
},
"title": "Hello World!"
},
{
"file": "bookshop/db/schema.cds",
"description": "### A Bookshop!\n\nIntroduces:\n- [Project Setup](https://cap.cloud.sap/docs/get-started/) and [Layouts](https://cap.cloud.sap/docs/get-started/projects)\n- [Domain Modeling](https://cap.cloud.sap/docs/guides/domain-models)\n- [Defining Services](https://cap.cloud.sap/docs/guides/providing-services)\n- [Generic Providers](https://cap.cloud.sap/docs/guides/generic-providers)\n- [Adding Custom Logic](https://cap.cloud.sap/docs/guides/service-impl)\n- [Using Databases](https://cap.cloud.sap/docs/guides/databases)\n",
"line": 1,
"selection": {
"start": {
"line": 1,
"character": 1
},
"end": {
"line": 32,
"character": 1
}
},
"title": "Bookshop"
},
{
"file": "common/index.cds",
"description": "### Extend and Reuse\n\nShowcases how to extend [@sap/cds/common](https://cap.cloud.sap/docs/cds/common) thereby covering:\n- Building [extension packages](https://cap.cloud.sap/docs/guides/domain-models#aspects-extensibility)\n- Providing [reuse packages](https://cap.cloud.sap/docs/get-started/projects#sharing-and-reusing-content)\n- [Verticalization](https://cap.cloud.sap/docs/cds/common#adapting-to-your-needs)\n- Using [Aspects](https://cap.cloud.sap/docs/cds/cdl#aspects)\n- Used in the [fiori app sample](#fiori)\n",
"line": 1,
"selection": {
"start": {
"line": 1,
"character": 1
},
"end": {
"line": 46,
"character": 1
}
},
"title": "Common"
},
{
"file": "orders/db/schema.cds",
"description": "### Compositions and Serving Documents\n\nA standalone orders management service, demonstrating:\n- Using [Compositions](https://cap.cloud.sap/docs/cds/cdl#compositions) in [Domain Models](https://cap.cloud.sap/docs/guides/domain-models), along with\n- [Serving deeply nested documents](https://cap.cloud.sap/docs/guides/generic-providers#serving-structured-data)\n",
"line": 1,
"selection": {
"start": {
"line": 1,
"character": 1
},
"end": {
"line": 27,
"character": 1
}
},
"title": "Orders"
},
{
"file": "reviews/db/schema.cds",
"description": "### More Modularity\n\nShows how to implement a modular service to manage product reviews, including:\n- Consuming other services synchronously and asynchronously\n- Serving requests synchronously\n- Emitting events asynchronously\n- Grow as you go, with:\n- Mocking app services\n- Running service meshes\n- Late-cut Micro Services\n- As well as managed data, input validations, and authorization\n",
"line": 1,
"selection": {
"start": {
"line": 1,
"character": 1
},
"end": {
"line": 39,
"character": 1
}
},
"title": "Reviews"
},
{
"file": "fiori/app/index.cds",
"description": "### Annotations for SAP Fiori Elements\n\nA [composite app, reusing and combining](https://cap.cloud.sap/docs/guides/verticalize) these packages:\n - [@capire/bookshop](bookshop)\n - [@capire/reviews](reviews)\n - [@capire/orders](orders)\n - [@capire/common](common)\n\n[Adds a SAP Fiori elements application](https://cap.cloud.sap/docs/guides/fiori/) to bookshop, thereby introducing to:\n - [OData Annotations](https://cap.cloud.sap/docs/guides/fiori#adding-odata-annotations) in `.cds` files\n - Support for [Fiori Draft](https://cap.cloud.sap/docs/guides/fiori#draft)\n - Support for [Value Helps](https://cap.cloud.sap/docs/guides/fiori#value-help)\n - Serving SAP Fiori apps locally\n\n[The Vue.js app](bookshop/app/vue) imported from bookshop is served as well.\n",
"line": 1,
"selection": {
"start": {
"line": 1,
"character": 1
},
"end": {
"line": 13,
"character": 1
}
},
"title": "Fiori"
},
{
"file": "package.json",
"description": "### All-in-one Monorepo\n\nEach sample sub directory essentially is a standard npm package, some with standard npm dependencies to other samples. The root folder's [package.json](package.json) has local links to the sub folders, such that an `npm install` populates a local `node_modules` folder acts like a local npm registry to the individual sample packages.\n",
"line": 8,
"selection": {
"start": {
"line": 8,
"character": 1
},
"end": {
"line": 15,
"character": 1
}
},
"title": "Packages"
}
],
"isPrimary": true,
"description": "Overview of CAP Samples for Node.js"
}

View File

@@ -4,14 +4,15 @@
// List of extensions which should be recommended for users of this workspace.
"recommendations": [
"SAPSE.vscode-cds",
"sapse.vscode-cds",
"dbaeumer.vscode-eslint",
"esbenp.prettier-vscode",
"mechatroner.rainbow-csv",
"humao.rest-client",
"alexcvzz.vscode-sqlite",
"hbenl.vscode-mocha-test-adapter",
"sdras.night-owl"
"sdras.night-owl",
"vsls-contrib.codetour"
],
// List of extensions recommended by VS Code that should not be recommended for users of this workspace.
"unwantedRecommendations": [

View File

@@ -3,7 +3,7 @@
Find here a collection of samples for the [SAP Cloud Application Programming Model](https://cap.cloud.sap) organized in a simplistic [monorepo setup](samples.md#all-in-one-monorepo). → See [**Overview** of contained samples](samples.md)
![](https://github.com/SAP-samples/cloud-cap-samples/workflows/CI/badge.svg)
[![REUSE status](https://api.reuse.software/badge/github.com/SAP-samples/cloud-cap-samples)](https://api.reuse.software/info/github.com/SAP-samples/cloud-cap-samples)
<!--[![REUSE status](https://api.reuse.software/badge/github.com/SAP-samples/cloud-cap-samples)](https://api.reuse.software/info/github.com/SAP-samples/cloud-cap-samples)-->
### Preliminaries

View File

@@ -36,7 +36,7 @@
<td class="rating-stars">
{{ ('★'.repeat(Math.round(book.rating))+'☆☆☆☆☆').slice(0,5) }}
</td>
<td>{{ book.currency.symbol }} {{ book.price }}</td>
<td>{{ book.currency && book.currency.symbol }} {{ book.price }}</td>
</tr>
</table>

View File

@@ -3,6 +3,6 @@
"description": "Provides a pre-built extension package for std @sap/cds/common",
"version": "1.0.0",
"dependencies": {
"@sap/cds": "latest"
"@sap/cds": "*"
}
}

View File

@@ -1,3 +1,3 @@
<head>
<meta http-equiv="refresh" content="0;url=vue/bookshop/index.html">
<meta http-equiv="refresh" content="0;url=bookshop/index.html">
</head>

View File

@@ -1,3 +1,3 @@
<head>
<meta http-equiv="refresh" content="0;url=vue/reviews/index.html">
<meta http-equiv="refresh" content="0;url=reviews/index.html">
</head>

View File

@@ -1,19 +1,18 @@
const express = require ('express')
const cds = require ('@sap/cds')
cds.once('bootstrap',(app)=>{
const {dirname} = require ('path')
// serving the orders app imported from @capire/orders
const orders_app = dirname (require.resolve('@capire/orders/app/orders/webapp/manifest.json'))
app.use ('/orders/webapp', express.static(orders_app))
// serving the vue.js app imported from @capire/bookshop
const bookshop_app = dirname (require.resolve('@capire/bookshop/app/vue/index.html'))
app.use ('/vue/bookshop', express.static(bookshop_app))
// serving the vue.js app imported from @capire/reviews
const reviews_app = dirname (require.resolve('@capire/reviews/app/vue/index.html'))
app.use ('/vue/reviews', express.static(reviews_app))
app.use ('/orders/webapp', _from('@capire/orders/app/orders/webapp/manifest.json'))
app.use ('/bookshop', _from('@capire/bookshop/app/vue/index.html'))
app.use ('/reviews', _from('@capire/reviews/app/vue/index.html'))
})
cds.once('served', require('./srv/mashup'))
module.exports = cds.server
// -----------------------------------------------------------------------
// Helper for serving static content from npm-installed packages
const {static} = require('express')
const {dirname} = require('path')
const _from = target => static (dirname (require.resolve(target)))

View File

@@ -0,0 +1,14 @@
const cds = require ('./sap-cds')
module.exports = class extends cds.build.Task {
async build ({src='*'}) {
this.log (`Generating edmx output for '${src}'...`)
const csn = await this.model(src)
return Promise.all (csn.services.map (({name:service}) => {
const edmx = cds.compile(csn).to.edmx({service})
return this.write(edmx).to(`{srv}/src/main/resources/${service}.edmx`)
}))
}
}

View File

@@ -0,0 +1,51 @@
const cds = require ('@sap/cds/lib')
const path = require('path')
const cwd = process.cwd()
const _resolve = (root,file) => path.resolve (cwd, root, file.replace(/{(app|db|srv)}\/?/g, (_,folder) => cds.env.folders[folder]))
const _local = (file) => path.relative (cwd,file)
class BuildTask {
async build (options) {}
async clean (options) {}
async model(src='*') {
return cds.linked (await cds.load(src))
}
log(...args) { return console.log(...args) }
warn(...args) { return console.warn(...args) }
error(...args) { return console.error(...args) }
write(x) {
if (typeof x === 'object') x = JSON.stringify(x,null,' ')
return { to: async (dst)=>{
const file = _resolve (this.options.dest, dst)
await cds.utils.mkdirp (path.dirname (file))
await cds.utils.promises.writeFile (file,x)
console.log ('> wrote:', _local(file))
return file
}}
}
copy(x) {
return { to: async (dst) => {} }
}
}
module.exports = Object.assign (cds, {
build: {
run (tasks, _options) {
const options = { dest:'gen', ..._options }
return Promise.all(tasks.map (async each => {
const task = Object.assign (new each, {options})
await task.build (options)
}))
},
Task: BuildTask
}
})

5
fiori/test/build/test.js Normal file
View File

@@ -0,0 +1,5 @@
const cds = require ('./sap-cds')
const task = require('./build-task')
cds.build.run ([task], {src:process.argv[2]})
.catch(console.error)

View File

@@ -1 +0,0 @@
@sap:registry=http://nexus.wdf.sap.corp:8081/nexus/repository/build.milestones.npm/

View File

@@ -1,15 +0,0 @@
{
// See https://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations.
// Extension identifier format: ${publisher}.${name}. Example: vscode.csharp
// List of extensions which should be recommended for users of this workspace.
"recommendations": [
"SirTobi.pegjs-language",
"tamuratak.vscode-pegjs",
"joeandaverde.vscode-pegjs-live"
],
// List of extensions recommended by VS Code that should not be recommended for users of this workspace.
"unwantedRecommendations": [
]
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,26 +0,0 @@
const fs = require("fs");
const path = require("path");
const peg = require("pegjs");
const pegGrammarPath = path.join(__dirname, "/odata2cqn.pegjs");
const odataPegGrammar = fs.readFileSync(pegGrammarPath, {
encoding: "utf8",
flag: "r",
});
const parser = peg.generate(odataPegGrammar);
module.exports = {
parse: {
url: parser.parse,
},
to: {
cqn: parser.parse,
url: (cqn) => pending(cqn)
},
serialize: (data) => pending(data),
deserialize: (body) => pending(body),
}
const pending = ()=>{
throw new Error ('Not yet implemented')
}

View File

@@ -1,200 +0,0 @@
/** ------------------------------------------
* This is a peg.js adaptation of the https://github.com/oasis-tcs/odata-abnf/blob/master/abnf/odata-abnf-construction-rules.txt
* which directly constructs CQN out of parsed sources.
*
* NOTE:
* In contrast to the OData ABNF source, which uses very detailedsemantic rules,
* this adaptation uses rather generic syntactic rules only, e.g. not distinguishing
* betwenn Collection Navigation or not knowing individual function names.
* This is to be open to future enhancements of the OData standard, as well as
* to improve error messages. For example a typo in a function name could be
* reported specifically instead of throwing a generic parser error.
*
* See also: https://docs.microsoft.com/en-us/odata/concepts/queryoptions-overview
* Future test cases http://docs.oasis-open.org/odata/odata/v4.0/errata03/os/complete/abnf/odata-abnf-testcases.xml
*
* Limitations: Type, Geo functions are not supported,
* maxdatetime, mindatetime, fractionalseconds,
* totaloffsetminutes, date, totalseconds,
* floor, ceiling also are not supported by CAP
*
* Examples:
* Books
* Books/201
* Books?$select=ID,title&$expand=author($select=name)&$filter=stock gt 1&$orderby=title
*/
// ---------- JavaScript Helpers -------------
{
const stack=[]
let SELECT
}
// ---------- Entity Paths ---------------
ODataRelativeURI // Note: case-sensitive!
= (p:path { SELECT = { from:p } })
( o"?"o QueryOption ( o'&'o QueryOption )* )? {
if (SELECT.expand) {
SELECT.columns = SELECT.expand
delete SELECT.expand
}
return { SELECT }
}
path
= crv:$("$count"/"$ref"/"$value") {return {ref:[crv]}}
/ head:identifier filter:(OPEN args CLOSE)? tail:( '/' p:path {return p} )? {
const ref = [ filter ? { id:head, where:filter[1] } : head ]
if (tail) ref.push (...tail.ref)
return {ref}
}
args
= val:( number / integer / string ) {return [{val}]}
/ ref:identifier o"="o val:( number / integer / string ) more:( COMMA args )? {
const args = [ {ref}, '=', {val} ]
if (more) args.push ('and', ...more[1])
return args
}
ref "a reference"
= head:identifier tail:( '/' identifier )* {
return { ref:[ head, ...tail ] }
}
//
// ---------- Query Options ------------
QueryOption = ExpandOption
ExpandOption =
"$select=" o select ( COMMA select )* /
"$expand=" o expand ( COMMA expand )* /
"$filter=" o filter /
"$orderby=" o orderby /
"$top=" o top /
"$skip=" o skip /
"$search=" o search /
"$count=" o count
select
= col:ref {
(SELECT.expand || (SELECT.expand = [])).push(col)
return col
}
expand =
( c:select {c.expand='*'} )
( // --- nested query options, if any
(OPEN {
stack.push (SELECT)
SELECT = SELECT.expand[SELECT.expand.length-1]
SELECT.expand = []
})
ExpandOption ( o";"o ExpandOption )*
(CLOSE {
SELECT = stack.pop()
})
)? // --- end of nested query options
( COMMA expand )?
top
= val:integer {
(SELECT.limit || (SELECT.limit={})).rows = {val}
}
skip
= val:integer {
(SELECT.limit || (SELECT.limit={})).offset = {val}
}
search
= p:search_clause {SELECT.search = p}
search_clause = p:( n:NOT? {return n?[n]:[]} )(
OPEN xpr:search_clause CLOSE {p.push({xpr})}
/ val:(identifier/string) {p.push({val})}
)( ao:(AND/OR) more:search_clause {p.push(ao,...more)} )*
{return p}
filter
= p:where_clause {SELECT.where = p}
where_clause = p:( n:NOT? {return n?[n]:[]} )(
OPEN xpr:where_clause CLOSE {p.push({xpr})}
/ comp:comparison {p.push(...comp)}
/ func:boolish {p.push(func)}
)( ao:(AND/OR) more:where_clause {p.push(ao,...more)} )*
{return p}
orderby
= ref:ref sort:( _ s:$("asc"/"desc") {return s})? {
SELECT.orderby = $(ref, sort && {sort})
}
count
= c:$[^,?&()]+ { SELECT.count = true }
//
// ---------- Expressions ------------
comparison "a comparison"
= a:operand _ o:$("eq"/"ne"/"lt"/"gt"/"le"/"ge") _ b:operand {
const op = { eq:'=', ne:'!=', lt:'<', gt:'>', le:'<=', ge:'>=' }[o]||o
return [ a, op, b ]
}
operand "an operand"
= val:number {return Number.isSafeInteger(val) ? {val} : { val:String(val), literal:'number' }}
/ val:string {return {val}}
/ function
/ ref
function "a function call"
= func:$[a-z]+ OPEN a:operand more:( COMMA o:operand {return o} )* CLOSE
{ return { func, args:[a,...more] }}
boolish "a boolean function"
= func:("contains"/"endswith"/"startswith") OPEN a:operand COMMA b:operand CLOSE
{ return { func, args:[a,b] }}
NOT = o "not"i _ {return 'not'}
AND = _ "and"i _ {return 'and'}
OR = _ "or"i _ {return 'or'}
//
// ---------- Literals -----------
string "Edm.String"
= "'" s:$("''"/[^'])* "'"
{return s.replace(/''/g,"'")}
number
= x:$( [+-]? [0-9]+ ("."[0-9]+)? ("e"[0-9]+)? )
{return Number(x)}
integer
= x:$( [+-]? [0-9]+ )
{return parseInt(x)}
identifier
= $([a-zA-Z][_a-zA-Z0-9]*)
//
// ---------- Punctuation ----------
COLON = o":"o
COMMA = o","o
SEMI = o";"o
OPEN = o"("o
CLOSE = o")"
//
// ---------- Whitespaces -----------
o "optional whitespaces" = $[ \t\n]*
_ "mandatory whitespaces" = $[ \t\n]+
//
// ------------------------------------

View File

@@ -1,15 +0,0 @@
{
"name": "@sap/cds-odata",
"version": "1.0.0",
"dependencies": {
"@sap/cds-compiler": "latest",
"@sap/cds": "^4.4.10"
},
"scripts": {
"postinstall": "rm -fr node_modules/@sap/cds/node_modules"
},
"devDependencies": {
"pegjs": "^0.10.0"
},
"private": true
}

View File

@@ -1,105 +0,0 @@
const cds = require("@sap/cds/lib"), {expect} = cds.test
cds.odata = require("../lib")
describe("$filter", () => {
describe("comparing expressions", () => {
const types = {
strings: "'some string'",
integers: 11,
decimals: 0.99,
// ...
}
it.each(Object.keys(types))("should support expressions with %s", (t) => {
expect (cds.odata.parse.url(`Foo?$filter=bar eq ${types[t]}`))
.to.eql (cds.parse.cql(`SELECT from Foo where bar = ${types[t]}`))
})
const operators = {
eq: '=',
lt: '<',
le: '<=',
gt: '>',
ge: '>=',
ne: '!=',
// ...
}
it.each(Object.keys(operators))("should support comparison operator '%s'", (op) => {
expect (cds.odata.parse.url(`Foo?$filter=bar ${op} 11`))
.to.eql (cds.parse.cql(`SELECT from Foo where bar ${operators[op]} 11`))
})
});
describe("logical expressions", () => {
it.each(['and','or'])("should support '%s'", (t) => {
expect (cds.odata.parse.url(`Foo?$filter=bar lt 11 ${t} name eq 'some name'`))
.to.eql (cds.parse.cql(`SELECT from Foo where bar < 11 ${t} name = 'some name'`))
})
it("should support 'not'", () => {
// REVISIT: We need to check with the Node.js team why they translated that to the equivalent of:
// not name like concat('%','sunny','%') escape '^'
expect (cds.odata.parse.url(`Foo?$filter= not contains(name,'sunny')`))
.to.eql (cds.parse.cql(`SELECT from Foo where not contains(name,'sunny')`))
});
// REVISIT: wait for compiler v2
it("should support group expr", () => {
expect (cds.odata.parse.url(`Foo?$filter= (unitPrice gt 11 and length(name) eq 12) or name eq 'Restless and Wild'`))
.to.eql (cds.parse.cql(`SELECT from Foo where (unitPrice > 11 and length(name) = 12) or name = 'Restless and Wild'`))
});
});
describe("function expressions", () => {
it("should support contains", () => {
expect (cds.odata.parse.url(`Foo?$filter= contains(name,'sunny')`))
.to.eql (cds.parse.cql(`SELECT from Foo where contains(name,'sunny')`))
});
it("should support startswith", () => {
expect (cds.odata.parse.url(`Foo?$filter= startswith(name,'sunny')`))
.to.eql (cds.parse.cql(`SELECT from Foo where startswith(name,'sunny')`))
});
it("should support endswith", () => {
expect (cds.odata.parse.url(`Foo?$filter= endswith(name,'sunny')`))
.to.eql (cds.parse.cql(`SELECT from Foo where endswith(name,'sunny')`))
});
it("should support length", () => {
expect (cds.odata.parse.url(`Foo?$filter= length(name) lt 11`))
.to.eql (cds.parse.cql(`SELECT from Foo where length(name) < 11`))
});
it("should support indexof", () => {
expect (cds.odata.parse.url(`Foo?$filter= indexof(name,'x') eq 11`))
.to.eql (cds.parse.cql(`SELECT from Foo where indexof(name,'x') = 11`))
});
it("should support substring", () => {
expect (cds.odata.parse.url(`Foo?$filter= substring(name,1) eq 'foo'`))
.to.eql (cds.parse.cql(`SELECT from Foo where substring(name,1) = 'foo'`))
});
it.each(['tolower','toupper','trim'])("should support '%s'", (fn) => {
expect (cds.odata.parse.url(`Foo?$filter= ${fn}(name) eq 'foo'`))
.to.eql (cds.parse.cql(`SELECT from Foo where ${fn}(name) = 'foo'`))
});
it("should support 'day'", () => {
expect (cds.odata.parse.url(`Foo?$filter= day(name) eq 11`))
.to.eql (cds.parse.cql(`SELECT from Foo where day(name) = 11`))
});
it("should support concat", () => {
expect (cds.odata.parse.url(`Foo?$filter= concat(name,'o') eq 'foo'`))
.to.eql (cds.parse.cql(`SELECT from Foo where concat(name,'o') = 'foo'`))
});
});
});

View File

@@ -21,6 +21,3 @@ entity Orders_Items {
entity Products @(cds.persistence.skip:'always') {
key ID : String;
}
// Activate extension package
using from '@capire/common';

View File

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

View File

@@ -14,14 +14,13 @@
"@capire/reviews": "./reviews"
},
"devDependencies": {
"pegjs": "^0.10.0",
"chai": "^4.2.0",
"chai-as-promised": "^7.1.1",
"chai-subset": "^1.6.0",
"sqlite3": "^5"
"sqlite3": "5.0.0"
},
"scripts": {
"registry": "cd .registry && node server.js",
"registry": "node .registry/server.js",
"bookshop": "cds watch bookshop",
"fiori": "cds watch fiori",
"media": "cds watch media",