Currently the "Developing with SAP Extension Suite" Learning Journey has some limitations, because code snippets required in some of the exercises are yet to be included. This blog post provides the snippets for the individual exercises in unit 1 to unit 3 until they are included in the official Learning Journey.
See also Code snippets for the "Developing with SAP Extension Suite" Learning Journey Unit 4 to Unit 6
schema.cds file
namespace riskmanagement;
using { managed } from '@sap/cds/common';
entity Risks : managed {
key ID : UUID @(Core.Computed : true);
title : String(100);
owner : String;
prio : String(5);
descr : String;
miti : Association to Mitigations;
impact : Integer;
//bp : Association to BusinessPartners;
// You will need this definition in a later step
criticality : Integer;
}
entity Mitigations : managed {
key ID : UUID @(Core.Computed : true);
descr : String;
owner : String;
timeline : String;
risks : Association to many Risks on risks.miti = $self;
}
risk-service.cds file
using { riskmanagement as rm } from '../db/schema';
@path: 'service/risk'
service RiskService {
entity Risks as projection on rm.Risks;
annotate Risks with @odata.draft.enabled;
entity Mitigations as projection on rm.Mitigations;
annotate Mitigations with @odata.draft.enabled;
//@readonly entity BusinessPartners as projection on rm.BusinessPartners;
}
riskmanagement-Risks.csv file
ID;createdAt;createdBy;title;owner;prio;descr;miti_id;impact;
20466922-7d57-4e76-b14c-e53fd97dcb11;2019-10-24;SYSTEM;CFR non-compliance;Fred Fish;3;Recent restructuring might violate CFR code 71;20466921-7d57-4e76-b14c-e53fd97dcb11;10000;
20466922-7d57-4e76-b14c-e53fd97dcb12;2019-10-24;SYSTEM;SLA violation with possible termination cause;George Gung;2;Repeated SAL violation on service delivery for two successive quarters;20466921-7d57-4e76-b14c-e53fd97dcb12;90000;
20466922-7d57-4e76-b14c-e53fd97dcb13;2019-10-24;SYSTEM;Shipment violating export control;Herbert Hunter;1;Violation of export and trade control with unauthorized downloads;20466921-7d57-4e76-b14c-e53fd97dcb13;200000;
riskmanagement-Mitigations.csv file
ID;createdAt;createdBy;descr;owner;timeline
20466921-7d57-4e76-b14c-e53fd97dcb11;2019-10-24;SYSTEM;SLA violation: authorize account manager to offer service credits for recent delivery issues;suitable BuPa;Q2 2020
20466921-7d57-4e76-b14c-e53fd97dcb12;2019-10-24;SYSTEM;"SLA violation: review third party contractors to ease service delivery challenges; trigger budget review";suitable BuPa;Q3 2020
20466921-7d57-4e76-b14c-e53fd97dcb13;2019-10-24;SYSTEM;Embargo violation: investigate source of shipment request, revoke authorization;SFSF Employee with link possible?;29.03.2020
20466921-7d57-4e76-b14c-e53fd97dcb14;2019-10-24;SYSTEM;Embargo violation: review shipment proceedure and stop delivery until further notice;SFSF Employee with link possible?;01.03.2020
common.cds file
using riskmanagement as rm from '../db/schema';
// Annotate Risk elements
annotate rm.Risks with {
ID @title : 'Risk';
title @title : 'Title';
owner @title : 'Owner';
prio @title : 'Priority';
descr @title : 'Description';
miti @title : 'Mitigation';
impact @title : 'Impact';
}
// Annotate Miti elements
annotate rm.Mitigations with {
ID @(
UI.Hidden,
Commong : {Text : descr}
);
owner @title : 'Owner';
descr @title : 'Description';
}
annotate rm.Risks with {
miti @(Common : {
//show text, not id for mitigation in the context of risks
Text : miti.descr,
TextArrangement : #TextOnly,
ValueList : {
Label : 'Mitigations',
CollectionPath : 'Mitigations',
Parameters : [
{
$Type : 'Common.ValueListParameterInOut',
LocalDataProperty : miti_ID,
ValueListProperty : 'ID'
},
{
$Type : 'Common.ValueListParameterDisplayOnly',
ValueListProperty : 'descr'
}
]
}
});
}
Include common.cds file into services.cds file
using from './risks/annotations';
using from './common';
UI annotations in apps/risks/annotations.cds file
using RiskService from '../../srv/risk-service';
// Risk List Report Page
annotate RiskService.Risks with @(UI : {
HeaderInfo : {
TypeName : 'Risk',
TypeNamePlural : 'Risks',
Title : {
$Type : 'UI.DataField',
Value : title
},
Description : {
$Type : 'UI.DataField',
Value : descr
}
},
SelectionFields : [prio],
Identification : [{Value : title}],
// Define the table columns
LineItem : [
{Value : title},
{Value : miti_ID},
{Value : owner},
{
Value : prio,
Criticality : criticality
},
{
Value : impact,
Criticality : criticality
},
],
});
// Risk Object Page
annotate RiskService.Risks with @(UI : {
Facets : [{
$Type : 'UI.ReferenceFacet',
Label : 'Main',
Target : '@UI.FieldGroup#Main',
}],
FieldGroup #Main : {Data : [
{Value : miti_ID},
{Value : owner},
{
Value : prio,
Criticality : criticality
},
{
Value : impact,
Criticality : criticality
}
]},
});
risk-service.js file
// Imports
const cds = require("@sap/cds");
/**
* The service implementation with all service handlers
*/
module.exports = cds.service.impl(async function () {
// Define constants for the Risk and BusinessPartners entities from the risk-service.cds file
const { Risks, BusinessPartners } = this.entities;
/**
* Set criticality after a READ operation on /risks
*/
this.after("READ", Risks, (data) => {
const risks = Array.isArray(data) ? data : [data];
risks.forEach((risk) => {
if (risk.impact >= 100000) {
risk.criticality = 1;
} else {
risk.criticality = 2;
}
});
});
});
Additional code for db/schema.cds file
// using an external service from S/4
using { API_BUSINESS_PARTNER as external } from '../srv/external/API_BUSINESS_PARTNER.csn';
entity BusinessPartners as projection on external.A_BusinessPartner {
key BusinessPartner,
LastName,
FirstName
}
Additional lines for package.json file
"credentials": {
"url": "https://sandbox.api.sap.com/s4hanacloud/sap/opu/odata/sap/API_BUSINESS_PARTNER/"
}
Additional code for risk-service.js file
// connect to remote service
const BPsrv = await cds.connect.to("API_BUSINESS_PARTNER");
/**
* Event-handler for read-events on the BusinessPartners entity.
* Each request to the API Business Hub requires the apikey in the header.
*/
this.on("READ", BusinessPartners, async (req) => {
// The API Sandbox returns alot of business partners with empty names.
// We don't want them in our application
req.query.where("LastName <> '' and FirstName <> '' ");
return await BPsrv.transaction(req).send({
query: req.query,
headers: {
apikey: process.env.apikey,
},
});
});
New riskmanagement-Risks.csv file content
ID;createdAt;createdBy;title;owner;prio;descr;miti_id;impact;bp_BusinessPartner
20466922-7d57-4e76-b14c-e53fd97dcb11;2019-10-24;SYSTEM;CFR non-compliance;Fred Fish;3;Recent restructuring might violate CFR code 71;20466921-7d57-4e76-b14c-e53fd97dcb11;10000;9980000448
20466922-7d57-4e76-b14c-e53fd97dcb12;2019-10-24;SYSTEM;SLA violation with possible termination cause;George Gung;2;Repeated SAL violation on service delivery for two successive quarters;20466921-7d57-4e76-b14c-e53fd97dcb12;90000;9980002245
20466922-7d57-4e76-b14c-e53fd97dcb13;2019-10-24;SYSTEM;Shipment violating export control;Herbert Hunter;1;Violation of export and trade control with unauthorized downloads;20466921-7d57-4e76-b14c-e53fd97dcb13;200000;9980000230
New app/common.cds file content
Note: Lines starting with //### BEGIN OF INSERT and ending with //### END OF INSERT have been added to the previous version of the file.
using riskmanagement as rm from '../db/schema';
// Annotate Risk elements
annotate rm.Risks with {
ID @title : 'Risk';
title @title : 'Title';
owner @title : 'Owner';
prio @title : 'Priority';
descr @title : 'Description';
miti @title : 'Mitigation';
impact @title : 'Impact';
//### BEGIN OF INSERT
bp @title : 'Business Partner';
//### END OF INSERT
criticality @title : 'Criticality';
}
// Annotate Miti elements
annotate rm.Mitigations with {
ID @(
UI.Hidden,
Commong : {Text : descr}
);
owner @title : 'Owner';
descr @title : 'Description';
}
//### BEGIN OF INSERT
annotate rm.BusinessPartners with {
BusinessPartner @(
UI.Hidden,
Common : {Text : LastName}
);
LastName @title : 'Last Name';
FirstName @title : 'First Name';
}
//### END OF INSERT
annotate rm.Risks with {
miti @(Common : {
//show text, not id for mitigation in the context of risks
Text : miti.descr,
TextArrangement : #TextOnly,
ValueList : {
Label : 'Mitigations',
CollectionPath : 'Mitigations',
Parameters : [
{
$Type : 'Common.ValueListParameterInOut',
LocalDataProperty : miti_ID,
ValueListProperty : 'ID'
},
{
$Type : 'Common.ValueListParameterDisplayOnly',
ValueListProperty : 'descr'
}
]
}
});
//### BEGIN OF INSERT
bp @(Common : {
Text : bp.LastName,
TextArrangement : #TextOnly,
ValueList : {
Label : 'Business Partners',
CollectionPath : 'BusinessPartners',
Parameters : [
{
$Type : 'Common.ValueListParameterInOut',
LocalDataProperty : bp_BusinessPartner,
ValueListProperty : 'BusinessPartner'
},
{
$Type : 'Common.ValueListParameterDisplayOnly',
ValueListProperty : 'LastName'
},
{
$Type : 'Common.ValueListParameterDisplayOnly',
ValueListProperty : 'FirstName'
}
]
}
})
//### END OF INSERT
}
Line that needs to be added in app/risks/annotations.cds in LineItem array and FieldGroup - Data array
{Value : bp_BusinessPartner},
New event handler to be added in srv/risk-service.js file
/**
* Event-handler on risks.
* Retrieve BusinessPartner data from the external API
*/
this.on("READ", Risks, async (req, next) => {
/*
Check whether the request wants an "expand" of the business partner
As this is not possible, the risk entity and the business partner entity are in different systems (SAP BTP and S/4 HANA Cloud),
if there is such an expand, remove it
*/
if (!req.query.SELECT.columns) return next();
const expandIndex = req.query.SELECT.columns.findIndex(
({ expand, ref }) => expand && ref[0] === "bp"
);
console.log(req.query.SELECT.columns);
if (expandIndex < 0) return next();
req.query.SELECT.columns.splice(expandIndex, 1);
if (
!req.query.SELECT.columns.find((column) =>
column.ref.find((ref) => ref == "bp_BusinessPartner")
)
) {
req.query.SELECT.columns.push({ ref: ["bp_BusinessPartner"] });
}
/*
Instead of carrying out the expand, issue a separate request for each business partner
This code could be optimized, instead of having n requests for n business partners, just one bulk request could be created
*/
try {
const res = await next();
await Promise.all(
res.map(async (risk) => {
const bp = await BPsrv.transaction(req).send({
query: SELECT.one(this.entities.BusinessPartners)
.where({ BusinessPartner: risk.bp_BusinessPartner })
.columns(["BusinessPartner", "LastName", "FirstName"]),
headers: {
apikey: process.env.apikey,
},
});
risk.bp = bp;
})
);
} catch (error) {}
});
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
User | Count |
---|---|
5 | |
2 | |
2 | |
2 | |
2 | |
1 | |
1 | |
1 |