Compare commits

..

4 Commits

Author SHA1 Message Date
Daniel
9c49454f44 . 2021-06-18 17:59:54 +02:00
Daniel
d570ad20b2 ... 2021-06-18 17:59:27 +02:00
Daniel
d891a1b905 .. 2021-06-18 17:15:59 +02:00
Daniel
9f604b9d13 . 2021-06-18 16:57:15 +02:00
43 changed files with 373 additions and 3160 deletions

View File

@@ -5,9 +5,9 @@ name: CI
on:
push:
branches: [ main ]
branches: [ master ]
pull_request:
branches: [ main ]
branches: [ master ]
jobs:
build:
@@ -24,5 +24,5 @@ jobs:
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- run: npm ci
- run: npm install
- run: npm test

2
.gitignore vendored
View File

@@ -15,5 +15,3 @@ default-env.json
packages/messageBox
reviews/msg-box
reviews/db/test.db
*.openapi3.json

3
.npmrc
View File

@@ -1,3 +0,0 @@
# Ensure we always use public packages, i.e. avoid using local registries from ~/.npmrc
@sap:registry=https://registry.npmjs.org/
registry=https://registry.npmjs.org/

View File

@@ -105,5 +105,5 @@
"description": "### Summary\n\nThat's it! You have seen: \n- How to integrate database-specific functions in a CDS model.\n- How to switch between the two implementations for SQLite and SAP HANA."
}
],
"ref": "main"
"ref": "master"
}

View File

@@ -17,7 +17,7 @@ Find here a collection of samples for the [SAP Cloud Application Programming Mod
### Download
If you've [Git](https://git-scm.com/downloads) installed, clone this repo as shown below, otherwise [download as ZIP file](archive/main.zip).
If you've [Git](https://git-scm.com/downloads) installed, clone this repo as shown below, otherwise [download as ZIP file](archive/master.zip).
```sh
git clone https://github.com/sap-samples/cloud-cap-samples samples

View File

@@ -10,7 +10,7 @@ const books = new Vue ({
data: {
list: [],
book: undefined,
order: { quantity:1, succeeded:'', failed:'' }
order: { amount:1, succeeded:'', failed:'' }
},
methods: {
@@ -26,18 +26,18 @@ const books = new Vue ({
const book = books.book = books.list [eve.currentTarget.rowIndex-1]
const res = await GET(`/Books/${book.ID}?$select=descr,stock,image`)
Object.assign (book, res.data)
books.order = { quantity:1 }
books.order = { amount:1 }
setTimeout (()=> $('form > input').focus(), 111)
},
async submitOrder () {
const {book,order} = books, quantity = parseInt (order.quantity) || 1 // REVISIT: Okra should be less strict
const {book,order} = books, amount = parseInt (order.amount) || 1 // REVISIT: Okra should be less strict
try {
const res = await POST(`/submitOrder`, { quantity, book: book.ID })
const res = await POST(`/submitOrder`, { amount, book: book.ID })
book.stock = res.data.stock
books.order = { quantity, succeeded: `Successfully ordered ${quantity} item(s).` }
books.order = { amount, succeeded: `Successfully ordered ${amount} item(s).` }
} catch (e) {
books.order = { quantity, failed: e.response.data.error.message }
books.order = { amount, failed: e.response.data.error.message }
}
}

View File

@@ -48,7 +48,7 @@
   {{ book.stock }} in stock
</label>
<form @submit.prevent="submitOrder" style="float:right; display:flex; flex-direction:row-reverse">
<input type="number" v-model="order.quantity" v-bind:class="{ failed: order.failed }" style="width:5em">
<input type="number" v-model="order.amount" v-bind:class="{ failed: order.failed }" style="width:5em">
<input type="submit" value="Order:" class="muted-button">
</form>
<h4> {{ book.title }} </h4>

View File

@@ -11,6 +11,6 @@ service CatalogService @(path:'/browse') {
} excluding { createdBy, modifiedBy };
@requires: 'authenticated-user'
action submitOrder ( book: Books:ID, quantity: Integer ) returns { stock: Integer };
event OrderedBook : { book: Books:ID; quantity: Integer; buyer: String };
action submitOrder ( book: Books:ID, amount: Integer ) returns { stock: Integer };
event OrderedBook : { book: Books:ID; amount: Integer; buyer: String };
}

View File

@@ -5,14 +5,14 @@ class CatalogService extends cds.ApplicationService { init(){
// Reduce stock of ordered books if available stock suffices
this.on ('submitOrder', async req => {
const {book,quantity} = req.data
const {book,amount} = req.data
let {stock} = await SELECT `stock` .from (Books,book)
if (stock >= quantity) {
await UPDATE (Books,book) .with (`stock -=`, quantity)
await this.emit ('OrderedBook', { book, quantity, buyer:req.user.id })
if (stock >= amount) {
await UPDATE (Books,book) .with (`stock -=`, amount)
await this.emit ('OrderedBook', { book, amount, buyer:req.user.id })
return { stock }
}
else return req.error (409,`${quantity} exceeds stock for book #${book}`)
else return req.error (409,`${amount} exceeds stock for book #${book}`)
})
// Add some discount for overstocked books

