Compare commits

..

4 Commits

Author SHA1 Message Date
Daniel Hutzel
7ccd11062a Moved ord-service into plugin package 2025-02-21 18:19:54 +01:00
Daniel Hutzel
20348e0776 . 2025-02-21 14:39:47 +01:00
Daniel Hutzel
6acd5338d9 Adding arbitrary express routes 2025-02-21 14:37:26 +01:00
Daniel Hutzel
413d4de745 some samples how to serve things 2025-02-21 12:57:05 +01:00
54 changed files with 3560 additions and 6713 deletions

View File

@@ -24,6 +24,6 @@ Disclaimer: The code in this project may include calls to APIs (“API Calls”)
you any rights to use or access any SAP External Product, or provide any third
parties the right to use of access any SAP External Product, through API Calls.
Files: *.*
Copyright: 2019-2025 SAP SE or an SAP affiliate company and cap-cloud-samples
Files: *
Copyright: 2019-2020 SAP SE or an SAP affiliate company and cap-cloud-samples
License: Apache-2.0

View File

@@ -1,5 +1,3 @@
# Welcome to cap/samples
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).
@@ -9,12 +7,20 @@ Find here a collection of samples for the [SAP Cloud Application Programming Mod
![](etc/samples.drawio.svg)
![](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)
### Preliminaries
Ensure you have the latest LTS version of Node.js, [`@sap/cds-dk`](https://www.npmjs.com/package/@sap/cds-dk) installed globally, `git` and your IDE ready (see [Initial Setup](https://cap.cloud.sap/docs/get-started/#setup))
1. Ensure you have the latest LTS version of Node.js installed (see [Getting Started](https://cap.cloud.sap/docs/get-started/jumpstart))
2. Install [**@sap/cds-dk**](https://cap.cloud.sap/docs/get-started/) globally:
```sh
npm i -g @sap/cds-dk
```
3. _Optional:_ [Use Visual Studio Code](https://cap.cloud.sap/docs/get-started/tools#vscode)
### Download

View File

@@ -1 +0,0 @@
../../bookshop/app/vue

View File

@@ -1 +0,0 @@
../../orders/app/orders

File diff suppressed because it is too large Load Diff

View File

@@ -1,12 +0,0 @@
{
"name": "approuter",
"dependencies": {
"@sap/approuter": "^19.0.0"
},
"engines": {
"node": "^20"
},
"scripts": {
"start": "node node_modules/@sap/approuter/approuter.js"
}
}

View File

@@ -1 +0,0 @@
../../reviews/app/vue

View File

@@ -1,52 +0,0 @@
{
"welcomeFile": "app/bookshop/index.html",
"routes": [
{
"source": "^/app/(.*)$",
"target": "$1",
"localDir": ".",
"cacheControl": "no-cache, no-store, must-revalidate"
},
{
"source": "^/appconfig/",
"localDir": ".",
"cacheControl": "no-cache, no-store, must-revalidate"
},
{
"source": "^/admin/(.*)$",
"target": "/admin/$1",
"destination": "bookstore-api",
"csrfProtection": true
},
{
"source": "^/browse/(.*)$",
"target": "/browse/$1",
"destination": "bookstore-api",
"csrfProtection": true
},
{
"source": "^/user/(.*)$",
"target": "/user/$1",
"destination": "bookstore-api",
"csrfProtection": true
},
{
"source": "^/odata/v4/orders/(.*)$",
"target": "/odata/v4/orders/$1",
"destination": "orders-api",
"csrfProtection": true
},
{
"source": "^/reviews/(.*)$",
"target": "/reviews/$1",
"destination": "reviews-api",
"csrfProtection": true
},
{
"source": "^(.*)$",
"target": "$1",
"localDir": ".",
"cacheControl": "no-cache, no-store, must-revalidate"
}
]
}

View File

@@ -13,6 +13,7 @@
"@cap-js/sqlite": "*"
},
"dependencies": {
"@cap-js/ord": "2",
"@sap/cds": ">=7",
"express": "^4.17.1"
},

View File

@@ -2,21 +2,14 @@
"name": "@capire/bookstore",
"version": "1.0.0",
"dependencies": {
"@cap-js/hana": "^1.7.0",
"@capire/bookshop": "*",
"@capire/reviews": "*",
"@capire/orders": "*",
"@capire/common": "*",
"@capire/data-viewer": "*",
"@capire/orders": "*",
"@capire/reviews": "*",
"@sap-cloud-sdk/http-client": "^3.26.4",
"@sap-cloud-sdk/resilience": "^3.26.4",
"@sap/cds": ">=5",
"@sap/xssec": "^4.4.0",
"express": "^4.17.1"
},
"scripts": {
"start": "cds-serve"
},
"cds": {
"requires": {
"ReviewsService": {
@@ -27,13 +20,15 @@
"kind": "odata",
"model": "@capire/orders"
},
"messaging": true,
"messaging": {
"[development]": { "kind": "file-based-messaging" },
"[hybrid]": { "kind": "enterprise-messaging-shared" },
"[production]": { "kind": "enterprise-messaging" }
},
"db": {
"kind": "sql"
}
},
"log": {
"service": true
}
"log": { "service": true }
}
}
}

View File

@@ -29,7 +29,7 @@ module.exports = async()=>{ // called by server.js
CatalogService.on ('OrderedBook', async (msg) => {
const { book, quantity, buyer } = msg.data
const { title, price } = await db.read (Books, book, b => { b.title, b.price })
return OrdersService.create ('OrdersNoDraft').entries({
return OrdersService.create ('Orders').entries({
OrderNo: 'Order at '+ (new Date).toLocaleString(),
Items: [{ product:{ID:`${book}`}, title, price, quantity }],
buyer, createdBy: buyer

View File

@@ -34,8 +34,7 @@ class DataService extends cds.ApplicationService { init(){
query.SELECT.limit = req.query.SELECT.limit // forward $skip / $top
const dataSource = findDataSource(dataSourceName, entityName)
let res = await dataSource.run(query)
if (!Array.isArray(res)) res = [res] // singleton result
const res = await dataSource.run(query)
return res.map((line) => {
const record = Object.entries(line).map(([column, data]) => ({ column, data }))
return {

View File

@@ -1,29 +0,0 @@
{
"version": "1.1.0",
"options": {
"management": true,
"messagingrest": true,
"messaging": true
},
"rules": {
"topicRules": {
"publishFilter": [
"*"
],
"subscribeFilter": [
"*"
]
},
"queueRules": {
"publishFilter": [
"*"
],
"subscribeFilter": [
"*"
]
}
},
"authorities": [
"$ACCEPT_GRANTED_AUTHORITIES"
]
}

15
hello/README.md Normal file
View File

@@ -0,0 +1,15 @@
# Hello World Getting Started Sample
## Next Steps
- To run the JavaScript implementation, open a new terminal and run `cds watch`.
- To run the TypeScript implementation, open a new terminal and run `cds-ts watch`.
Then call the service at: http://localhost:4004/say/hello(to='world')
## Learn More
Learn more about:
- [Hello World!](https://cap.cloud.sap/docs/get-started/hello-world)
- [Using TypeScript](https://cap.cloud.sap/docs/node.js/typescript)

12
hello/package.json Normal file
View File

@@ -0,0 +1,12 @@
{
"name": "@capire/hello-world",
"version": "1.0.0",
"scripts": {
"test": "npx jest --silent",
"start": "cds-serve srv/world.cds",
"start:ts": "cds-ts serve srv/world.cds"
},
"dependencies": {
"@sap/cds": ">=5.0.4"
}
}

3
hello/srv/world.cds Normal file
View File

@@ -0,0 +1,3 @@
service say {
function hello (to:String) returns String;
}

7
hello/srv/world.js Normal file
View File

@@ -0,0 +1,7 @@
module.exports = class say {
hello(req) {
let {to} = req.data
if (to === 'me') to = require('os').userInfo().username
return `Hello ${to}!`
}
}

7
hello/srv/world.ts Normal file
View File

@@ -0,0 +1,7 @@
import { Request } from "@sap/cds"
module.exports = class say {
hello(req: Request) {
return `Hello ${req.data.to} from a TypeScript file!`
}
}

5
hello/test/test.http Normal file
View File

@@ -0,0 +1,5 @@
GET http://localhost:4004/odata/v4/say/hello
###
GET http://localhost:4004/odata/v4/say/hello(to='me')
###

13
media/db/data-model.cds Normal file
View File

@@ -0,0 +1,13 @@
namespace sap.capire.media;
entity Media {
key id:Integer;
@Core.MediaType: mediaType
content : LargeBinary ;
@Core.IsMediaType: true
mediaType : String;
fileName : String;
applicationName:String;
}

2
media/index.cds Normal file
View File

@@ -0,0 +1,2 @@
using from './db/data-model';
using from './srv/media-service';

19
media/package.json Normal file
View File

@@ -0,0 +1,19 @@
{
"name": "@capire/media",
"version": "1.0.0",
"dependencies": {
"lokijs": "^1.5.6"
},
"files": [
"db",
"srv",
"index.cds"
],
"cds": {
"requires": {
"db": {
"kind": "sql"
}
}
}
}

View File

@@ -0,0 +1,9 @@
using { sap.capire.media as db } from '../db/data-model';
namespace sap.capire.media;
@path: '/media-server'
service MediaServer {
entity Media as projection on db.Media ;
}

View File

@@ -0,0 +1,68 @@
const loki = require('lokijs')
const db = new loki('DB')
const mediaDB = db.addCollection('Media')
const { Readable, PassThrough } = require('stream')
module.exports = srv => {
srv.before('CREATE', 'Media', req => {
const obj = mediaDB.insert({ media: '' })
req.data.id = obj.$loki
})
srv.on('UPDATE', 'Media', (req, next) => {
const url = req.path
if (url.includes('content')) {
const id = req.data.id
const obj = mediaDB.get(id)
if (!obj) {
req.reject(404, 'No record found for the ID')
return
}
const stream = new PassThrough()
const chunks = []
stream.on('data', chunk => {
chunks.push(chunk)
})
stream.on('end', () => {
obj.media = Buffer.concat(chunks).toString('base64')
mediaDB.update(obj)
})
req.data.content.pipe(stream)
} else return next()
})
srv.on('READ', 'Media', (req, next) => {
const url = req.path
if (url.includes('content')) {
const id = req.data.id
const mediaObj = mediaDB.get(id)
if (!mediaObj) {
req.reject(404, 'Media not found for the ID')
return
}
const decodedMedia = Buffer.from(
mediaObj.media.split(';base64,').pop(),
'base64'
)
return _formatResult(decodedMedia)
} else return next() //> delegate to next/default handlers
})
srv.on('DELETE', 'Media', (req, next) => {
const id = req.data.id
mediaDB
.chain()
.find({ $loki: id })
.remove()
return next() //> delegate to next/default handlers
})
function _formatResult (decodedMedia) {
const readable = new Readable()
const result = new Array()
readable.push(decodedMedia)
readable.push(null)
result.push({ value: readable })
return result
}
}

BIN
media/test/Test.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

35
media/test/media.http Normal file
View File

@@ -0,0 +1,35 @@
### Requires REST Client for VS Code
### https://marketplace.visualstudio.com/items?itemName=humao.rest-client
###
@protocol = http
@host = localhost
@port = 4004
### Read Pictures
GET {{protocol}}://{{host}}:{{port}}/media-server/Media
Authorization: Basic admin:
### Create Picture with mediatype
POST {{protocol}}://{{host}}:{{port}}/media-server/Media
Authorization: Basic admin:
Accept: application/json
Content-Type: application/json
{
"id": 1,
"mediaType": "image/png"
}
### Upload Binary PNG
PUT {{protocol}}://{{host}}:{{port}}/media-server/Media(1)/content
Authorization: Basic admin:
Content-Type: image/png
< ./Test.png
### Read Binary
GET {{protocol}}://{{host}}:{{port}}/media-server/Media(1)/content
Authorization: Basic admin:
### Delete Image
DELETE {{protocol}}://{{host}}:{{port}}/media-server/Media(1)
Authorization: Basic admin:

177
mta.yaml
View File

@@ -1,177 +0,0 @@
_schema-version: 3.3.0
ID: capire.samples
version: 3.0.0
description: "A monorepo with several samples for CAP."
parameters:
enable-parallel-deployments: true
build-parameters:
before-all:
- builder: custom
commands:
- npm ci
- npx cds build ./shared-db --for hana --production
- npx cds build ./orders --for nodejs --production --ws-pack
- npx cds build ./reviews --for nodejs --production
- npx cds build ./bookstore --for nodejs --production --ws-pack
modules:
- name: bookstore-srv
type: nodejs
path: bookstore/gen/srv
parameters:
instances: 1
buildpack: nodejs_buildpack
build-parameters:
builder: npm
properties:
cds_requires_ReviewsService_credentials: {"destination": "reviews-dest","path": "/reviews"}
cds_requires_OrdersService_credentials: {"destination": "orders-dest","path": "/odata/v4/orders"}
provides:
- name: bookstore-api # required by consumers of CAP services (e.g. approuter)
properties:
srv-url: ${default-url}
requires:
- name: samples-db
- name: samples-auth
- name: samples-messaging
- name: samples-destination
- name: orders-srv
type: nodejs
path: orders/gen/srv
parameters:
instances: 1
buildpack: nodejs_buildpack
build-parameters:
builder: npm
provides:
- name: orders-api # required by consumers of CAP services (e.g. approuter)
properties:
srv-url: ${default-url}
requires:
- name: samples-db
- name: samples-auth
- name: samples-messaging
- name: samples-destination
- name: reviews-srv
type: nodejs
path: reviews/gen/srv
parameters:
instances: 1
buildpack: nodejs_buildpack
build-parameters:
builder: npm
provides:
- name: reviews-api # required by consumers of CAP services (e.g. approuter)
properties:
srv-url: ${default-url}
requires:
- name: samples-db
- name: samples-auth
- name: samples-messaging
- name: samples-destination
- name: samples-db-deployer
type: hdb
path: shared-db/gen/db
parameters:
buildpack: nodejs_buildpack
requires:
- name: samples-db
- name: samples
type: approuter.nodejs
path: app/router
parameters:
keep-existing-routes: true
disk-quota: 256M
memory: 256M
requires:
- name: orders-api
group: destinations
properties:
name: orders-api
url: ~{srv-url}
forwardAuthToken: true
- name: reviews-api
group: destinations
properties:
name: reviews-api
url: ~{srv-url}
forwardAuthToken: true
- name: bookstore-api
group: destinations
properties:
name: bookstore-api
url: ~{srv-url}
forwardAuthToken: true
- name: samples-auth
- name: samples-destination
provides:
- name: app-api
properties:
app-protocol: ${protocol}
app-uri: ${default-uri}
- name: destination-content
type: com.sap.application.content
requires:
- name: orders-api
- name: reviews-api
- name: bookstore-api
- name: samples-auth
parameters:
service-key:
name: xsuaa_service-key
- name: samples-destination
parameters:
content-target: true
build-parameters:
no-source: true
parameters:
content:
instance:
existing_destinations_policy: update
destinations:
- Name: orders-dest
URL: ~{orders-api/srv-url}
Authentication: OAuth2ClientCredentials
TokenServiceInstanceName: samples-auth
TokenServiceKeyName: xsuaa_service-key
- Name: reviews-dest
URL: ~{reviews-api/srv-url}
Authentication: OAuth2ClientCredentials
TokenServiceInstanceName: samples-auth
TokenServiceKeyName: xsuaa_service-key
resources:
- name: samples-db
type: com.sap.xs.hdi-container
parameters:
service: hana
service-plan: hdi-shared
- name: samples-auth
type: org.cloudfoundry.managed-service
processed-after:
- samples-messaging
parameters:
service: xsuaa
service-plan: application
path: ./xs-security.json
config:
xsappname: samples-${org}-${space}
tenant-mode: dedicated
- name: samples-messaging
type: org.cloudfoundry.managed-service
parameters:
service: enterprise-messaging
service-plan: default
path: ./event-mesh.json
config:
emname: bookstore-${org}-${space}
namespace: cap/samples/${space}
- name: samples-destination
type: org.cloudfoundry.managed-service
parameters:
service: destination
service-plan: lite

1
ord/cds-plugin.js Normal file
View File

@@ -0,0 +1 @@
// just a dummy tag file to be identified as a plugin

12
ord/package.json Normal file
View File

@@ -0,0 +1,12 @@
{
"name": "@cap-js/ord",
"version": "2.0.0",
"cds": {
"requires": {
"SAP ORD Service": {
"model": "@cap-js/ord/srv/ord-service",
"service": "OrdService"
}
}
}
}

12
ord/srv/ord-service.cds Normal file
View File

@@ -0,0 +1,12 @@
// @requires: 'ORDconsumer'
@rest @path:'/ord/v1'
service OrdService {
@readonly entity documents {
key id: String;
}
@readonly entity csn {
key id: String;
}
function api (service: String, format: String) returns {};
}

73
ord/srv/ord-service.mjs Normal file
View File

@@ -0,0 +1,73 @@
import cds from '@sap/cds'
export class OrdService extends cds.ApplicationService {
init(){
this.on('READ','documents', req => {
let csn = cds.context?.model || cds.model
return { ord: csn }
})
/**
* Just an example to do something with id, if given.
* Try it out with URLs like that:
*
* - http://localhost:4004/ord/v1/documents
* - http://localhost:4004/ord/v1/documents/CatalogService
* - http://localhost:4004/ord/v1/documents/CatalogService.Books
* - http://localhost:4004/ord/v1/documents/CatalogService.Authors
*/
this.on('READ','csn', req => {
let csn = cds.context?.model || cds.model
let { id } = req.data
if (id) csn = csn.definitions[id] || 'not in model!'
return { id, csn }
})
/**
* Just an example to serve arbitrary content with a function.
* Try it out with URLs like that:
*
* - http://localhost:4004/ord/v1/api?service=CatalogService
* - http://localhost:4004/ord/v1/api?service=CatalogService&format=edmx
* - http://localhost:4004/ord/v1/api?service=CatalogService&format=edmx-v2
* - http://localhost:4004/ord/v1/api?service=CatalogService&format=openapi
*/
this.on('api', req => {
let csn = cds.context?.model || cds.model
let { service, format = 'csn' } = req.data
let { res } = req.http
if (format === 'csn') {
if (!service) return res.send(csn)
service = csn.services[service]
return res.send({ definitions: [ service, ...service.entities ] .reduce ((all,e) => {
let d = all[e.name] = {...e}
delete d.projection // not part of the API
delete d.query // not part of the API
return all
},{})})
}
let api = cds.compile(csn).to[format]({service})
return res.send(api)
})
/**
* Example how to register arbitrary express routes,
* and map them to our service's interface.
* Try it out with URLs like that:
*
* - http://localhost:4004/ord/v1/csn/CatalogService
* - http://localhost:4004/ord/v1/edmx/CatalogService
* - http://localhost:4004/ord/v1/openapi/CatalogService
* - http://localhost:4004/ord/v1/asyncapi/CatalogService
*
* NOTE: we add cds.middlewares.before to the route, which gives us all
* the context and auth handling, which is also available to CAP services.
*/
cds.app.get (`${this.path}/:api?/:service?`, cds.middlewares.before, req => {
const { api, service } = req.params
return this.api (service, api)
})
return super.init()
}
}

View File

@@ -2,18 +2,7 @@
"name": "@capire/orders",
"version": "1.0.0",
"dependencies": {
"@cap-js/hana": "^1.7.0",
"@capire/common": "*",
"@sap/cds": ">=5",
"@sap/xssec": "^4.4.0"
},
"scripts": {
"start": "cds-serve"
},
"cds": {
"requires": {
"messaging": true,
"db": true
}
"@sap/cds": ">=5"
}
}
}

View File

@@ -2,14 +2,4 @@ using { sap.capire.orders as my } from '../db/schema';
service OrdersService {
entity Orders as projection on my.Orders;
event OrderChanged {
product: String;
deltaQuantity: Integer;
}
@odata.draft.bypass
@(requires: 'system-user')
entity OrdersNoDraft as projection on my.Orders;
}

7045
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,42 +1,46 @@
{
"name": "@capire/samples",
"version": "3.0.0",
"version": "2.1.0",
"description": "A monorepo with several samples for CAP.",
"repository": "https://github.com/sap-samples/cloud-cap-samples.git",
"author": "daniel.hutzel@sap.com",
"dependencies": {
"@sap/cds": ">=8",
"@cap-js/hana": "^1",
"@sap/xssec": "^4"
"@sap/cds": ">=8"
},
"workspaces": [
"./bookshop",
"./bookstore",
"./common",
"./data-viewer",
"./fiori",
"./hello",
"./media",
"./ord",
"./orders",
"./reviews",
"./etc/data-viewer",
"./etc/loggers"
"./loggers",
"./reviews"
],
"devDependencies": {
"@cap-js/cds-test": "^0",
"@cap-js/cds-types": "^0",
"@cap-js/sqlite": "^1",
"@sap/cds-dk": "^8",
"axios": "^1",
"chai": "^4.3.4",
"chai-as-promised": "^7.1.1",
"chai-subset": "^1.6.0",
"eslint": "^9",
"semver": "^7"
},
"scripts": {
"build": "mbt build -t gen --mtar mta.tar",
"deploy": "cf deploy gen/mta.tar",
"undeploy": "cf undeploy capire.samples --delete-services --delete-service-keys",
"bookshop": "cds watch bookshop",
"start": "cds watch fiori",
"fiori": "cds watch fiori",
"hello": "cds watch hello",
"media": "cds watch media",
"lint": "eslint",
"test": "npx jest --silent",
"jest": "npx jest --silent",
"mocha": "CDS_TEST_SILENT=y npx mocha"
"mocha": "CDS_TEST_SILENT=y npx mocha",
"test:hello": "cd hello && npm test"
},
"mocha": {
"recursive": true,
@@ -44,19 +48,5 @@
"timeout": 6666
},
"license": "SEE LICENSE IN LICENSE",
"private": true,
"cds": {
"sql": {
"native_hana_associations": false
},
"requires": {
"[production]": {
"auth": "xsuaa"
},
"messaging": {
"kind": "enterprise-messaging"
},
"destinations": true
}
}
}
"private": true
}

View File

@@ -1,5 +1,5 @@
ID;subject;rating;reviewer;title;text
1689144d-3b10-4849-bcbe-2408a13e161a;201;5;bob;Intriguing;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.
1689144d-3b10-4849-bcbe-2408a13e161b;201;4;bob;Fascinating;Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Id diam maecenas ultricies mi eget mauris pharetra et. Risus at ultrices mi tempus imperdiet nulla malesuada pellentesque. Pulvinar mattis nunc sed blandit libero. Facilisis magna etiam tempor orci eu. Nec sagittis aliquam malesuada bibendum arcu. Eu consequat ac felis donec. Ultricies tristique nulla aliquet enim tortor at auctor urna nunc. Tortor posuere ac ut consequat semper viverra nam libero. Amet nisl suscipit adipiscing bibendum est ultricies integer quis auctor. Scelerisque purus semper eget duis at tellus. Elementum tempus egestas sed sed risus pretium. Arcu dictum varius duis at. Amet luctus venenatis lectus magna fringilla urna. Eget velit aliquet sagittis id consectetur purus ut faucibus. Vitae auctor eu augue ut lectus. Fermentum iaculis eu non diam phasellus vestibulum.
1689144d-3b10-4849-bcbe-2408a13e161c;207;2;bob;What is this?;Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Libero justo laoreet sit amet cursus sit amet dictum. Nunc faucibus a pellentesque sit. Dis parturient montes nascetur ridiculus mus mauris vitae ultricies. Enim nunc faucibus a pellentesque. Commodo quis imperdiet massa tincidunt nunc pulvinar sapien. Cras ornare arcu dui vivamus. Facilisi etiam dignissim diam quis enim lobortis. Et molestie ac feugiat sed. Urna neque viverra justo nec ultrices dui. Ullamcorper a lacus vestibulum sed arcu non. Volutpat ac tincidunt vitae semper quis. Dignissim sodales ut eu sem. Feugiat in fermentum posuere urna nec. At augue eget arcu dictum varius.
1689144d-3b10-4849-bcbe-2408a13e161d;251;3;bob;It's dark...;Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Suscipit tellus mauris a diam. Velit aliquet sagittis id consectetur purus ut. Viverra adipiscing at in tellus integer. Vitae elementum curabitur vitae nunc. Mattis ullamcorper velit sed ullamcorper morbi. Diam quis enim lobortis scelerisque. Auctor neque vitae tempus quam pellentesque nec nam aliquam. Semper auctor neque vitae tempus. Quis eleifend quam adipiscing vitae proin. Neque convallis a cras semper auctor neque vitae. Imperdiet massa tincidunt nunc pulvinar sapien et ligula. Sit amet consectetur adipiscing elit ut aliquam purus. Pretium quam vulputate dignissim suspendisse.
subject;rating;reviewer;title;text
201;5;bob;Intriguing;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.
201;4;bob;Fascinating;Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Id diam maecenas ultricies mi eget mauris pharetra et. Risus at ultrices mi tempus imperdiet nulla malesuada pellentesque. Pulvinar mattis nunc sed blandit libero. Facilisis magna etiam tempor orci eu. Nec sagittis aliquam malesuada bibendum arcu. Eu consequat ac felis donec. Ultricies tristique nulla aliquet enim tortor at auctor urna nunc. Tortor posuere ac ut consequat semper viverra nam libero. Amet nisl suscipit adipiscing bibendum est ultricies integer quis auctor. Scelerisque purus semper eget duis at tellus. Elementum tempus egestas sed sed risus pretium. Arcu dictum varius duis at. Amet luctus venenatis lectus magna fringilla urna. Eget velit aliquet sagittis id consectetur purus ut faucibus. Vitae auctor eu augue ut lectus. Fermentum iaculis eu non diam phasellus vestibulum.
207;2;bob;What is this?;Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Libero justo laoreet sit amet cursus sit amet dictum. Nunc faucibus a pellentesque sit. Dis parturient montes nascetur ridiculus mus mauris vitae ultricies. Enim nunc faucibus a pellentesque. Commodo quis imperdiet massa tincidunt nunc pulvinar sapien. Cras ornare arcu dui vivamus. Facilisi etiam dignissim diam quis enim lobortis. Et molestie ac feugiat sed. Urna neque viverra justo nec ultrices dui. Ullamcorper a lacus vestibulum sed arcu non. Volutpat ac tincidunt vitae semper quis. Dignissim sodales ut eu sem. Feugiat in fermentum posuere urna nec. At augue eget arcu dictum varius.
251;3;bob;It's dark...;Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Suscipit tellus mauris a diam. Velit aliquet sagittis id consectetur purus ut. Viverra adipiscing at in tellus integer. Vitae elementum curabitur vitae nunc. Mattis ullamcorper velit sed ullamcorper morbi. Diam quis enim lobortis scelerisque. Auctor neque vitae tempus quam pellentesque nec nam aliquam. Semper auctor neque vitae tempus. Quis eleifend quam adipiscing vitae proin. Neque convallis a cras semper auctor neque vitae. Imperdiet massa tincidunt nunc pulvinar sapien et ligula. Sit amet consectetur adipiscing elit ut aliquam purus. Pretium quam vulputate dignissim suspendisse.
1 ID subject rating reviewer title text
2 1689144d-3b10-4849-bcbe-2408a13e161a 201 5 bob Intriguing Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.
3 1689144d-3b10-4849-bcbe-2408a13e161b 201 4 bob Fascinating Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Id diam maecenas ultricies mi eget mauris pharetra et. Risus at ultrices mi tempus imperdiet nulla malesuada pellentesque. Pulvinar mattis nunc sed blandit libero. Facilisis magna etiam tempor orci eu. Nec sagittis aliquam malesuada bibendum arcu. Eu consequat ac felis donec. Ultricies tristique nulla aliquet enim tortor at auctor urna nunc. Tortor posuere ac ut consequat semper viverra nam libero. Amet nisl suscipit adipiscing bibendum est ultricies integer quis auctor. Scelerisque purus semper eget duis at tellus. Elementum tempus egestas sed sed risus pretium. Arcu dictum varius duis at. Amet luctus venenatis lectus magna fringilla urna. Eget velit aliquet sagittis id consectetur purus ut faucibus. Vitae auctor eu augue ut lectus. Fermentum iaculis eu non diam phasellus vestibulum.
4 1689144d-3b10-4849-bcbe-2408a13e161c 207 2 bob What is this? Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Libero justo laoreet sit amet cursus sit amet dictum. Nunc faucibus a pellentesque sit. Dis parturient montes nascetur ridiculus mus mauris vitae ultricies. Enim nunc faucibus a pellentesque. Commodo quis imperdiet massa tincidunt nunc pulvinar sapien. Cras ornare arcu dui vivamus. Facilisi etiam dignissim diam quis enim lobortis. Et molestie ac feugiat sed. Urna neque viverra justo nec ultrices dui. Ullamcorper a lacus vestibulum sed arcu non. Volutpat ac tincidunt vitae semper quis. Dignissim sodales ut eu sem. Feugiat in fermentum posuere urna nec. At augue eget arcu dictum varius.
5 1689144d-3b10-4849-bcbe-2408a13e161d 251 3 bob It's dark... Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Suscipit tellus mauris a diam. Velit aliquet sagittis id consectetur purus ut. Viverra adipiscing at in tellus integer. Vitae elementum curabitur vitae nunc. Mattis ullamcorper velit sed ullamcorper morbi. Diam quis enim lobortis scelerisque. Auctor neque vitae tempus quam pellentesque nec nam aliquam. Semper auctor neque vitae tempus. Quis eleifend quam adipiscing vitae proin. Neque convallis a cras semper auctor neque vitae. Imperdiet massa tincidunt nunc pulvinar sapien et ligula. Sit amet consectetur adipiscing elit ut aliquam purus. Pretium quam vulputate dignissim suspendisse.

View File

@@ -7,28 +7,17 @@
"index.cds"
],
"dependencies": {
"@cap-js/hana": "^1.7.0",
"@sap/cds": ">=5",
"@sap/xssec": "^4.4.0",
"express": "^4.17.1"
},
"scripts": {
"start": "cds-serve"
},
"cds": {
"requires": {
"messaging": {
"[development]": {
"kind": "file-based-messaging"
},
"[hybrid]": {
"kind": "enterprise-messaging-shared"
},
"[production]": {
"kind": "enterprise-messaging"
}
"[development]": { "kind": "file-based-messaging" },
"[hybrid]": { "kind": "enterprise-messaging-shared" },
"[production]": { "kind": "enterprise-messaging" }
},
"db": true
"db": { "kind": "sql" }
}
}
}
}

View File

@@ -6,6 +6,12 @@ Each sub directory essentially is an individual npm package arranged in an [all-
![](etc/samples.drawio.svg)
## [@capire/hello-world](hello)
- 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).
- [Typescript support](https://cap.cloud.sap/docs/node.js/typescript)
## [@capire/bookshop](bookshop)
- [Getting Started](https://cap.cloud.sap/docs/get-started/in-a-nutshell) with CAP, briefly introducing:
@@ -54,10 +60,10 @@ Each sub directory essentially is an individual npm package arranged in an [all-
- [@capire/reviews](reviews)
- [@capire/orders](orders)
- [@capire/common](common)
- [@capire/data-viewer](etc/data-viewer)
- [@capire/data-viewer](data-viewer)
- [The Vue.js app](bookshop/app/vue) imported from `bookshop` is served as well
- [The Vue.js app](reviews/app/vue) imported from `reviews` is served as well
- [The Vue.js app](etc/data-viewer/app/data) imported from `data-viewer` is served as well
- [The Vue.js app](data-viewer/app/data) imported from `data-viewer` is served as well
- [The Fiori app](orders/app) imported from `orders` is served as well
- [OpenAPI export + Swagger UI](https://cap.cloud.sap/docs/advanced/openapi)

View File

@@ -1,3 +0,0 @@
using from '@capire/bookstore';
using from '@capire/reviews';
using from '@capire/orders';

View File

@@ -1,17 +0,0 @@
{
"name": "shared-db",
"version": "1.0.0",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"description": "",
"cds": {
"sql": {
"native_hana_associations": false
}
}
}

22
test/hello-world.test.js Normal file
View File

@@ -0,0 +1,22 @@
const cds = require('@sap/cds')
describe('cap/samples - Hello world!', () => {
const { GET, expect } = cds.test (__dirname+'/../hello')
it('should say hello with class impl', async () => {
const {data} = await GET `/odata/v4/say/hello(to='world')`
expect(data.value).to.eql('Hello world!')
})
it('should say hello with another impl', async () => {
await cds.serve('say').from(cds.model)
.at('/say-again').in(cds.app)
.with(srv => {
srv.on('hello', (req) => `Hello again ${req.data.to}!`)
})
const {data} = await GET `/say-again/hello(to='world')`
expect(data.value).to.eql('Hello again world!')
})
})

View File

@@ -1,34 +0,0 @@
{
"scopes": [
{
"name": "$XSAPPNAME.admin",
"description": "admin"
},
{
"name": "$XSAPPNAME.emcallback",
"description": "Enterprise-Messaging Callback Access",
"grant-as-authority-to-apps": [
"$XSSERVICENAME(samples-messaging)"
]
},
{
"name": "$XSAPPNAME.emmanagement",
"description": "Enterprise-Messaging Management Access"
}
],
"attributes": [],
"role-templates": [
{
"name": "admin",
"scope-references": [
"$XSAPPNAME.admin"
],
"description": "cap samples multi-service shared-db"
}
],
"authorities-inheritance": false,
"authorities": [
"$XSAPPNAME.emmanagement",
"$XSAPPNAME.mtcallback"
]
}