SAP Full-Stack CAP app BTP environment
https://developers.sap.com/group.cap-application-full-stack.html
https://developers.sap.com/group.deploy-full-stack-cap-application.html
https://developers.sap.com/group.fiori-tools-odata-v4-incident.html
1. Creare a CAP Project
cd projects
cds init incident-management
open folder /home/user/projects/incident-management
2. Add data model schema.cds under /db
Example:
using { cuid, managed, sap.common.CodeList } from ‘@sap/cds/common’;
namespace sap.capire.incidents;
/**
* Incidents created by Customers.
*/
entity Incidents : cuid, managed {
customer : Association to Customers;
title : String @title : ‘Title’;
urgency : Association to Urgency default ‘M’;
status : Association to Status default ‘N’;
conversation : Composition of many {
key ID : UUID;
timestamp : type of managed:createdAt;
author : type of managed:createdBy;
message : String;
};
}
/**
* Customers entitled to create support Incidents.
*/
entity Customers : managed {
key ID : String;
firstName : String;
lastName : String;
name : String = firstName ||’ ‘|| lastName;
email : EMailAddress;
phone : PhoneNumber;
incidents : Association to many Incidents on incidents.customer = $self;
creditCardNo : String(16) @assert.format: ‘^[1-9]\d{15}$’;
addresses : Composition of many Addresses on addresses.customer = $self;
}
entity Addresses : cuid, managed {
customer : Association to Customers;
city : String;
postCode : String;
streetAddress : String;
}
entity Status : CodeList {
key code: String enum {
new = ‘N’;
assigned = ‘A’;
in_process = ‘I’;
on_hold = ‘H’;
resolved = ‘R’;
closed = ‘C’;
};
criticality : Integer;
}
entity Urgency : CodeList {
key code: String enum {
high = ‘H’;
medium = ‘M’;
low = ‘L’;
};
}
type EMailAddress : String;
type PhoneNumber : String;
3. Define the service services.cds under /srv
Example:
using { sap.capire.incidents as my } from ‘../db/schema’;
/**
* Service used by support personell, i.e. the incidents’ ‘processors’.
*/
service ProcessorService {
entity Incidents as projection on my.Incidents;
@readonly
entity Customers as projection on my.Customers;
}
annotate ProcessorService.Incidents with @odata.draft.enabled;
/**
* Service used by administrators to manage customers and incidents.
*/
service AdminService {
entity Customers as projection on my.Customers;
entity Incidents as projection on my.Incidents;
}
4. Add initial data
cds add data
db/data/sap-capire.incidents-Adresses.csv
ID,customer_ID,city,postCode,streetAddress
17e00347-dc7e-4ca9-9c5d-06ccef69f064,1004155,Rome,00164,Piazza Adriana
d8e797d9-6507-4aaa-b43f-5d2301df5135,1004161,Munich,80809,Olympia Park
ff13d2fa-e00f-4ee5-951c-3303f490777b,1004100,Walldorf,69190,Dietmar-Hopp-Allee
db/data/sap-capire.incidents-Customers.csv
ID,firstName,lastName,email,phone
1004155,Daniel,Watts,daniel.watts@demo.com,+39-555-123
1004161,Stormy,Weathers,stormy.weathers@demo.com,+49-020-022
1004100,Sunny,Sunshine,sunny.sunshine@demo.com,+49-555-789
db/data/sap-capire.incidents-Incidents.csv
ID,customer_ID,title,urgency_code,status_code
3b23bb4b-4ac7-4a24-ac02-aa10cabd842c,1004155,Inverter not functional,H,C
3a4ede72-244a-4f5f-8efa-b17e032d01ee,1004161,No current on a sunny day,H,N
3ccf474c-3881-44b7-99fb-59a2a4668418,1004161,Strange noise when switching off Inverter,M,N
3583f982-d7df-4aad-ab26-301d4a157cd7,1004100,Solar panel broken,H,I
db/data/sap-capire.incidents-Status.csv
code;descr;criticality
N;New;3
A;Assigned;2
I;In Process;2
H;On Hold;3
R;Resolved;2
C;Closed;4
db/data/sap-capire.incidents-Urgency.csv
code;descr
H;High
M;Medium
L;Low
5. Add custom logic
Create a file services.js under folder /srv
const cds = require(‘@sap/cds’)
class ProcessorService extends cds.ApplicationService {
/** Registering custom event handlers */
init() {
this.before(“UPDATE”, “Incidents”, (req) => this.onUpdate(req));
this.before(“CREATE”, “Incidents”, (req) => this.changeUrgencyDueToSubject(req.data));
return super.init();
}
changeUrgencyDueToSubject(data) {
if (data) {
const incidents = Array.isArray(data) ? data : [data];
incidents.forEach((incident) => {
if (incident.title?.toLowerCase().includes(“urgent”)) {
incident.urgency = { code: “H”, descr: “High” };
}
});
}
}
/** Custom Validation */
async onUpdate (req) {
const { status_code } = await SELECT.one(req.subject, i => i.status_code).where({ID: req.data.ID})
if (status_code === ‘C’)
return req.reject(`Can’t modify a closed incident`)
}
}
module.exports = { ProcessorService }
6. Add Authorization
Specify restrictions, add lines to the srv/service.cds file
annotate AdminService with @(requires: ‘admin’);
annotate ProcessorService with @(requires: ‘support’);
Add user for local testing in package.json
“cds”: {
“requires”: {
“[development]”: {
“auth”: {
“kind”: “mocked”,
“users”: {
“admin”: {
“password”: “initial”,
“roles”: [“support”]
},
“alice”: {
“roles”: [“support”]
},
“bob”: {
“roles”: [“support”]
}
}
}
}
}
https://cap.cloud.sap/docs/node.js/authentication
7. Add SAP HANA Cloud
cds add hana –for production
{
“name”: “incident-management”,
“dependencies”: {
…
“@cap-js/hana”: “^x”
},
…
“cds”: {
“requires”: {
…
“[production]”: {
“db”: “hana”
}
},
“sql”: {
“native_hana_associations”: false
}
}
}
cds env requires -4 production
{
middlewares: true,
auth: { kind: ‘jwt’, vcap: { label: ‘xsuaa’ } },
db: { impl: ‘@sap/cds/libx/_runtime/hana/Service.js’, kind: ‘hana’ }
}
cds add xsuaa –for production
{
“name”: “incident-management”,
“dependencies”: {
…
“@sap/xssec”: “^x”
},
…
“cds”: {
“requires”: {
…
“[production]”: {
“db”: “hana”,
“auth”: “xsuaa”
}
…
}
}
}
cds add html5-repo
{
“name”: “incident-management”,
“dependencies”: {
…
},
…
“cds”: {
“requires”: {
…
“[production]”: {
…
},
“html5-repo”: true
}
…
}
}
npm install
cds build –production
[cds] – build completed in 511 ms