Initial commit
This commit is contained in:
492
references/development.md
Normal file
492
references/development.md
Normal file
@@ -0,0 +1,492 @@
|
||||
# Development Reference
|
||||
|
||||
Development patterns and best practices for SAP BTP applications.
|
||||
|
||||
**Source**: [https://github.com/SAP-docs/sap-btp-cloud-platform/tree/main/docs/30-development](https://github.com/SAP-docs/sap-btp-cloud-platform/tree/main/docs/30-development)
|
||||
|
||||
---
|
||||
|
||||
## Table of Contents
|
||||
|
||||
1. [Multi-Target Applications](#multi-target-applications)
|
||||
2. [Application Router](#application-router)
|
||||
3. [CAP Development](#cap-development)
|
||||
4. [Service Bindings](#service-bindings)
|
||||
5. [CI/CD Pipelines](#cicd-pipelines)
|
||||
6. [Deployment Strategies](#deployment-strategies)
|
||||
|
||||
---
|
||||
|
||||
## Multi-Target Applications
|
||||
|
||||
### MTA Structure
|
||||
|
||||
```
|
||||
my-app/
|
||||
├── mta.yaml # MTA descriptor
|
||||
├── srv/ # Backend service
|
||||
│ ├── package.json
|
||||
│ └── src/
|
||||
├── app/ # Frontend
|
||||
│ └── webapp/
|
||||
├── db/ # Database artifacts
|
||||
│ └── src/
|
||||
└── xs-security.json # Security config
|
||||
```
|
||||
|
||||
### mta.yaml Template
|
||||
|
||||
```yaml
|
||||
_schema-version: "3.1"
|
||||
ID: my-app
|
||||
version: 1.0.0
|
||||
description: My SAP BTP Application
|
||||
|
||||
parameters:
|
||||
enable-parallel-deployments: true
|
||||
|
||||
build-parameters:
|
||||
before-all:
|
||||
- builder: custom
|
||||
commands:
|
||||
- npm install --production
|
||||
|
||||
modules:
|
||||
# Backend service
|
||||
- name: my-app-srv
|
||||
type: nodejs
|
||||
path: srv
|
||||
parameters:
|
||||
buildpack: nodejs_buildpack
|
||||
memory: 256M
|
||||
build-parameters:
|
||||
builder: npm
|
||||
requires:
|
||||
- name: my-app-db
|
||||
- name: my-app-auth
|
||||
provides:
|
||||
- name: srv-api
|
||||
properties:
|
||||
srv-url: ${default-url}
|
||||
|
||||
# Database deployer
|
||||
- name: my-app-db-deployer
|
||||
type: hdb
|
||||
path: db
|
||||
parameters:
|
||||
buildpack: nodejs_buildpack
|
||||
requires:
|
||||
- name: my-app-db
|
||||
|
||||
# UI module
|
||||
- name: my-app-ui
|
||||
type: html5
|
||||
path: app
|
||||
build-parameters:
|
||||
builder: custom
|
||||
commands:
|
||||
- npm run build
|
||||
supported-platforms: []
|
||||
|
||||
# App Router
|
||||
- name: my-app-approuter
|
||||
type: approuter.nodejs
|
||||
path: approuter
|
||||
parameters:
|
||||
disk-quota: 256M
|
||||
memory: 256M
|
||||
requires:
|
||||
- name: my-app-auth
|
||||
- name: srv-api
|
||||
group: destinations
|
||||
properties:
|
||||
name: srv-api
|
||||
url: ~{srv-url}
|
||||
forwardAuthToken: true
|
||||
|
||||
resources:
|
||||
# HDI Container
|
||||
- name: my-app-db
|
||||
type: com.sap.xs.hdi-container
|
||||
parameters:
|
||||
service: hana
|
||||
service-plan: hdi-shared
|
||||
|
||||
# XSUAA
|
||||
- name: my-app-auth
|
||||
type: org.cloudfoundry.managed-service
|
||||
parameters:
|
||||
service: xsuaa
|
||||
service-plan: application
|
||||
path: ./xs-security.json
|
||||
```
|
||||
|
||||
### Build and Deploy
|
||||
|
||||
```bash
|
||||
# Build MTA archive
|
||||
mbt build
|
||||
|
||||
# Deploy
|
||||
cf deploy mta_archives/my-app_1.0.0.mtar
|
||||
|
||||
# Deploy with options
|
||||
cf deploy my-app.mtar --strategy blue-green
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Application Router
|
||||
|
||||
### Purpose
|
||||
|
||||
- Single entry point for applications
|
||||
- User authentication
|
||||
- Static content serving
|
||||
- URL routing to microservices
|
||||
- Session management
|
||||
|
||||
### xs-app.json
|
||||
|
||||
```json
|
||||
{
|
||||
"welcomeFile": "/index.html",
|
||||
"authenticationMethod": "route",
|
||||
"sessionTimeout": 30,
|
||||
"routes": [
|
||||
{
|
||||
"source": "^/api/(.*)$",
|
||||
"target": "$1",
|
||||
"destination": "srv-api",
|
||||
"authenticationType": "xsuaa",
|
||||
"csrfProtection": true
|
||||
},
|
||||
{
|
||||
"source": "^/(.*)$",
|
||||
"target": "$1",
|
||||
"localDir": "webapp",
|
||||
"authenticationType": "xsuaa"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Authentication Types
|
||||
|
||||
| Type | Description |
|
||||
|------|-------------|
|
||||
| `xsuaa` | Require authentication |
|
||||
| `none` | No authentication |
|
||||
| `basic` | Basic auth (dev only) |
|
||||
|
||||
### Route Properties
|
||||
|
||||
| Property | Description |
|
||||
|----------|-------------|
|
||||
| `source` | Regex pattern for incoming URL |
|
||||
| `target` | Rewritten path |
|
||||
| `destination` | Destination name |
|
||||
| `localDir` | Serve from local directory |
|
||||
| `csrfProtection` | Enable CSRF tokens |
|
||||
| `scope` | Required authorization scope |
|
||||
|
||||
### Environment Variables
|
||||
|
||||
```json
|
||||
{
|
||||
"destinations": [
|
||||
{
|
||||
"name": "srv-api",
|
||||
"url": "[https://my-srv.cfapps.eu10.hana.ondemand.com",](https://my-srv.cfapps.eu10.hana.ondemand.com",)
|
||||
"forwardAuthToken": true
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## CAP Development
|
||||
|
||||
### Project Setup
|
||||
|
||||
```bash
|
||||
# Create new project
|
||||
cds init my-project
|
||||
|
||||
# Add features
|
||||
cds add hana
|
||||
cds add xsuaa
|
||||
cds add mta
|
||||
```
|
||||
|
||||
### Service Definition (CDS)
|
||||
|
||||
```cds
|
||||
// srv/catalog-service.cds
|
||||
using { my.bookshop as my } from '../db/schema';
|
||||
|
||||
service CatalogService {
|
||||
@readonly entity Books as projection on my.Books;
|
||||
entity Orders as projection on my.Orders;
|
||||
}
|
||||
```
|
||||
|
||||
### Data Model
|
||||
|
||||
```cds
|
||||
// db/schema.cds
|
||||
namespace my.bookshop;
|
||||
|
||||
entity Books {
|
||||
key ID : Integer;
|
||||
title : String;
|
||||
author : Association to Authors;
|
||||
stock : Integer;
|
||||
}
|
||||
|
||||
entity Authors {
|
||||
key ID : Integer;
|
||||
name : String;
|
||||
books : Association to many Books on books.author = $self;
|
||||
}
|
||||
|
||||
entity Orders {
|
||||
key ID : UUID;
|
||||
book : Association to Books;
|
||||
amount : Integer;
|
||||
}
|
||||
```
|
||||
|
||||
### Service Implementation
|
||||
|
||||
```javascript
|
||||
// srv/catalog-service.js
|
||||
module.exports = cds.service.impl(async function() {
|
||||
const { Books, Orders } = this.entities;
|
||||
|
||||
this.before('CREATE', 'Orders', async (req) => {
|
||||
const { book_ID, amount } = req.data;
|
||||
const book = await SELECT.one.from(Books).where({ ID: book_ID });
|
||||
if (book.stock < amount) {
|
||||
req.error(409, 'Not enough stock');
|
||||
}
|
||||
});
|
||||
|
||||
this.after('CREATE', 'Orders', async (order, req) => {
|
||||
await UPDATE(Books)
|
||||
.set({ stock: { '-=': order.amount } })
|
||||
.where({ ID: order.book_ID });
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
### Running Locally
|
||||
|
||||
```bash
|
||||
# Start with watch (SQLite in-memory)
|
||||
cds watch
|
||||
|
||||
# With hybrid profile (remote services, local app)
|
||||
cds watch --profile hybrid
|
||||
|
||||
# Deploy to database
|
||||
cds deploy --to hana
|
||||
```
|
||||
|
||||
**Profile Options**:
|
||||
| Profile | Description | Use Case |
|
||||
|---------|-------------|----------|
|
||||
| `default` | SQLite in-memory, mock auth | Initial development, quick testing |
|
||||
| `hybrid` | Connect to remote BTP services while running locally | Test with real HANA, XSUAA, destinations |
|
||||
| `production` | Full BTP services | Deployed application |
|
||||
|
||||
**Hybrid Profile Setup** (`.cdsrc.json`):
|
||||
```json
|
||||
{
|
||||
"[hybrid]": {
|
||||
"requires": {
|
||||
"db": {
|
||||
"kind": "hana",
|
||||
"credentials": { "from": "env:VCAP_SERVICES" }
|
||||
},
|
||||
"auth": {
|
||||
"kind": "xsuaa",
|
||||
"credentials": { "from": "env:VCAP_SERVICES" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Run `cds bind` to fetch service credentials, then `cds watch --profile hybrid`.
|
||||
|
||||
---
|
||||
|
||||
## Service Bindings
|
||||
|
||||
### Accessing Bound Services
|
||||
|
||||
**Environment Variable (VCAP_SERVICES)**:
|
||||
```javascript
|
||||
const vcap = JSON.parse(process.env.VCAP_SERVICES);
|
||||
const hanaCredentials = vcap.hana[0].credentials;
|
||||
```
|
||||
|
||||
**Using @sap/xsenv**:
|
||||
```javascript
|
||||
const xsenv = require('@sap/xsenv');
|
||||
xsenv.loadEnv();
|
||||
|
||||
const hanaCredentials = xsenv.serviceCredentials({ tag: 'hana' });
|
||||
```
|
||||
|
||||
**Using CAP**:
|
||||
```javascript
|
||||
// Automatic binding via cds.requires in package.json
|
||||
const db = await cds.connect.to('db');
|
||||
```
|
||||
|
||||
### package.json (CAP)
|
||||
|
||||
```json
|
||||
{
|
||||
"cds": {
|
||||
"requires": {
|
||||
"db": {
|
||||
"kind": "hana",
|
||||
"credentials": {
|
||||
"binding": "db"
|
||||
}
|
||||
},
|
||||
"auth": {
|
||||
"kind": "xsuaa"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## CI/CD Pipelines
|
||||
|
||||
### SAP Continuous Integration and Delivery
|
||||
|
||||
Pipeline types:
|
||||
1. Cloud Foundry - Fiori, CAP
|
||||
2. SAP Fiori for ABAP Platform
|
||||
3. SAP Integration Suite Artifacts
|
||||
|
||||
### Pipeline Configuration
|
||||
|
||||
```yaml
|
||||
# .pipeline/config.yml
|
||||
general:
|
||||
buildTool: mta
|
||||
mtaBuildTool: cloudMbt
|
||||
|
||||
stages:
|
||||
Build:
|
||||
npmExecuteBefore:
|
||||
dockerImage: 'node:18'
|
||||
|
||||
Integration:
|
||||
credentials:
|
||||
cfCredentialsId: cf-credentials
|
||||
|
||||
Release:
|
||||
cfSpace: prod
|
||||
cfCredentialsId: cf-credentials
|
||||
```
|
||||
|
||||
### GitHub Actions Example
|
||||
|
||||
```yaml
|
||||
name: Deploy to BTP
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: '18'
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
- name: Build MTA
|
||||
run: npx mbt build
|
||||
|
||||
- name: Deploy to CF
|
||||
env:
|
||||
CF_API: ${{ secrets.CF_API }}
|
||||
CF_USER: ${{ secrets.CF_USER }}
|
||||
CF_PASSWORD: ${{ secrets.CF_PASSWORD }}
|
||||
run: |
|
||||
cf login -a $CF_API -u $CF_USER -p $CF_PASSWORD -o $CF_ORG -s $CF_SPACE
|
||||
cf deploy mta_archives/*.mtar -f
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Deployment Strategies
|
||||
|
||||
### Rolling Deployment (Default)
|
||||
|
||||
Replace instances one by one:
|
||||
```bash
|
||||
cf push my-app
|
||||
```
|
||||
|
||||
### Blue-Green Deployment
|
||||
|
||||
Zero-downtime with instant rollback:
|
||||
|
||||
```bash
|
||||
# Deploy new version
|
||||
cf push my-app-new -f manifest.yml
|
||||
|
||||
# Map production route
|
||||
cf map-route my-app-new cfapps.eu10.hana.ondemand.com -n my-app
|
||||
|
||||
# Unmap from old
|
||||
cf unmap-route my-app cfapps.eu10.hana.ondemand.com -n my-app
|
||||
|
||||
# Delete old version
|
||||
cf delete my-app -f
|
||||
|
||||
# Rename
|
||||
cf rename my-app-new my-app
|
||||
```
|
||||
|
||||
**With MTA**:
|
||||
```bash
|
||||
cf deploy my-app.mtar --strategy blue-green
|
||||
```
|
||||
|
||||
### Canary Deployment
|
||||
|
||||
Gradual traffic shift:
|
||||
```bash
|
||||
# Deploy canary with different route
|
||||
cf push my-app-canary -f manifest-canary.yml
|
||||
|
||||
# Gradually shift traffic (manual or with load balancer)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Related Documentation
|
||||
|
||||
- Development Guide: [https://github.com/SAP-docs/sap-btp-cloud-platform/tree/main/docs/30-development](https://github.com/SAP-docs/sap-btp-cloud-platform/tree/main/docs/30-development)
|
||||
- CAP Documentation: [https://cap.cloud.sap/docs/](https://cap.cloud.sap/docs/)
|
||||
- MTA Guide: [https://help.sap.com/docs/btp/sap-business-technology-platform/multitarget-applications](https://help.sap.com/docs/btp/sap-business-technology-platform/multitarget-applications)
|
||||
Reference in New Issue
Block a user