From 54d0c8b35d9b7760882cf90c5cbc05ce39451402 Mon Sep 17 00:00:00 2001 From: Daniel Date: Tue, 17 Dec 2019 12:30:13 +0100 Subject: [PATCH] simplified models and impl -> requires latest snapshots --- packages/bookshop/srv/admin-service.js | 71 +++++++++++++++----------- packages/bookshop/srv/external.cds | 27 ++++------ 2 files changed, 50 insertions(+), 48 deletions(-) diff --git a/packages/bookshop/srv/admin-service.js b/packages/bookshop/srv/admin-service.js index cfd711d6..f3d0e71d 100644 --- a/packages/bookshop/srv/admin-service.js +++ b/packages/bookshop/srv/admin-service.js @@ -2,60 +2,62 @@ const cds = require('@sap/cds') // We are mashing up three services... const admin = cds.connect.to ('AdminService') -const bupa = cds.connect.to('API_BUSINESS_PARTNER') -const db = cds.connect.to('db') +const bupa = cds.connect.to ('API_BUSINESS_PARTNER') +const db = cds.connect.to ('db') + +// Using reflected definitions from connected services/database +const { Addresses: externalAddresses } = bupa.entities // projection on external addresses +const { Books, Addresses } = db.entities // entities in local database -// Reflected entities for local database -const { Books, Addresses } = db.entities -// Fetch current user's addresses from S/4 for ValueHelp. module.exports = (admin => { - admin.on ('READ', 'usersAddresses', async (req) => { - // const UsersAddresses = req.query.from (Addresses) .where ({ BusinessPartner: req.user.id }) - // FIXME: Again that absolutely useless error message: - // [2019-12-16T20:30:14.106Z | ERROR | 1940862]: The server does not support the functionality required to fulfill the request - // FIXME: Even worse: click Orders Edit -> - // [2019-12-16T20:38:52.918Z | WARNING | 1575675]: Not Found - const { A_BusinessPartnerAddress:Addresses } = bupa.entities - const UsersAddresses = SELECT.from (Addresses, a => { - a.AddressID.as('ID'), - a.BusinessPartner, - a.Country.as('country'), - a.CityName.as('cityName'), - a.PostalCode.as('postalCode'), - a.StreetName.as('streetName'), - a.HouseNumber.as('houseNumber') - }) .where ({ BusinessPartner: req.user.id }) - return bupa.transaction(req) .run (UsersAddresses) // TODO: I'd like to write .read instead of .run + // Handler to delegate ValueHelp requests to S/4 backend, fetching current user's addresses from there + admin.on ('READ', 'Addresses', (req) => { + const { SELECT } = cds.ql(req) //> convenient alternative to bupa.transaction(req).run(SELECT...) + return SELECT.from (externalAddresses) .where ({ BusinessPartner: req.user.id || 'anonymous' }) + //> this is applying projection generically, i.e. the equivalent of: + // const { A_BusinessPartnerAddress } = bupa.entities + // return SELECT.from (A_BusinessPartnerAddress, a => { + // a.AddressID.as('ID'), + // a.BusinessPartner, + // a.Country.as('country'), + // a.CityName.as('cityName'), + // a.PostalCode.as('postalCode'), + // a.StreetName.as('streetName'), + // a.HouseNumber.as('houseNumber') + // }) .where ({ BusinessPartner: req.user.id }) }) }) + + // Replicate chosen addresses from S/4 when filing orders. admin.before ('PATCH', 'Orders', async (req) => { const ID = req.data.shippingAddress_ID; if (!ID) return //> something else - const address = await bupa.tx(req) .run ( - SELECT.one.from(Addresses).where({ - ID, BusinessPartner: req.user.id - }) - ) - if (address) return db.tx(req) .upsert (Addresses) .entries (address) + const { SELECT, UPSERT } = cds.ql(req) //> convenient alternative to .transaction(req).run(SELECT...) + const address = await SELECT.one.from(externalAddresses).where({ + ID, BusinessPartner: req.user.id + }) + if (address) return UPSERT (Addresses) .entries (address) }) + // Update local replicas when sources change in S/4. bupa.on ('BusinessPartner/Changed', async (msg) => { console.log('>> received:', msg.data) - const BusinessPartner = msg.data.KEY[0].BUSINESSPARTNER //> .KEY[0] >> revisit w/ Oliver + const BusinessPartner = msg.data.KEY[0].BUSINESSPARTNER // TODO: .KEY[0] >> revisit w/ Oliver + const { SELECT, UPDATE } = cds.ql(msg) //> convenient alternative to .transaction(req).run(SELECT...) // fetch affected entries from local replicas const local = db.transaction (msg) - const replicas = await local.read (Addresses) .where ({BusinessPartner}) + const replicas = await SELECT.from (Addresses) .where ({BusinessPartner}) // skip if not affected if (replicas.length === 0) return // fetch changed data from S/4 -> might be less than local due to deletes - const changed = await bupa.tx(msg).read (Addresses) .where ({ + const changed = await SELECT.from (externalAddresses) .where ({ BusinessPartner, ID: replicas.map(a => a.ID) // where in }) @@ -66,6 +68,7 @@ bupa.on ('BusinessPartner/Changed', async (msg) => { }) + // Validate incoming orders and reduce books' stocks. admin.before ('CREATE', 'Orders', async (req) => { @@ -78,6 +81,11 @@ admin.before ('CREATE', 'Orders', async (req) => { 'Please enter a valid shpping address.', 'shippingAddress_ID' ) + // TODO: future way of doing that: + // const {assert} = req + // assert ('Items') .check (items => items && items.length, 'Please enter at least one order item') + // assert ('shippingAddress') .mandatory() .exists() + // if (req.hasErrors) return // reduce stock on ordered books... const all = await db.tx(req) .run (Items.map (each => @@ -91,6 +99,7 @@ admin.before ('CREATE', 'Orders', async (req) => { }) + // eslint-disable-next-line no-unused-vars function _diff (a,b) { let any, diff={} diff --git a/packages/bookshop/srv/external.cds b/packages/bookshop/srv/external.cds index b8805102..e6de79ba 100644 --- a/packages/bookshop/srv/external.cds +++ b/packages/bookshop/srv/external.cds @@ -8,6 +8,7 @@ extend service API_BUSINESS_PARTNER with { /** * Simplified view on external addresses */ + @mashup @cds.autoexpose //> for ValueHelps entity Addresses as projection on external.A_BusinessPartnerAddress { key AddressID as ID, key BusinessPartner, @@ -21,12 +22,12 @@ extend service API_BUSINESS_PARTNER with { /** * Re-modelling the event which is currently not available declaratively from S/4 */ - // @messaging.topic:'sap/S4HANAOD/c532/BO/BusinessPartner/Changed' - // event "BusinessPartner/Changed" { - // "KEY": array of { - // BUSINESSPARTNER : external.A_BusinessPartner.BusinessPartner - // } - // } + // @messaging.topic:'${prefix}/BusinessPartner/Changed' + event "BusinessPartner/Changed" { + "KEY": array of { + BUSINESSPARTNER : external.A_BusinessPartner.BusinessPartner + } + } } @@ -35,13 +36,14 @@ extend service API_BUSINESS_PARTNER with { */ using { AdminService } from './admin-service'; extend service AdminService { - entity usersAddresses as projection on bookshop.Addresses; + // entity usersAddresses as projection on external.Addresses; } +// TODO: not used so far... using { CatalogService } from './cat-service'; extend service CatalogService { @readonly @requires:'authenticated-user' - entity usersAddresses as projection on bookshop.Addresses; + entity usersAddresses as projection on external.Addresses; } @@ -65,12 +67,3 @@ extend bookshop.Orders with { entity sap.capire.bookshop.Addresses as SELECT from external.Addresses { *, false as tombstone : Boolean }; -// entity sap.capire.bookshop.Addresses as SELECT from external.A_BusinessPartnerAddress { -// key AddressID as ID, -// key BusinessPartner, -// Country as country, -// CityName as cityName, -// PostalCode as postalCode, -// StreetName as streetName, -// HouseNumber as houseNumber -// };