Compare commits

..

8 Commits

Author SHA1 Message Date
i048695
f00e484dfc update npmrc
npm.sap.com is deprecated
2021-04-19 10:04:34 +02:00
Offer Shostak
aaafe52b01 Support NODE 14
When trying to executing the exercise in this branch:
https://github.com/SAP-samples/cloud-cap-samples/tree/CAA265-node-initial
On machine with NODE 14 it failed.
When bumping sqlite3 to 5.0.0 it works OK.

Regards,
Offer.
2021-02-17 14:52:38 +01:00
D065023
9591866112 version 2019-10-25 09:53:31 +02:00
johannes-vogel
e84792f122 Update README.md 2019-09-23 14:13:55 +02:00
d045778
2fc2cea260 add sap registry to npmrc 2019-09-23 13:06:09 +02:00
johannes-vogel
bfaaf36e14 Fix Key in Products 2019-09-22 13:32:18 +02:00
johannes-vogel
33b24c8d96 Use version 3.17.4 (#1) 2019-09-21 11:36:45 +02:00
i079695
387b25b1bf Add initial branch for CAA265 2019-09-11 11:07:11 +02:00
31 changed files with 374 additions and 2300 deletions

View File

@@ -6,7 +6,7 @@
"jest": true
},
"parserOptions": {
"ecmaVersion": 2018
"ecmaVersion": 2017
},
"globals": {
"SELECT": true,

2
.npmrc
View File

@@ -1 +1 @@
@sap:registry=https://npm.sap.com
package-lock=false

37
.vscode/launch.json vendored
View File

@@ -1,37 +0,0 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "bookshop", "request": "launch", "type": "node", "runtimeExecutable": "npx", "runtimeArgs": [ "-n" ],
"args": [ "--", "cds", "run", "--in-memory" ],
"cwd": "${workspaceFolder}/packages/bookshop",
"console": "integratedTerminal",
"skipFiles": ["<node_internals>/**"]
},
{
"name": "cds run ...", "request": "launch", "type": "node", "runtimeExecutable": "npx", "runtimeArgs": [ "-n" ],
"args": [ "--", "cds", "run", "--with-mocks", "--in-memory?" ],
"cwd": "${workspaceFolder}/packages/${input:service}",
"console": "integratedTerminal",
"skipFiles": ["<node_internals>/**"]
}
],
"inputs": [
{
"type": "pickString",
"id": "service",
"description": "Which service do you want to start?",
"options": [
"bookshop",
"bookstore",
"media-server",
"office-supplies",
"reviews-service"
],
"default": "bookshop"
}
]
}

View File

@@ -1,6 +0,0 @@
{
"files.exclude": {
"**/.gitignore": true,
"**/.vscode": true
}
}

17
.vscode/tasks.json vendored
View File

@@ -1,17 +0,0 @@
{
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
"version": "2.0.0",
"tasks": [
{
"type": "npm", "script": "watch", "path": "packages/bookshop/",
"options": { "env": { "PORT": "4004" }},
"presentation": { "group": "A" }
},
{
"type": "npm", "script": "watch", "path": "packages/reviews-service/",
"options": { "env": { "PORT": "5005" }},
"presentation": { "group": "A" }
}
]
}

View File