View File

@@ -71,7 +71,7 @@ POST {{server}}/browse/submitOrder
Content-Type: application/json
{{me}}
{ "book":201, "quantity":5 }
{ "book":201, "amount":5 }
### ------------------------------------------------------------------------

View File

@@ -1,5 +1,4 @@
using { AdminService } from '../../db/schema';
using from '../common'; // to help UI linter get the complete annotations
////////////////////////////////////////////////////////////////////////////
//
@@ -87,8 +86,8 @@ annotate AdminService.Books.texts with @(
annotate AdminService.Books.texts {
locale @ValueList:{entity:'Languages',type:#fixed}
}
// In addition we need to expose Languages through AdminService as a target for ValueList
// In addition we need to expose Languages through AdminService
using { sap } from '@sap/cds/common';
extend service AdminService {
@readonly entity Languages as projection on sap.common.Languages;
entity Languages as projection on sap.common.Languages;
}

View File

@@ -6,11 +6,11 @@ using CatalogService from '@capire/bookshop';
//
annotate CatalogService.Books with @(
UI: {
HeaderInfo: {
HeaderInfo: {
TypeName: 'Book',
TypeNamePlural: 'Books',
Description: {Value: author}
},
Description: {Value: author}
},
HeaderFacets: [
{$Type: 'UI.ReferenceFacet', Label: '{i18n>Description}', Target: '@UI.FieldGroup#Descr'},
],
@@ -38,7 +38,7 @@ annotate CatalogService.Books with @(
//
annotate CatalogService.Books with @(
UI: {
SelectionFields: [ ID, price, currency_code ],
SelectionFields: [ ID, price, currency_code ],
LineItem: [
{Value: title},
{Value: author, Label:'{i18n>Author}'},

View File

@@ -1,5 +1,4 @@
const cds = require ('@sap/cds')
module.exports = cds.server
cds.once('bootstrap',(app)=>{
app.use ('/orders/webapp', _from('@capire/orders/app/orders/webapp/manifest.json'))
@@ -9,13 +8,7 @@ cds.once('bootstrap',(app)=>{
cds.once('served', require('./srv/mashup'))
// Swagger UI - see https://cap.cloud.sap/docs/advanced/openapi
try {
const cds_swagger = require ('cds-swagger-ui-express')
cds.once ('bootstrap', app => app.use (cds_swagger()) )
} catch (err) {
if (err.code !== 'MODULE_NOT_FOUND') throw err
}
module.exports = cds.server
// -----------------------------------------------------------------------

View File

@@ -19,7 +19,7 @@ extend Books with {
// Extend Orders with Books as Products
//
using { sap.capire.orders.Orders_Items } from '@capire/orders';
extend Orders_Items with {
using { sap.capire.orders.Orders } from '@capire/orders';
extend Orders.Items with {
book : Association to Books on product.ID = book.ID
}

View File

@@ -27,11 +27,11 @@ module.exports = async()=>{ // called by server.js
// Create an order with the OrdersService when CatalogService signals a new order
//
CatalogService.on ('OrderedBook', async (msg) => {
const { book, quantity, buyer } = msg.data
const { book, amount, buyer } = msg.data
const { title, price } = await db.tx(msg).read (Books, book, b => { b.title, b.price })
return OrdersService.tx(msg).create ('Orders').entries({
OrderNo: 'Order at '+ (new Date).toLocaleString(),
Items: [{ product:{ID:`${book}`}, title, price, quantity }],
Items: [{ product:{ID:`${book}`}, title, price, amount }],
buyer, createdBy: buyer
})
})
@@ -51,9 +51,9 @@ module.exports = async()=>{ // called by server.js
//
OrdersService.on ('OrderChanged', (msg) => {
console.debug ('> received:', msg.event, msg.data)
const { product, deltaQuantity } = msg.data
const { product, deltaAmount } = msg.data
return UPDATE (Books) .where ('ID =', product)
.and ('stock >=', deltaQuantity)
.set ('stock -=', deltaQuantity)
.and ('stock >=', deltaAmount)
.set ('stock -=', deltaAmount)
})
}

View File

@@ -1,15 +0,0 @@
# 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/get-started/using-typescript)

View File

@@ -2,53 +2,6 @@
"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"
},
"devDependencies": {
"@types/jest": "^26.0.23",
"@types/node": "^15.12.0",
"ts-jest": "^27.0.2",
"typescript": "^4.3.5"
},
"jest": {
"testEnvironment": "node",
"preset": "ts-jest",
"globals": {
"ts-jest": {
"diagnostics": {
"_comment": "see https://githubmemory.com/repo/kulshekhar/ts-jest/issues/2722",
"ignoreCodes": [
151001
]
}
}
}
},
"eslintConfig": {
"extends": "eslint:recommended",
"env": {
"es2020": true,
"node": true,
"jest": true,
"mocha": true
},
"globals": {
"SELECT": true,
"INSERT": true,
"UPDATE": true,
"DELETE": true,
"CREATE": true,
"DROP": true,
"CDL": true,
"CQL": true,
"CXL": true,
"cds": true
},
"rules": {
"no-console": "off",
"require-atomic-updates": "off"
}
"watch": "cds serve world.cds"
}
}

View File

@@ -1,5 +0,0 @@
module.exports = class say {
hello(req: any) {
return `Hello ${req.data.to} from a TypeScript file!`
}
}

View File

@@ -1,15 +0,0 @@
process.env.CDS_TYPESCRIPT = 'true';
import * as cds from '@sap/cds';
//@ts-ignore
const {GET} = cds.test.in(__dirname,'../srv').run('serve', 'world.cds');
describe('Hello world!', () => {
afterAll(() => { delete process.env.CDS_TYPESCRIPT; });
it('should say hello with class impl from a typescript file', async () => {
const {data} = await GET`/say/hello(to='world')`
expect(data.value).toMatch(/Hello world.*typescript.*/i)
})
})

View File

@@ -1,2 +1,3 @@
cds.requires.messaging.kind = file-based-messaging
PORT = 4006
# cds.odata.flavor = x4

View File

@@ -11,6 +11,7 @@
using { OrdersService } from '../../srv/orders-service';
using { sap.common } from '@capire/common';
@odata.draft.enabled
@@ -45,8 +46,7 @@ annotate OrdersService.Orders with @(
],
FieldGroup#Details: {
Data: [
{Value: currency.code, Label:'Currency'},
{Value: status.code, Label:'Status'},
{Value: currency.code, Label:'Currency'}
]
},
FieldGroup#Created: {
@@ -67,30 +67,36 @@ annotate OrdersService.Orders with @(
createdBy @UI.HiddenFilter:false;
};
annotate OrdersService.OrderStatus with {
code @Common: { Text: name, TextArrangement: #TextOnly };
}
annotate OrdersService.Orders_Items with @(
annotate OrdersService.Orders.Items with @(
UI: {
LineItem: [
{Value: product_ID, Label:'Product ID'},
{Value: title, Label:'Product Title'},
{Value: price, Label:'Unit Price'},
{Value: quantity, Label:'Quantity'},
{Value: amount, Label:'Quantity'},
],
Identification: [ //Is the main field group
{Value: quantity, Label:'Quantity'},
{Value: title, Label:'Product'},
{Value: product_ID, Label:'Product ID'},
{Value: title, Label:'Product Title'},
{Value: amount, Label:'Amount'},
{Value: price, Label:'Unit Price'},
],
Facets: [
{$Type: 'UI.ReferenceFacet', Label: '{i18n>OrderItems}', Target: '@UI.Identification'},
],
HeaderInfo: {
TypeName: 'Order Item', TypeNamePlural: 'Order Items',
Title: {
Label: 'Product ID ', //A label is possible but it is not considered on the ObjectPage yet
Value: product_ID
},
Description: {Value: createdBy}
},
},
) {
quantity @(
amount @(
Common.FieldControl: #Mandatory
);
};

View File

@@ -1,4 +0,0 @@
code;name;descr
O;Open;Order is open
P;In Process;Order is about to be processed
C;Closed;Order is closed
Internal Server Error - Bruno's Gitea
500 Internal Server Error

Gitea Version: 1.24.4