← Tous les articles

Votre premier service SAP CAP : de zéro à Fiori en 30 minutes

Nous reconstruisons l'application de demande d'équipement de l'article RAP — cette fois avec SAP Cloud Application Programming Model (CAP), Node.js et OData V4 — puis nous la déployons sur BTP.

CAPBTPCDSNode.jsOData V4

Dans l'article RAP, nous avons construit une application de demande d'équipement sur la pile ABAP. Ici, nous construisons la même application avec CAP, le Cloud Application Programming Model de SAP, en Node.js. Même métier, autre runtime : une bonne façon de voir où chaque modèle excelle.

RAP ou CAP ?

RAP CAP
S'exécute sur ABAP (S/4HANA, environnement ABAP de BTP) Node.js ou Java (BTP Cloud Foundry / Kyma)
Modèle de données CDS view entities + tables DDIC Entités CDS (base générée)
Logique Behavior pool ABAP, EML Handlers d'événements JavaScript/Java
Usage typique Extensions proches des données S/4 Applications side-by-side, nouveaux produits, scénarios multi-systèmes

Aucun n'est « meilleur » : RAP s'impose quand la logique appartient à S/4HANA, CAP convient aux extensions side-by-side sur BTP.

1. Créer le projet

Il faut Node.js (LTS) et les outils CAP. VS Code comme SAP Business Application Studio conviennent.

terminal.sh
# once per machine
npm install -g @sap/cds-dk

# new project
cds init equipment-requests
cd equipment-requests
npm install

# start with live reload (in-memory SQLite + sample data)
cds watch

2. Modèle de données

db/schema.cds décrit les données. Les aspects cuid et managed ajoutent une clé UUID et les champs de création/modification — l'équivalent CAP des champs administratifs écrits à la main en RAP.

schema.cds
namespace sap.equipment;

using { cuid, managed, Currency } from '@sap/cds/common';

entity Requests : cuid, managed {
  requestNo     : Integer @readonly;
  description   : String(80) @mandatory;
  equipmentType : String(20) @mandatory;
  estimatedCost : Decimal(15, 2);
  currency      : Currency;
  status        : String(1) default 'O' @readonly enum {
    Open     = 'O';
    Approved = 'A';
    Rejected = 'X';
  };
}

Les données d'exemple vont dans db/data/sap.equipment-Requests.csv ; CAP les charge automatiquement :

sap.equipment-Requests.csv
ID;requestNo;description;equipmentType;estimatedCost;currency_code;status
6f1c2a90-1b3e-4c55-9d7a-0e7f5b1a2c01;1;Laptop for new developer;Laptop;1450.00;EUR;A
6f1c2a90-1b3e-4c55-9d7a-0e7f5b1a2c02;2;Ergonomic keyboard;Accessory;89.00;EUR;X
6f1c2a90-1b3e-4c55-9d7a-0e7f5b1a2c03;3;Docking station for meeting room;Docking;320.00;EUR;O

3. Définition du service

srv/service.cds expose une projection comme service OData V4. @odata.draft.enabled apporte la même gestion de brouillon que with draft en RAP, et les deux actions liées sont déclarées directement sur l'entité.

service.cds
using { sap.equipment as db } from '../db/schema';

service EquipmentService @(path: '/equipment') {
  @odata.draft.enabled
  entity Requests as projection on db.Requests actions {
    action approve() returns Requests;
    action reject()  returns Requests;
  };
}

4. Logique métier

srv/service.js enregistre des handlers d'événements — l'équivalent CAP des déterminations, validations et implémentations d'actions :

service.js
const cds = require('@sap/cds');

module.exports = class EquipmentService extends cds.ApplicationService {
  init() {
    const { Requests } = this.entities;

    // Next request number when a draft is activated (demo: MAX + 1)
    this.before('CREATE', Requests, async (req) => {
      const { max } = await SELECT.one`max(requestNo) as max`.from(Requests);
      req.data.requestNo = (max ?? 0) + 1;
    });

    // Validation – the message is attached to the field in Fiori
    this.before(['CREATE', 'UPDATE'], Requests, (req) => {
      const { estimatedCost } = req.data;
      if (estimatedCost !== undefined && estimatedCost <= 0) {
        req.error(400, 'Estimated cost must be greater than zero', 'estimatedCost');
      }
    });

    this.on('approve', Requests, (req) => this.setStatus(req, 'A'));
    this.on('reject', Requests, (req) => this.setStatus(req, 'X'));

    return super.init();
  }

  async setStatus(req, status) {
    const { Requests } = this.entities;
    const [{ ID }] = req.params;
    const request = await SELECT.one.from(Requests, ID).columns('status');
    if (!request) return req.reject(404, 'Request not found');
    if (request.status !== 'O') return req.reject(409, 'Only open requests can be approved or rejected');

    await UPDATE(Requests, ID).with({ status });
    return SELECT.one.from(Requests, ID);
  }
};
  • before('CREATE') → comme une détermination à l'enregistrement : attribue le numéro suivant.
  • before(['CREATE','UPDATE']) → validation ; req.error rattache le message au champ.
  • on('approve') / on('reject') → implémentations d'actions avec contrôle du statut.