@@ -1,42 +1,3 @@
# Welcome to SAP Cloud Application Programming model samples
# Initial branch for CAA265 - Rapid Service Development with SAP Cloud Application Programming Model
Find here the samples for the openSAP course [Building Applications with the SAP Cloud Application Programming Model](https://open.sap.com/courses/cp7).
## Get Access to SAP Business Application Studio
The recommended environment for the course is SAP Business Application Studio. Watch [unit 2 of week 1](https://open.sap.com/courses/cp7/items/51pzQUzbXHr2kdbOmVs6jI) for how to get access.
## Setup
In SAP Business Application Studio, open a terminal.
Then clone the repo with this specific branch:
```sh
git clone https://github.com/sap-samples/cloud-cap-samples projects/cloud-cap-samples -b openSAP-week1-unit4-final
cd projects/cloud-cap-samples
```
In the `cloud-cap-samples` folder run:
```sh
npm install
```
## Run
Now you're ready to run the samples, for example:
```sh
cd packages/bookshop
cds watch
```
After that, watch out for the little popup in the lower right corner of SAP Business Application Studio that asks you to open the application in your browser.
## Get Support
Check out the cap docs at 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).
## License
Copyright (c) 2020 SAP SE or an SAP affiliate company. All rights reserved. This file is licensed under SAP Sample Code License Agreement, except as noted otherwise in the [LICENSE](/LICENSE) file.
**DO NOT MERGE IN MASTER**

View File

@@ -1 +1,7 @@
{"packages":["packages/*"],"version":"1.0.0"}
{
"packages": [
"packages/*",
"shared/*"
],
"version": "0.0.0"
}

2064
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,29 +1,16 @@
{
"name": "@sap/capire-samples",
"description": "The umbrella project for all samples to easily setup for local development and tests.",
"repository": "https://github.com/SAP-samples/cloud-cap-samples.git",
"repository": "https://github.wdf.sap.corp/capire/samples.git",
"author": "daniel.hutzel@sap.com",
"private": true,
"scripts": {
"lerna": "npx --no-install lerna -v > /dev/null || npm i lerna --no-save",
"install": "(npm -s run lerna) && lerna bootstrap --hoist",
"cleanup": "lerna clean -y && rm -fr node_modules",
"bookshop": "cds watch packages/bookshop",
"bookshop-enhanced": "cds watch packages/bookshop-enhanced",
"reviews-service": "cds watch packages/reviews-service",
"bookstore": "cds watch packages/bookstore",
"media-server": "cds watch packages/media-server"
"install": "(npx --no-install lerna -v || npm i lerna --no-save) && lerna bootstrap --hoist --ignore tests-env --loglevel warn",
"reset": "lerna clean -y && rm -fr node_modules",
"test": "jest",
"bookstore": "cds run packages/bookstore --in-memory",
"products-service": "cds run packages/products-service --in-memory",
"reviews-service": "cds run packages/reviews-service --in-memory"
},
"dependencies": {
"@sap/cds": "^3",
"express": "^4"
},
"devDependencies": {
"sqlite3": "*"
},
"--add-these-to-devDependencies-for-tests": {
"@types/jest": "*",
"jest": "*"
},
"license": "SAP SAMPLE CODE LICENSE"
"license": "ISC"
}

3
packages/.cdsrc.json Normal file
View File

@@ -0,0 +1,3 @@
{
"cds_version": "^3.17.4"
}

View File

@@ -1,5 +0,0 @@
ID;name
101;Emily Brontë
107;Charlotte Brontë
150;Edgar Allen Poe
170;Richard Carpenter
1 ID name
2 101 Emily Brontë
3 107 Charlotte Brontë
4 150 Edgar Allen Poe
5 170 Richard Carpenter

View File

@@ -1,6 +0,0 @@
ID;title;author_ID;stock
201;Wuthering Heights;101;12
207;Jane Eyre;107;11
251;The Raven;150;333
252;Eleonora;150;555
271;Catweazle;170;22
1 ID title author_ID stock
2 201 Wuthering Heights 101 12
3 207 Jane Eyre 107 11
4 251 The Raven 150 333
5 252 Eleonora 150 555
6 271 Catweazle 170 22

View File

@@ -1,30 +0,0 @@
namespace sap.capire.bookshop;
using { Currency, managed } from '@sap/cds/common';
entity Books : managed {
key ID : Integer;
title : localized String(111);
descr : localized String(1111);
author : Association to Authors;
stock : Integer;
price : Decimal(9,2);
currency : Currency;
}
entity Authors : managed {
key ID : Integer;
name : String(111);
books : Association to many Books on books.author = $self;
}
entity Orders : managed {
key ID : UUID;
OrderNo : String @title:'Order Number'; //> readable key
Items : Composition of many OrderItems on Items.parent = $self;
}
entity OrderItems {
key ID : UUID;
parent : Association to Orders;
book : Association to Books;
amount : Integer;
}

View File

@@ -1,14 +0,0 @@
{
"name": "bookshop",
"version": "1.0.0",
"description": "A simple CAP project.",
"repository": "<Add your repository here>",
"license": "ISC",
"dependencies": {
"@sap/cds": "^3",
"express": "^4"
},
"scripts": {
"start": "npx cds run"
}
}

View File

@@ -1,12 +0,0 @@
### Submit Orders
POST http://localhost:4004/browse/Orders
Content-Type: application/json
{ "OrderNo":"2019-09...", "Items":[
{ "book_ID":201, "amount":5 }
]}
# Sending this three times should result in a 409: 5 exceeds stock for book #201
### Check books entity that stock was reduced
GET http://localhost:4004/admin/Books(201)

View File

@@ -1,6 +0,0 @@
using { sap.capire.bookshop as my } from '../db/schema';
service AdminService @(_requires:'admin',path:'/admin') {
entity Books as projection on my.Books;
entity Authors as projection on my.Authors;
entity Orders as select from my.Orders;
}

View File

@@ -1,10 +0,0 @@
using { sap.capire.bookshop as my } from '../db/schema';
service CatalogService @(path:'/browse') {
@readonly entity Books as SELECT from my.Books {*,
author.name as author
} excluding { createdBy, modifiedBy };
@requires_: 'authenticated-user'
@insertonly entity Orders as projection on my.Orders;
}

View File

@@ -1,28 +0,0 @@
/**
* Implementation for CatalogService defined in ./cat-service.cds
*/
module.exports = (srv)=>{
// Use reflection to get the csn definition of Books
const {Books} = cds.entities
// Add some discount for overstocked books
srv.after ('READ','Books', (each)=>{
if (each.stock > 111) each.title += ' -- 11% discount!'
})
// Reduce stock of books upon incoming orders
srv.before ('CREATE','Orders', async (req)=>{
const tx = cds.transaction(req), order = req.data;
if (order.Items) {
const affectedRows = await tx.run(order.Items.map(item =>
UPDATE(Books) .where({ID:item.book_ID})
.and(`stock >=`, item.amount)
.set(`stock -=`, item.amount)
)
)
if (affectedRows.some(row => !row)) req.error(409, 'Sold out, sorry')
}
})
}

View File

@@ -0,0 +1,6 @@
{
"build": {
"target": ".",
"tasks": []
}
}

15
packages/bookstore/.gitignore vendored Normal file
View File

@@ -0,0 +1,15 @@
.che/
.gen/
gen/
mta_archives/
node_modules/
target/
.cds_gen.log
connection.properties
*.db
.DS_Store
*.orig
_out
default-*.json
package-lock.json

View File

@@ -0,0 +1,19 @@
{
"name": "bookstore",
"description": "Generated by cds init",
"repository": "<Add your repository here>",
"license": "ISC",
"version": "1.0.0",
"engines": {
"node": "^8.9"
},
"dependencies": {
"@sap/cds": "^3.17.4",
"express": "^4.17.1"
},
"scripts": {
"build": "cds build/all --clean",
"deploy": "cds deploy",
"start": "cds run"
}
}

12
packages/products-service/.gitignore vendored Normal file
View File

@@ -0,0 +1,12 @@
gen/
.gen/
node_modules/
target/
*.db
.DS_Store
_out
.che/
.cds_gen.log
package-lock.json
*.orig
mta_archives/

View File

@@ -0,0 +1,19 @@
namespace sap.capire.products;
using { Currency, cuid, managed, sap.common.CodeList } from '@sap/cds/common';
entity Products : cuid, managed {
key ID : String(36);
title : localized String(111);
descr : localized String(1111);
stock : Integer;
price : Decimal(9,2);
currency : Currency;
category : Association to Categories;
}
entity Categories : CodeList {
key ID : Integer;
parent : Association to Categories;
children : Composition of many Categories on children.parent = $self;
}

View File

@@ -0,0 +1,2 @@
using from './db/schema';
using from './srv/admin-service';

View File

@@ -0,0 +1,16 @@
{
"name": "@sap/capire-products",
"version": "1.0.0",
"description": "A reuse package providing domain models and services to manage product catalogs.",
"repository": "https://github.wdf.sap.corp/caps/products-service.git",
"license": "ISC",
"dependencies": {
"@sap/cds": "^3.17.4",
"express": "^4.17.1"
},
"files": [
"db",
"srv",
"index.cds"
]
}

View File

@@ -0,0 +1,7 @@
using { sap.capire.products as db } from '../db/schema';
namespace sap.capire.products;
service AdminService @(_requires:'admin') {
entity Products as projection on db.Products;
entity Categories as projection on db.Categories;
}

View File

@@ -0,0 +1,68 @@
const cds = require ('@sap/cds')
describe('reading/writing hierarchies', ()=>{
it ('should prepare to sqlite in-memory', async()=>{
await cds.deploy (__dirname+'/../db') .to ('sqlite::memory:')
expect (cds.model) .toBeDefined()
})
it ('should insert hierarchy of categories', ()=>{
const { Categories } = cds.entities
return INSERT.into (Categories) .entries (
{ ID:100, name:'Some Sample Categories...', children:[
{ ID:101, name:'Cat', children:[
{ ID:102, name:'Kitty', children:[
{ ID:103, name:'Kitty Cat', children:[
{ ID:104, name:'Aristocat' } ]},
{ ID:105, name:'Kitty Bat' } ]},
{ ID:106, name:'Catwoman', children:[
{ ID:107, name:'Catalina' } ]} ]},
{ ID:108, name:'Catweazle' }
]}
)
})
it ('should read categories with children', async()=>{
const { Categories } = cds.entities
expect (await
SELECT.one.from (Categories, c=>{
c.ID, c.name.as('parent'), c.children (c=>{
c.name.as('child')
})
}) .where ({name:'Cat'})
) .toMatchObject (
{ ID:101, parent:'Cat', children:[
{ child:'Kitty' },
{ child:'Catwoman' },
]}
)
})
it ('should read hierarchy of categories', async()=>{
const { Categories } = cds.entities
expect (await
SELECT.one.from (Categories, c=>{
c.ID, c.name, c.children (c=>{ c.name },{levels:3})
}) .where ({name:'Cat'})
) .toMatchObject (
{ ID:101, name:'Cat', children:[
{ name:'Kitty', children:[
{ name:'Kitty Cat', children:[
{ name:'Aristocat' }, ]},
{ name:'Kitty Bat' }, ]},
{ name:'Catwoman', children:[
{ name:'Catalina' } ]},
]}
)
})
})

View File

@@ -0,0 +1,10 @@
ID;parent_ID;name
0;;Some Sample Categories...
1;;Cat
2;1;Kitty
3;2;Kitty Cat
4;3;Aristocat
5;2;Kitty Bat
6;1;Catwoman
7;6;Catalina
8;;Catweazle
1 ID parent_ID name
2 0 Some Sample Categories...
3 1 Cat
4 2 1 Kitty
5 3 2 Kitty Cat
6 4 3 Aristocat
7 5 2 Kitty Bat
8 6 1 Catwoman
9 7 6 Catalina
10 8 Catweazle

View File

@@ -0,0 +1,157 @@
{
"info": {
"_postman_id": "0f8d4e79-a1c2-47fe-aeab-0319fb4ce180",
"name": "@sap/capire-products",
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
},
"item": [
{
"name": "Categories",
"request": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{ \"ID\":0, \"name\":\"Some Sample Categories...\", \"children\":[\n { \"ID\":1, \"name\":\"Cat\", \"children\":[\n { \"ID\":2, \"name\":\"Kitty\", \"children\":[\n { \"ID\":3, \"name\":\"Kitty Cat\", \"children\":[\n { \"ID\":4, \"name\":\"Aristocat\" }\n ]},\n { \"ID\":5, \"name\":\"Kitty Bat\" }\n ]},\n { \"ID\":6, \"name\":\"Catwoman\", \"children\":[\n { \"ID\":7, \"name\":\"Catalina\" }\n ]}\n ] },\n { \"ID\":8, \"name\":\"Catweazle\" }\n]}\n"
},
"url": {
"raw": "http://localhost:4004/admin/cats",
"protocol": "http",
"host": [
"localhost"
],
"port": "4004",
"path": [
"admin",
"cats"
]
}
},
"response": []
},
{
"name": "Categories",
"request": {
"method": "GET",
"header": [],
"body": {
"mode": "raw",
"raw": ""
},
"url": {
"raw": "http://localhost:4004/admin/cats/0?$expand=children($expand=children($expand=children($expand=children)))",
"protocol": "http",
"host": [
"localhost"
],
"port": "4004",
"path": [
"admin",
"cats",
"0"
],
"query": [
{
"key": "$expand",
"value": "children($expand=children($expand=children($expand=children)))"
}
]
}
},
"response": [
{
"name": "Categories",
"originalRequest": {
"method": "GET",
"header": [],
"body": {
"mode": "raw",
"raw": ""
},
"url": {
"raw": "http://localhost:4004/admin/cats/0?$expand=children($expand=children($expand=children($expand=children)))",
"protocol": "http",
"host": [
"localhost"
],
"port": "4004",
"path": [
"admin",
"cats",
"0"
],
"query": [
{
"key": "$expand",
"value": "children($expand=children($expand=children($expand=children)))"
}
]
}
},
"status": "OK",
"code": 200,
"_postman_previewlanguage": "json",
"header": [
{
"key": "X-Powered-By",
"value": "Express"
},
{
"key": "OData-Version",
"value": "4.0"
},
{
"key": "content-type",
"value": "application/json;odata.metadata=minimal"
},
{
"key": "Date",
"value": "Tue, 21 May 2019 19:20:24 GMT"
},
{
"key": "Connection",
"value": "keep-alive"
},
{
"key": "Content-Length",
"value": "767"
}
],
"cookie": [],
"body": "{\n \"@odata.context\": \"$metadata#cats(children(children(children(children()))))/$entity\",\n \"@odata.metadataEtag\": \"W/\\\"+AAp4JKNOcr+OusjrdQo55RCfM+UHKpTh8EbhsxyPhM=\\\"\",\n \"name\": \"Some Sample Categories...\",\n \"descr\": null,\n \"ID\": 0,\n \"parent_ID\": null,\n \"children\": [\n {\n \"name\": \"Cat\",\n \"descr\": null,\n \"ID\": 1,\n \"parent_ID\": 0,\n \"children\": [\n {\n \"name\": \"Kitty\",\n \"descr\": null,\n \"ID\": 2,\n \"parent_ID\": 1,\n \"children\": [\n {\n \"name\": \"Kitty Cat\",\n \"descr\": null,\n \"ID\": 3,\n \"parent_ID\": 2,\n \"children\": [\n {\n \"name\": \"Aristocat\",\n \"descr\": null,\n \"ID\": 4,\n \"parent_ID\": 3\n }\n ]\n },\n {\n \"name\": \"Kitty Bat\",\n \"descr\": null,\n \"ID\": 5,\n \"parent_ID\": 2,\n \"children\": []\n }\n ]\n },\n {\n \"name\": \"Catwoman\",\n \"descr\": null,\n \"ID\": 6,\n \"parent_ID\": 1,\n \"children\": [\n {\n \"name\": \"Catalina\",\n \"descr\": null,\n \"ID\": 7,\n \"parent_ID\": 6,\n \"children\": []\n }\n ]\n }\n ]\n },\n {\n \"name\": \"Catweazle\",\n \"descr\": null,\n \"ID\": 8,\n \"parent_ID\": 0,\n \"children\": []\n }\n ]\n}"
}
]
},
{
"name": "Categories",
"request": {
"method": "DELETE",
"header": [],
"body": {
"mode": "raw",
"raw": ""
},
"url": {
"raw": "http://localhost:4004/admin/cats/0",
"protocol": "http",
"host": [
"localhost"
],
"port": "4004",
"path": [
"admin",
"cats",
"0"
]
}
},
"response": []
}
]
}

View File

@@ -0,0 +1,11 @@
{
"name": "dev-env",
"author": "daniel.hutzel@sap.com",
"dependencies": {
"@sap/cds": "^3.17.4"
},
"devDependencies": {
"sqlite3": "5.0.0"
},
"license": "ISC"
}

View File

@@ -0,0 +1,10 @@
{
"name": "tests-env",
"author": "daniel.hutzel@sap.com",
"devDependencies": {
"@types/jest": "*",
"jest": "^24.8.0",
"supertest": "^4.0.2"
},
"license": "ISC"
}