Improvements for Supplier replication
This commit is contained in:
38
suppliers/monkey-patch.js
Normal file
38
suppliers/monkey-patch.js
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
const deploy = require("@sap/cds/lib/deploy");
|
||||||
|
|
||||||
|
const DEBUG = (...args) => console.log(...args);
|
||||||
|
|
||||||
|
deploy.exclude_external_entities_in = function (csn, _bound) {
|
||||||
|
// NOSONAR
|
||||||
|
for (let [each, { service = each, model, credentials }] of Object.entries(
|
||||||
|
cds.requires
|
||||||
|
)) {
|
||||||
|
if (!model) continue; //> not for internal services like cds.requires.odata
|
||||||
|
if (_bound && !credentials) continue;
|
||||||
|
DEBUG && DEBUG("excluding external entities for", service, "...");
|
||||||
|
const prefix = service + ".";
|
||||||
|
for (let each in csn.definitions) {
|
||||||
|
const def = csn.definitions[each];
|
||||||
|
if (def["@cds.persistence.table"] === true) continue;
|
||||||
|
if (each.startsWith(prefix)) {
|
||||||
|
DEBUG && DEBUG("excluding external entity", each);
|
||||||
|
_exclude(each);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return csn;
|
||||||
|
|
||||||
|
function _exclude(each) {
|
||||||
|
const def = csn.definitions[each];
|
||||||
|
if (def.kind !== "entity") return;
|
||||||
|
def["@cds.persistence.skip"] = true;
|
||||||
|
// propagate to all views...
|
||||||
|
for (let other in csn.definitions) {
|
||||||
|
const d = csn.definitions[other];
|
||||||
|
// do not exclude replica table
|
||||||
|
if (d["@cds.persistence.table"] === true) continue;
|
||||||
|
const p = (d.query && d.query.SELECT) || d.projection;
|
||||||
|
if (p && p.from.ref && p.from.ref[0] === each) _exclude(other);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
27
suppliers/requests.http
Normal file
27
suppliers/requests.http
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
|
||||||
|
@server = http://localhost:4004
|
||||||
|
@authAlice = Authorization: Basic alice:
|
||||||
|
|
||||||
|
PUT {{server}}/api-business-partner/A_BusinessPartner('ACME')
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"BusinessPartnerFullName": "Alice Changed"
|
||||||
|
}
|
||||||
|
|
||||||
|
###
|
||||||
|
|
||||||
|
PATCH {{server}}/admin/Books(201)
|
||||||
|
{{authAlice}}
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"supplier_ID": "PNG"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
###
|
||||||
|
|
||||||
|
GET {{server}}/admin/Books?$top=11&$expand=supplier
|
||||||
|
{{authAlice}}
|
||||||
@@ -1,3 +1,6 @@
|
|||||||
const cds = require ('@sap/cds')
|
const cds = require ('@sap/cds')
|
||||||
|
|
||||||
|
require('./monkey-patch');
|
||||||
|
|
||||||
cds.once('served', require('./srv/mashup'))
|
cds.once('served', require('./srv/mashup'))
|
||||||
module.exports = cds.server
|
module.exports = cds.server
|
||||||
|
|||||||
10
suppliers/srv/external/API_BUSINESS_PARTNER.js
vendored
Normal file
10
suppliers/srv/external/API_BUSINESS_PARTNER.js
vendored
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
const cds = require('@sap/cds');
|
||||||
|
|
||||||
|
module.exports = cds.service.impl(function (srv) {
|
||||||
|
const { A_BusinessPartner } = this.entities;
|
||||||
|
|
||||||
|
srv.after('UPDATE', A_BusinessPartner, data => {
|
||||||
|
console.log(`>>> BusinessPartner updated ${data.BusinessPartner}`);
|
||||||
|
srv.emit("BusinessPartners/Changed", { businessPartners: [ data.BusinessPartner ] });
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -16,10 +16,22 @@ extend service S4 with {
|
|||||||
// CityName as name
|
// CityName as name
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
// to_BusinessPartnerAddress
|
||||||
|
|
||||||
// REVISIT: following is not supported so far in cqn2odata...
|
// REVISIT: following is not supported so far in cqn2odata...
|
||||||
// to_BusinessPartnerAddress.CityCode as city,
|
// to_BusinessPartnerAddress.CityCode as city,
|
||||||
// to_BusinessPartnerAddress.CityName as city_name,
|
// to_BusinessPartnerAddress.CityName as city_name,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// REVISIT: Alternative idea to use a specific replication view, but request data from
|
||||||
|
// a different view and manual map values.
|
||||||
|
// entity ReplicatedSuppliers as projection on Suppliers {
|
||||||
|
// ID,
|
||||||
|
// name,
|
||||||
|
// to_BusinessPartnerAddress.CityCode as city,
|
||||||
|
// to_BusinessPartnerAddress.CityName as city_name
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -23,13 +23,21 @@ module.exports = async()=>{ // called by server.js
|
|||||||
})
|
})
|
||||||
|
|
||||||
// Replicate Supplier data when edited Books have suppliers
|
// Replicate Supplier data when edited Books have suppliers
|
||||||
admin.on (['CREATE','UPDATE'], 'Books', ({data:{supplier}}, next) => {
|
admin.on (['CREATE','UPDATE'], 'Books', async ({data:{supplier_ID: supplierId}}, next) => {
|
||||||
// Using Promise.all(...) to parallelize local write, i.e. next(), and replication
|
// Using Promise.all(...) to parallelize local write, i.e. next(), and replication
|
||||||
if (supplier) return Promise.all ([ next(), async()=>{
|
|
||||||
let replicated = await db.exists (Suppliers, supplier)
|
/*
|
||||||
if (!replicated) await replicate (supplier, 'initial')
|
// ERROR: Reference integrity is violated for association "supplier"
|
||||||
|
if (supplierId) return Promise.all ([ next(), async()=>{
|
||||||
|
let replicated = await db.exists (Suppliers, supplierId)
|
||||||
|
if (!replicated) await replicate (supplierId, 'initial')
|
||||||
}])
|
}])
|
||||||
else return next() //> don't forget to pass down the interceptor stack
|
else return next() //> don't forget to pass down the interceptor stack
|
||||||
|
*/
|
||||||
|
|
||||||
|
let replicated = await db.exists (Suppliers, supplierId);
|
||||||
|
if (!replicated) await replicate (supplierId, 'initial');
|
||||||
|
return next();
|
||||||
})
|
})
|
||||||
|
|
||||||
})
|
})
|
||||||
@@ -47,7 +55,10 @@ module.exports = async()=>{ // called by server.js
|
|||||||
*/
|
*/
|
||||||
async function replicate (IDs,_initial) {
|
async function replicate (IDs,_initial) {
|
||||||
if (!Array.isArray(IDs)) IDs = [ IDs ]
|
if (!Array.isArray(IDs)) IDs = [ IDs ]
|
||||||
let suppliers = await S4bupa.read (Suppliers).where('ID in',IDs)
|
// TODO: Doesn't work when running in same process with mocked API_BUSINESS_PARTNER
|
||||||
|
|
||||||
|
let suppliers = await S4bupa.read (Suppliers).where(...([[]].concat(IDs).reduce( (where, id, index ) => { where.push(`${index>1 ? "OR ":""}ID = `, id); return where })));
|
||||||
|
//let suppliers = await S4bupa.read (Suppliers).where('ID in',IDs)
|
||||||
if (_initial) return db.insert (suppliers) .into (Suppliers) //> using bulk insert
|
if (_initial) return db.insert (suppliers) .into (Suppliers) //> using bulk insert
|
||||||
else return Promise.all(suppliers.map ( //> parallelizing updates
|
else return Promise.all(suppliers.map ( //> parallelizing updates
|
||||||
each => db.update (Suppliers,each.ID) .with (each)
|
each => db.update (Suppliers,each.ID) .with (each)
|
||||||
|
|||||||
Reference in New Issue
Block a user