5. Lancer l'application

Lancez cds watch. CAP compile le modèle, crée une base SQLite en mémoire, charge le CSV et sert le service — avec rechargement à chaque sauvegarde.

cds watch : modèle chargé, données déployées, serveur à l'écoute sur le port 4004
cds watch : modèle chargé, données déployées, serveur à l'écoute sur le port 4004

Ouvrez http://localhost:4004. La page d'accueil liste les endpoints et un lien Fiori preview.

La page d'accueil CAP et la réponse OData
La page d'accueil CAP et la réponse OData

Pour tester sans interface, un fichier .http avec l'extension REST Client est très pratique :

requests.http
### List requests
GET http://localhost:4004/odata/v4/equipment/Requests?$filter=IsActiveEntity eq true

### Create a draft
POST http://localhost:4004/odata/v4/equipment/Requests
Content-Type: application/json

{ "description": "Second monitor", "equipmentType": "Monitor", "estimatedCost": 289.90, "currency_code": "EUR" }

### Activate the draft (replace <ID>)
POST http://localhost:4004/odata/v4/equipment/Requests(ID=<ID>,IsActiveEntity=false)/EquipmentService.draftActivate
Content-Type: application/json

{}

### Approve (replace <ID>)
POST http://localhost:4004/odata/v4/equipment/Requests(ID=<ID>,IsActiveEntity=true)/EquipmentService.approve
Content-Type: application/json

{}

6. Annotations Fiori Elements

Comme la metadata extension en RAP, les annotations UI vivent dans un fichier séparé, app/annotations.cds :

annotations.cds
using EquipmentService as service from '../srv/service';

annotate service.Requests with @(
  UI.HeaderInfo      : {
    TypeName       : 'Equipment Request',
    TypeNamePlural : 'Equipment Requests',
    Title          : { Value: description },
    Description    : { Value: requestNo }
  },
  UI.SelectionFields : [ requestNo, equipmentType, status ],
  UI.LineItem        : [
    { Value: requestNo,     Label: 'Request No.' },
    { Value: description,   Label: 'Description' },
    { Value: equipmentType, Label: 'Equipment Type' },
    { Value: estimatedCost, Label: 'Estimated Cost' },
    { Value: status,        Label: 'Status' },
    { $Type: 'UI.DataFieldForAction', Action: 'EquipmentService.approve', Label: 'Approve' },
    { $Type: 'UI.DataFieldForAction', Action: 'EquipmentService.reject',  Label: 'Reject' }
  ],
  UI.Facets          : [{
    $Type  : 'UI.ReferenceFacet',
    Label  : 'General Information',
    Target : '@UI.FieldGroup#General'
  }],
  UI.FieldGroup #General : { Data: [
    { Value: description },
    { Value: equipmentType },
    { Value: estimatedCost },
    { Value: currency_code },
    { Value: status }
  ]}
);

Rafraîchissez l'aperçu Fiori : list report, object page, brouillon et boutons Approve/Reject sont là.

7. Déployer sur BTP

Pour la production, CAP ajoute SAP HANA Cloud, l'authentification XSUAA, un approuter et un descripteur MTA en une commande :

deploy.sh
# production building blocks: SAP HANA Cloud, XSUAA, approuter, MTA descriptor
cds add hana,xsuaa,approuter,mta

# build the multi-target archive and deploy it to Cloud Foundry
npm install
mbt build
cf login -a https://api.cf.eu10.hana.ondemand.com
cf deploy mta_archives/equipment-requests_1.0.0.mtar

Après le déploiement, attribuez les role collections dans le cockpit BTP et ajoutez l'application au launchpad (SAP Build Work Zone).

À retenir

  • CAP mène d'un dossier vide à un service OData V4 avec brouillon en quelques minutes.
  • Les concepts correspondent à RAP : entités ↔ view entities, handlers ↔ behavior implementation, annotations ↔ metadata extensions.
  • Choisissez selon l'endroit où vit la logique : dans S/4HANA → RAP, side-by-side sur BTP → CAP.

Commentaires

Laisser un commentaire