Adding suppliers showing integration with S/4

This commit is contained in:
Daniel
2021-02-17 13:24:17 +01:00
parent b6e5a2fced
commit d72ff809b0
18 changed files with 5941 additions and 5 deletions

View File

@@ -3,9 +3,10 @@
"version": "1.0.0",
"dependencies": {
"@capire/bookshop": "*",
"@capire/reviews": "*",
"@capire/orders": "*",
"@capire/common": "*",
"@capire/orders": "*",
"@capire/reviews": "*",
"@capire/suppliers": "*",
"@sap/cds": "^4",
"express": "^4.17.1",
"passport": "0.4.1"
@@ -16,6 +17,10 @@
},
"cds": {
"requires": {
"API_BUSINESS_PARTNER": {
"kind": "odata",
"model": "@capire/suppliers"
},
"ReviewsService": {
"kind": "odata", "model": "@capire/reviews"
},

View File

@@ -7,6 +7,7 @@ cds.once('bootstrap',(app)=>{
})
cds.once('served', require('./srv/mashup'))
cds.once('served', require('@capire/suppliers/srv/mashup'))
module.exports = cds.server

View File

@@ -11,7 +11,8 @@
"@capire/hello": "./hello",
"@capire/media": "./media",
"@capire/orders": "./orders",
"@capire/reviews": "./reviews"
"@capire/reviews": "./reviews",
"@capire/suppliers": "./suppliers"
},
"devDependencies": {
"chai": "^4.2.0",

View File

@@ -49,14 +49,22 @@ Each sub directory essentially is an individual npm package arranged in an [all-
- Late-cut Micro Services
- As well as managed data, input validations, and authorization
## [@capire/suppliers](suppliers)
- Shows how to integrate remote services, in this case the BusinessPartner service from S/4.
- Extending [@capire/bookshop](bookshop) with suppliers from S/4
- Providing that as a pre-built integration & extension package
- Used in [@capire/fiori](fiori)
## [@capire/fiori](fiori)
- A [composite app, reusing and combining](https://cap.cloud.sap/docs/guides/verticalize) these packages:
- [@capire/bookshop](bookshop)
- [@capire/reviews](reviews)
- [@capire/orders](orders)
- [@capire/common](common)
- [@capire/orders](orders)
- [@capire/reviews](reviews)
- [@capire/suppliers](suppliers)
- [Adds a SAP Fiori elements application](https://cap.cloud.sap/docs/guides/fiori/) to bookshop, thereby introducing to:
- [OData Annotations](https://cap.cloud.sap/docs/guides/fiori#adding-odata-annotations) in `.cds` files
- Support for [Fiori Draft](https://cap.cloud.sap/docs/guides/fiori#draft)

29
suppliers/.gitignore vendored Normal file
View File

@@ -0,0 +1,29 @@
# CAP demo
_out
*.db
connection.properties
default-*.json
gen/
node_modules/
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

20
suppliers/.vscode/extensions.json vendored Normal file
View File

@@ -0,0 +1,20 @@
{
// 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": [
// >>>>>>>> Add CDS Editor here as soon it is available of vscode marketplace!,
"dbaeumer.vscode-eslint",
"esbenp.prettier-vscode",
"mechatroner.rainbow-csv",
"humao.rest-client",
"alexcvzz.vscode-sqlite",
"hbenl.vscode-mocha-test-adapter",
"sdras.night-owl"
],
// List of extensions recommended by VS Code that should not be recommended for users of this workspace.
"unwantedRecommendations": [
]
}

15
suppliers/.vscode/launch.json vendored Normal file
View File

@@ -0,0 +1,15 @@
{
// 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": [
{
"command": "cds run --with-mocks --in-memory?",
"name": "cds run",
"request": "launch",
"type": "node-terminal",
"skipFiles": [ "<node_internals>/**" ]
}
]
}

7
suppliers/.vscode/settings.json vendored Normal file
View File

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

25
suppliers/.vscode/tasks.json vendored Normal file
View File

@@ -0,0 +1,25 @@
{
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
"version": "2.0.0",
"tasks": [
{
"type": "shell",
"label": "cds watch",
"command": "cds",
"args": ["watch"],
"group": {
"kind": "build",
"isDefault": true
},
"problemMatcher": []
},
{
"type": "shell",
"label": "cds run",
"command": "cds",
"args": ["run", "--with-mocks", "--in-memory?"],
"problemMatcher": []
}
]
}

1
suppliers/index.cds Normal file
View File

@@ -0,0 +1 @@
using from './srv/mashup';

24
suppliers/package.json Normal file
View File

@@ -0,0 +1,24 @@
{
"name": "@capire/suppliers",
"version": "1.0.0",
"description": "Shows integration with S/4, in turn provided as a reusable extension package to bookshop.",
"private": true,
"dependencies": {
"@capire/common": "*",
"@sap/cds": "^4",
"express": "^4"
},
"scripts": {
"start": "cds run --in-memory?",
"watch": "cds watch",
"mocked-s4": "cds mock API_BUSINESS_PARTNER"
},
"cds": {
"requires": {
"API_BUSINESS_PARTNER": {
"kind": "odata",
"model": "srv/external/API_BUSINESS_PARTNER"
}
}
}
}

3
suppliers/server.js Normal file
View File

@@ -0,0 +1,3 @@
const cds = require ('@sap/cds')
cds.once('served', require('./srv/mashup'))
module.exports = cds.server

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,7 @@
BusinessPartner;BusinessPartnerFullName
ACME;A Company Making Everything
B4U;Books for You
S&C;Shakespeare & Co.
WSL;Waterstones
TLD;Thalia
PNG;Penguin Books
1 BusinessPartner BusinessPartnerFullName
2 ACME A Company Making Everything
3 B4U Books for You
4 S&C Shakespeare & Co.
5 WSL Waterstones
6 TLD Thalia
7 PNG Penguin Books

View File

@@ -0,0 +1,5 @@
ID;name
ACME;A Company Making Everything
B4U;Books for You
S&C;Shakespeare & Co.
WSL;Waterstones
1 ID name
2 ACME A Company Making Everything
3 B4U Books for You
4 S&C Shakespeare & Co.
5 WSL Waterstones

47
suppliers/srv/mashup.cds Normal file
View File

@@ -0,0 +1,47 @@
/*
Optionally add projections to external entities, to capture what
you actually want to use from there.
*/
using { API_BUSINESS_PARTNER as S4 } from './external/API_BUSINESS_PARTNER.csn';
extend service S4 with {
entity Suppliers as projection on S4.A_BusinessPartner {
key BusinessPartner as ID,
BusinessPartnerFullName as name,
}
}
/*
You can mashup entities from external services, or projections
thereof, with your project's own entities
*/
using { sap.capire.bookshop.Books } from '@capire/bookshop';
extend Books with {
supplier : Association to S4.Suppliers;
}
/*
You can also expose external entities through your own services
For this to work, you need to delegate the respective calls
addressed to your services into calls to the external service.
*/
extend service AdminService with {
entity Suppliers as projection on S4.Suppliers;
}
/*
Optionally add a local persistence to keep replicas of external
entities to have data in fast access locally; much like a cache.
*/
annotate S4.Suppliers with @cds.persistence:{table,skip:false};
/**
Having locally cached replicas also allows us to display supplier
data in lists of books, which otherwise would generate unwanted
traffic on S4 backends.
*/
extend projection CatalogService.ListOfBooks with {
supplier
}

52
suppliers/srv/mashup.js Normal file
View File

@@ -0,0 +1,52 @@
////////////////////////////////////////////////////////////////////////////
//
// Mashing up provided and required services...
//
module.exports = async()=>{ // called by server.js
if (!cds.services.AdminService) return //> mocking S4 service only
// Connect to services we want to mashup below...
const S4bupa = await cds.connect.to('API_BUSINESS_PARTNER') //> external S4 service
const admin = await cds.connect.to('AdminService') //> local domain service
const db = await cds.connect.to('db') //> our primary database
// Reflect CDS definition of the Suppliers entity
const { Suppliers } = S4bupa.entities
admin.prepend (()=>{
// Delegate Value Help reads for Suppliers to S4 backend
admin.on ('READ', 'Suppliers', async req => {
console.log ('>> delegating to S4 service...')
return await S4bupa.run(req.query)
})
// Replicate Supplier data when Books are edited
admin.on (['CREATE','UPDATE'], 'Books', async (req,next) => {
let { supplier } = req.data
if (supplier) {
let cached = await db.exists (Suppliers, supplier)
if (!cached) await replicate (supplier,'initial')
}
return next()
})
})
// Subscribe to changes in the S4 origin of Suppliers data
S4bupa.on ('BusinessPartner/Changed', async msg => {
let cached = await SELECT('ID').from (Suppliers)
.where ('ID in', msg.businessPartner.KEYS)
for (let each of cached) replicate (each)
})
// Helper function to replicate Suppliers data
async function replicate (ID,_initial) {
let data = await S4bupa.read (Suppliers, ID)
if (_initial) return db.insert (data) .into (Suppliers)
else return db.update (Suppliers,ID) .with (data)
}
}