Showing preview only (4,713K chars total). Download the full file or copy to clipboard to get everything.
Repository: SAP-samples/cap-sflight
Branch: main
Commit: 5a4377d7c540
Files: 148
Total size: 4.5 MB
Directory structure:
gitextract_co2fgw5z/
├── .cdsrc.json
├── .devcontainer/
│ ├── cds-dk/
│ │ ├── devcontainer-feature.json
│ │ └── install.sh
│ ├── cf-deploy/
│ │ ├── devcontainer-feature.json
│ │ └── install.sh
│ └── devcontainer.json
├── .github/
│ ├── actions/
│ │ ├── btp/
│ │ │ ├── Dockerfile
│ │ │ ├── action.yml
│ │ │ └── entrypoint.sh
│ │ └── cf-deploy/
│ │ ├── Dockerfile
│ │ ├── action.yml
│ │ └── entrypoint.sh
│ ├── dependabot.yml
│ ├── deployment/
│ │ └── kyma/
│ │ └── scripts/
│ │ ├── build-ui-image.sh
│ │ ├── create-container-registry-secret.sh
│ │ ├── create-db-secret.sh
│ │ ├── format-kyma-secret.js
│ │ ├── prepareUiFiles.js
│ │ ├── value.js
│ │ └── values.sh
│ └── workflows/
│ ├── deploy-btp.yml
│ ├── maven.yml
│ └── node.js.yml
├── .gitignore
├── .vscode/
│ └── launch.json
├── LICENSE
├── LICENSES/
│ └── Apache-2.0.txt
├── README-Kyma.md
├── README.md
├── REUSE.toml
├── _i18n/
│ ├── i18n.properties
│ ├── i18n_de.properties
│ ├── i18n_en.properties
│ └── i18n_fr.properties
├── app/
│ ├── .karma/
│ │ ├── karma-cap-middleware.js
│ │ └── karma.conf.js
│ ├── common.cds
│ ├── labels.cds
│ ├── services.cds
│ ├── travel_analytics/
│ │ ├── annotations.cds
│ │ ├── karma.conf.js
│ │ ├── package.json
│ │ ├── tsconfig.json
│ │ ├── ui5.yaml
│ │ ├── webapp/
│ │ │ ├── Component.ts
│ │ │ ├── i18n/
│ │ │ │ ├── i18n.properties
│ │ │ │ ├── i18n_de.properties
│ │ │ │ ├── i18n_en.properties
│ │ │ │ └── i18n_fr.properties
│ │ │ ├── index.html
│ │ │ ├── manifest.json
│ │ │ └── test/
│ │ │ ├── flpSandbox.html
│ │ │ ├── integration/
│ │ │ │ ├── Opa.qunit.html
│ │ │ │ ├── Opa.qunit.js
│ │ │ │ ├── OpaJourney.js
│ │ │ │ └── pages/
│ │ │ │ ├── BookingsList.js
│ │ │ │ └── BookingsObjectPage.js
│ │ │ ├── testsuite.qunit.html
│ │ │ └── testsuite.qunit.js
│ │ ├── xs-app.json
│ │ └── xs-security.json
│ ├── travel_processor/
│ │ ├── capabilities.cds
│ │ ├── field-control.cds
│ │ ├── karma.conf.js
│ │ ├── layouts.cds
│ │ ├── package.json
│ │ ├── tsconfig.json
│ │ ├── ui5.yaml
│ │ ├── webapp/
│ │ │ ├── Component.ts
│ │ │ ├── changes/
│ │ │ │ ├── changes-bundle.json
│ │ │ │ └── flexibility-bundle.json
│ │ │ ├── ext/
│ │ │ │ ├── controller/
│ │ │ │ │ ├── ControllerExtension.d.ts
│ │ │ │ │ └── ObjectPageExtension.controller.ts
│ │ │ │ └── fragment/
│ │ │ │ ├── CustomSection.fragment.xml
│ │ │ │ ├── CustomSection.ts
│ │ │ │ └── Trees4Tickets.fragment.xml
│ │ │ ├── i18n/
│ │ │ │ ├── i18n.properties
│ │ │ │ ├── i18n_de.properties
│ │ │ │ ├── i18n_en.properties
│ │ │ │ └── i18n_fr.properties
│ │ │ ├── index.html
│ │ │ ├── manifest.json
│ │ │ └── test/
│ │ │ ├── integration/
│ │ │ │ ├── Opa.qunit.html
│ │ │ │ ├── Opa.qunit.js
│ │ │ │ ├── OpaJourney.js
│ │ │ │ └── pages/
│ │ │ │ ├── ItemObjectPage.js
│ │ │ │ ├── MainListReport.js
│ │ │ │ └── MainObjectPage.js
│ │ │ ├── testsuite.qunit.html
│ │ │ └── testsuite.qunit.js
│ │ ├── xs-app.json
│ │ └── xs-security.json
│ └── value-helps.cds
├── db/
│ ├── common.cds
│ ├── data/
│ │ ├── sap.common-Countries.csv
│ │ ├── sap.common-Countries.texts.csv
│ │ ├── sap.common-Currencies.csv
│ │ ├── sap.common-Currencies.texts.csv
│ │ ├── sap.fe.cap.travel-Airline.csv
│ │ ├── sap.fe.cap.travel-Airport.csv
│ │ ├── sap.fe.cap.travel-Booking.csv
│ │ ├── sap.fe.cap.travel-BookingStatus.csv
│ │ ├── sap.fe.cap.travel-BookingStatus.texts.csv
│ │ ├── sap.fe.cap.travel-BookingSupplement.csv
│ │ ├── sap.fe.cap.travel-Flight.csv
│ │ ├── sap.fe.cap.travel-FlightConnection.csv
│ │ ├── sap.fe.cap.travel-Passenger.csv
│ │ ├── sap.fe.cap.travel-Supplement.csv
│ │ ├── sap.fe.cap.travel-Supplement.texts.csv
│ │ ├── sap.fe.cap.travel-SupplementType.csv
│ │ ├── sap.fe.cap.travel-Travel.csv
│ │ ├── sap.fe.cap.travel-TravelAgency.csv
│ │ ├── sap.fe.cap.travel-TravelStatus.csv
│ │ └── sap.fe.cap.travel-TravelStatus.texts.csv
│ ├── master-data.cds
│ ├── package.json
│ └── schema.cds
├── eslint.config.js
├── mta-java.yaml
├── mta.yaml
├── native-build-env.json
├── package.json
├── pom.xml
├── srv/
│ ├── analytics-service.cds
│ ├── pom.xml
│ ├── src/
│ │ ├── main/
│ │ │ ├── java/
│ │ │ │ └── com/
│ │ │ │ └── sap/
│ │ │ │ └── cap/
│ │ │ │ └── sflight/
│ │ │ │ ├── SFlightApplication.java
│ │ │ │ ├── processor/
│ │ │ │ │ ├── AcceptRejectHandler.java
│ │ │ │ │ ├── CreationHandler.java
│ │ │ │ │ ├── DeductDiscountHandler.java
│ │ │ │ │ ├── IllegalTravelDateException.java
│ │ │ │ │ ├── IllegalTravelStatusException.java
│ │ │ │ │ ├── RecalculatePriceHandler.java
│ │ │ │ │ └── UpdateFlightSeatsHandler.java
│ │ │ │ ├── security/
│ │ │ │ │ └── WebSecurityConfig.java
│ │ │ │ └── ui/
│ │ │ │ └── RedirectFilter.java
│ │ │ └── resources/
│ │ │ ├── META-INF/
│ │ │ │ └── native-image/
│ │ │ │ └── resource-config.json
│ │ │ ├── application.yml
│ │ │ └── messages.properties
│ │ └── test/
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── sap/
│ │ │ └── cap/
│ │ │ └── sflight/
│ │ │ ├── SFlightApplicationTest.java
│ │ │ └── processor/
│ │ │ ├── TravelSmokeTest.java
│ │ │ └── UpdateFlightSeatsHandlerServiceIntegrationTest.java
│ │ └── resources/
│ │ └── META-INF/
│ │ └── native-image/
│ │ └── reflect-config.json
│ ├── travel-service.cds
│ └── travel-service.ts
├── test/
│ ├── odata.test.ts
│ ├── requests.http
│ └── setup.ts
└── tsconfig.json
================================================
FILE CONTENTS
================================================
================================================
FILE: .cdsrc.json
================================================
{}
================================================
FILE: .devcontainer/cds-dk/devcontainer-feature.json
================================================
{
"id": "cds-dk",
"version": "1.0.0",
"name": "cds-dk",
"description": "Install @sap/cds-dk (via npm)",
"options": {
"version": {
"type": "string",
"proposals": [
"^9",
"^8"
],
"default": "latest",
"description": "Select the @sap/cds-dk version"
}
},
"dependsOn": {
"ghcr.io/devcontainers/features/node:1": { }
}
}
================================================
FILE: .devcontainer/cds-dk/install.sh
================================================
#!/bin/sh
set -e
export VERSION_SPECIFIER="${VERSION:+@${VERSION}}"
. ${NVM_DIR}/nvm.sh
npm i -g @sap/cds-dk${VERSION_SPECIFIER}
================================================
FILE: .devcontainer/cf-deploy/devcontainer-feature.json
================================================
{
"id": "cf-deploy",
"version": "1.0.1",
"name": "cf-deploy",
"description": "Install tooling for deploying to Cloud Foundry",
"dependsOn": {
"ghcr.io/devcontainers/features/node:1": { }
}
}
================================================
FILE: .devcontainer/cf-deploy/install.sh
================================================
#!/bin/sh
set -e
# Homebrew
if ! type brew >/dev/null 2>&1; then
NONINTERACTIVE=1 su vscode -s /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
echo >> /home/vscode/.bashrc
echo 'eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)"' >> /home/vscode/.bashrc
eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)"
fi
su vscode -s /bin/bash -c "brew install cloudfoundry/tap/cf-cli@8"
su vscode -s /bin/bash -c "cf install-plugin -f multiapps"
npm i -g mbt
================================================
FILE: .devcontainer/devcontainer.json
================================================
// For format details, see https://aka.ms/devcontainer.json
{
"name": "CAP Node.js & TypeScript & Java",
"image": "mcr.microsoft.com/devcontainers/base:debian",
// Features to add to the dev container. More info: https://containers.dev/features.
"features": {
"ghcr.io/devcontainers/features/node:1": {},
"ghcr.io/devcontainers-extra/features/typescript:2": {},
"ghcr.io/devcontainers/features/java:1": {
"version": "21",
"jdkDistro": "sapmchn",
"installMaven": "true"
},
"./cds-dk": {},
"./cf-deploy": {}
},
// Use 'postCreateCommand' to run commands after the container is created.
"postCreateCommand": "npm install"
}
================================================
FILE: .github/actions/btp/Dockerfile
================================================
FROM buildpack-deps:stretch-curl
# https://blogs.sap.com/2021/09/01/sap-tech-bytes-btp-cli-installation/
RUN curl -s -O --location --url "https://raw.githubusercontent.com/SAP-samples/sap-tech-bytes/2021-09-01-btp-cli/getbtpcli" && \
chmod +x getbtpcli && \
echo | ./getbtpcli && \
mv $HOME/bin/btp* /usr/local/bin/
COPY entrypoint.sh /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]
================================================
FILE: .github/actions/btp/action.yml
================================================
name: 'BTP'
description: Performs BTP operations
inputs:
cli_url:
description: The API endpoint for the CLI
required: false
default: https://cpcli.cf.eu10.hana.ondemand.com
subdomain:
description: Name of BTP subdomain
required: true
subaccount_id:
description: ID of BTP subaccount
required: false
role_collection:
description: A role collection to assign to $CF_USER
required: false
command:
description: The command to execute
required: false
runs:
using: docker
image: Dockerfile
================================================
FILE: .github/actions/btp/entrypoint.sh
================================================
#!/bin/bash
set -e
INPUT_USERNAME=${INPUT_USERNAME:-$CF_USERNAME}
INPUT_PASSWORD=${INPUT_PASSWORD:-$CF_PASSWORD}
btp login --url "${INPUT_CLI_URL}" --user "${INPUT_USERNAME}" --password "${INPUT_PASSWORD}" --subdomain "${INPUT_SUBDOMAIN}"
if [ ! -z "${INPUT_SUBACCOUNT_ID}" ]; then
btp target --subaccount "${INPUT_SUBACCOUNT_ID}"
fi
if [ ! -z "${INPUT_ROLE_COLLECTION}" ]; then
btp assign security/role-collection ${INPUT_ROLE_COLLECTION} --to-user ${INPUT_USERNAME} > /dev/null
if [ ! -z "${GRANT_USERS}" ]; then
for user in ${GRANT_USERS//\\n/ } # newline separated
do
btp assign security/role-collection ${INPUT_ROLE_COLLECTION} --to-user ${user} --create-user-if-missing
done
fi
fi
if [ ! -z "${INPUT_COMMAND}" ]; then
${INPUT_COMMAND}
fi
================================================
FILE: .github/actions/cf-deploy/Dockerfile
================================================
FROM ppiper/cf-cli:v11
# needed for cf to find its config
# (GH resets HOME to /github/workspace)
ENV CF_HOME=$HOME
COPY entrypoint.sh /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]
================================================
FILE: .github/actions/cf-deploy/action.yml
================================================
name: 'CF Deploy MTA'
description: Deploys an MTA file to Cloud Foundry
inputs:
mtafile:
description: Path to your MTA file, relative to the workspace
required: true
api:
description: Cloud Foundry API endpoint. Default is $CF_API
required: false
username:
description: Cloud Foundry username. Default is $CF_USERNAME
required: false
password:
description: Cloud Foundry password. Default is $CF_PASSWORD
required: false
org:
description: Cloud Foundry organization. Default is $CF_ORG
required: false
space:
description: Cloud Foundry space to deploy to. Default is $CF_SPACE
required: false
createspace:
description: Whether to create the given space
required: false
default: 'false'
findurl_command:
description: Bash command that determines the application's main URL
required: false
findurl_regex:
description: Regex that scans the result of 'findurl_command'
required: false
default: 'https.*'
outputs:
url:
description: The applications's main URL (only if 'findurl_command' is set and successful)
runs:
using: docker
image: Dockerfile
================================================
FILE: .github/actions/cf-deploy/entrypoint.sh
================================================
#!/bin/bash
set -e
cf_opts=
cf api ${INPUT_API:-$CF_API} ${cf_opts}
INPUT_USERNAME=${INPUT_USERNAME:-$CF_USERNAME}
INPUT_PASSWORD=${INPUT_PASSWORD:-$CF_PASSWORD}
cf auth
if [ "x${INPUT_CREATESPACE}" = "xtrue" ]; then
cf create-space ${INPUT_SPACE:-$CF_SPACE} -o ${INPUT_ORG:-$CF_ORG}
fi
cf target -o ${INPUT_ORG:-$CF_ORG} -s ${INPUT_SPACE:-$CF_SPACE}
cf deploy ${INPUT_MTAFILE} -f
if [ ! -z "${INPUT_FINDURL_COMMAND}" ]; then
# echo "Find URL command: ${INPUT_FINDURL_COMMAND}, regex: ${INPUT_FINDURL_REGEX}"
res=`${INPUT_FINDURL_COMMAND}`
url=`echo "${res}" | grep -o "${INPUT_FINDURL_REGEX}"`
echo "URL: $url"
echo "::set-output name=url::$url"
fi
================================================
FILE: .github/dependabot.yml
================================================
version: 2
updates:
- package-ecosystem: npm
directory: /
versioning-strategy: increase-if-necessary
schedule:
interval: weekly
- package-ecosystem: npm
directory: /app/travel_processor
versioning-strategy: increase-if-necessary
schedule:
interval: weekly
- package-ecosystem: npm
directory: /app/travel_analytics
versioning-strategy: increase-if-necessary
schedule:
interval: weekly
- package-ecosystem: maven
directory: /
schedule:
interval: weekly
================================================
FILE: .github/deployment/kyma/scripts/build-ui-image.sh
================================================
#!/bin/bash
set -e
cd "$(dirname "$(npm root)")"
DIR="$(pwd)/.github"
npm install --no-save yaml
function value() {
node "$DIR/deployment/kyma/scripts/value.js" "$1"
}
function image() {
local REPOSITORY="$(value "$1.image.repository")"
local TAG="$(value "$1.image.tag")"
if [ "$TAG" != "" ]; then
echo "$REPOSITORY:$TAG"
else
echo "$REPOSITORY"
fi
}
rm -rf gen/ui
mkdir -p gen/ui/resources
CLOUD_SERVICE="$(value html5-apps-deployer.env.SAP_CLOUD_SERVICE)"
DESTINATIONS="$(value backendDestinations)"
IMAGE="$(image html5-apps-deployer)"
for APP in app/*; do
if [ -f "$APP/webapp/manifest.json" ]; then
echo "Build $APP..."
echo
rm -rf "gen/$APP"
mkdir -p "gen/app"
cp -r "$APP" gen/app
pushd >/dev/null "gen/$APP"
node "$DIR/deployment/kyma/scripts/prepareUiFiles.js" $CLOUD_SERVICE $DESTINATIONS
npm install
npx ui5 build preload --clean-dest --config ui5-deploy.yaml --include-task=generateManifestBundle generateCachebusterInfo
cd dist
rm manifest-bundle.zip
mv *.zip "$DIR/gen/ui/resources"
popd >/dev/null
fi
done
cd gen/ui
echo
echo "HTML5 Apps:"
ls -l resources
echo
cat >package.json <<EOF
{
"name": "ui-deployer",
"scripts": { "start": "node node_modules/@sap/html5-app-deployer/index.js" }
}
EOF
npm install @sap/html5-app-deployer
pack build $IMAGE --path . --buildpack gcr.io/paketo-buildpacks/nodejs --builder paketobuildpacks/builder-jammy-base
================================================
FILE: .github/deployment/kyma/scripts/create-container-registry-secret.sh
================================================
#!/bin/bash
read -p "Docker Server: " DOCKER_SERVER
read -p "User ($USER): " DOCKER_USER
if [ "$DOCKER_USER" == "" ]; then
DOCKER_USER="$USER"
fi
if [ "$EMAIL" == "" ]; then
read -p "EMail: " DOCKER_EMAIL
else
read -p "EMail ($EMAIL): " DOCKER_EMAIL
if [ "$DOCKER_EMAIL" == "" ]; then
DOCKER_EMAIL="$EMAIL"
fi
fi
read -sp "API Key: " API_KEY
echo
echo
kubectl create secret docker-registry container-registry \
"--docker-server=$DOCKER_SERVER" \
"--docker-username=$DOCKER_USER" \
"--docker-email=$DOCKER_EMAIL" \
"--docker-password=$API_KEY"
================================================
FILE: .github/deployment/kyma/scripts/create-db-secret.sh
================================================
#!/bin/bash
set -e
cd "$(dirname "$(dirname "$0")")"
. ./scripts/values.sh
if true-value 2>/dev/null .saas_registry.enabled; then
echo >&2 "[ERROR] DB secret only required for single tenancy apps"
fi
NAME="$1"
if [ "$NAME" == "" ]; then
if [ ! -f "chart/values.yaml" ]; then
echo >&2 "[ERROR] Please either specify the name for the DB secret or maintain it in the Helm chart"
exit 1
fi
NAME="$(value .srv.bindings.db.fromSecret)"
if [ "$NAME" == "" -o "$NAME" == "<nil>" ]; then
echo >&2 "[ERROR] Please either specify the name for the DB secret or maintain it in the Helm chart"
exit 1
fi
fi
SECRET_HEADER="$(cat <<EOF
apiVersion: v1
kind: Secret
metadata:
name: $NAME
type: Opaque
stringData:
.metadata: |
{
"credentialProperties":
[
{ "name": "certificate", "format": "text"},
{ "name": "database_id", "format": "text"},
{ "name": "driver", "format": "text"},
{ "name": "hdi_password", "format": "text"},
{ "name": "hdi_user", "format": "text"},
{ "name": "host", "format": "text"},
{ "name": "password", "format": "text"},
{ "name": "port", "format": "text"},
{ "name": "schema", "format": "text"},
{ "name": "url", "format": "text"},
{ "name": "user", "format": "text"}
],
"metaDataProperties":
[
{ "name": "plan", "format": "text" },
{ "name": "label", "format": "text" },
{ "name": "type", "format": "text" },
{ "name": "tags", "format": "json" }
]
}
type: hana
label: hana
plan: hdi-shared
tags: '[ "hana", "database", "relational" ]'
EOF
)"
cf 2>/dev/null >/dev/null service $NAME || cf create-service hana hdi-shared $NAME
while true; do
STATUS="$(cf 2>/dev/null service $NAME | grep status: | head -n 1)"
echo $STATUS
if [[ "$STATUS" = *succeeded* ]]; then
break
fi
sleep 1
done
cf create-service-key $NAME $NAME-key
node "$(dirname "$1")/scripts/format-kyma-secret.js" -- "$(echo "$SECRET_HEADER")" "$(cf service-key $NAME $NAME-key)" | kubectl apply -f -
echo
echo "HANA DB Kubernetes secret '$NAME' created."
echo
echo "You can view it using:"
echo
echo "kubectl get secret $NAME -o yaml"
exit 0
================================================
FILE: .github/deployment/kyma/scripts/format-kyma-secret.js
================================================
const key=JSON.parse(process.argv[4].replace(/^.*/, ""));
const credentials=key.credentials /* new cfcli? */ || key;
console.log(process.argv[3]);
console.log(Object.keys(credentials).map(k => {
if (credentials[k].match(/\n/s))
return (` ${k}: |\n${credentials[k]}`).replace(/\n/gs,"\n ")
else
return ` ${k}: "${credentials[k]}"`
}).join("\n"))
================================================
FILE: .github/deployment/kyma/scripts/prepareUiFiles.js
================================================
const Path = require('path');
const posixJoin = Path.posix.join;
function prepareUiFiles(path, options) {
const { readFileSync, writeFileSync, existsSync } = require('fs');
const Path = require('path');
const srvDestination = getSrvDestination(options.destinations);
const packageJsonInclude = getPackageJsonInclude();
const ui5DeployTemplate = getUI5DeployTemplateYaml();
const xsAppJsonTemplate = getXsAppTemplateJson();
const packageJsonPath = Path.join(path, 'package.json');
const manifestJsonPath = Path.join(path, 'webapp/manifest.json');
const packageJson = JSON.parse(readFileSync(packageJsonPath));
const manifestJson = JSON.parse(readFileSync(manifestJsonPath));
// Adjust package.json
packageJson.devDependencies = packageJsonInclude.devDependencies;
packageJson.ui5 = packageJsonInclude.ui5;
writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 4));
// Adjust manifest.json
console.log('Adjust webapp/manifest.json');
manifestJson["sap.cloud"] = {
"public": true,
"service": options.cloudService || throwError(`Missing cloudService name`)
};
if (!existsSync(Path.join(path, 'xs-app.json'))) {
const dataSources = manifestJson['sap.app'].dataSources || {};
for (const dataSource of Object.values(dataSources)) {
dataSource.uri = posixJoin('service', dataSource.uri);
}
}
writeFileSync(manifestJsonPath, JSON.stringify(manifestJson, null, 4));
// Add xs-app.json
if (!existsSync(Path.join(path, 'xs-app.json'))) {
console.log('Add xs-app.json');
const xsAppJson = JSON.parse(JSON.stringify(xsAppJsonTemplate));
xsAppJson.routes[0].destination = srvDestination || throwError('Expect srv destination');
writeFileSync(Path.join(path, 'xs-app.json'), JSON.stringify(xsAppJson, null, 4));
}
// Add ui5-deploy.yaml
if (!existsSync(Path.join(path, 'ui5-deploy.yaml'))) {
console.log('Add ui5-deploy.yaml');
const appId = manifestJson["sap.app"].id;
const replacements = {
"ID": appId,
"ARCHIVENAME": appId.replace(/\./g, "")
};
const ui5Deploy = ui5DeployTemplate.replace(/\$([A-Z]+|)/g, (_, v) => replacements[v]);
writeFileSync(Path.join(path, "ui5-deploy.yaml"), ui5Deploy);
}
}
function throwError(msg) {
throw new Error(msg);
}
function getPackageJsonInclude() {
return {
"name": "ui5-builde-root",
"devDependencies": {
"@ui5/cli": "^2.11.1",
"@ui5/fs": "^2.0.6",
"@ui5/logger": "^2.0.1",
"@sap/ux-ui5-tooling": "1",
"rimraf": "3.0.2",
"@sap/ui5-builder-webide-extension": "1.0.x",
"ui5-task-zipper": "^0.3.1",
"mbt": "^1.0.15"
},
"ui5": {
"dependencies": [
"@sap/ui5-builder-webide-extension",
"ui5-task-zipper",
"mbt"
]
}
}
}
function getUI5DeployTemplateYaml() {
return `specVersion: '2.4'
metadata:
name: $ID
type: application
resources:
configuration:
propertiesFileSourceEncoding: UTF-8
builder:
resources:
excludes:
- "/test/**"
- "/localService/**"
customTasks:
- name: webide-extension-task-updateManifestJson
beforeTask: generateManifestBundle
configuration:
appFolder: webapp
destDir: dist
- name: ui5-task-zipper
afterTask: generateCachebusterInfo
configuration:
archiveName: $ARCHIVENAME
additionalFiles:
- xs-app.json`
}
function getXsAppTemplateJson() {
return {
"welcomeFile": "/index.html",
"authenticationMethod": "route",
"routes": [
{
"source": "^/service/(.*)$",
"target": "$1",
"destination": "overwrite-me",
"authenticationType": "xsuaa",
"csrfProtection": false
},
{
"source": "^/resources/(.*)$",
"target": "/resources/$1",
"authenticationType": "none",
"destination": "ui5"
},
{
"source": "^/test-resources/(.*)$",
"target": "/test-resources/$1",
"authenticationType": "none",
"destination": "ui5"
},
{
"source": "^(.*)$",
"target": "$1",
"service": "html5-apps-repo-rt",
"authenticationType": "xsuaa"
}
]
}
}
function getSrvDestination(destinations) {
let destinationsJSON = JSON.parse(destinations);
for (let key in destinationsJSON) {
if(destinationsJSON[key].service === "srv") {
return key;
}
}
}
if (process.argv[1].endsWith('prepareUiFiles.js')) {
// Run in standalone mode
prepareUiFiles('.', { cloudService: process.argv[2], destinations: process.argv[3] });
} else {
module.exports = prepareUiFiles;
}
================================================
FILE: .github/deployment/kyma/scripts/value.js
================================================
#!/usr/bin/env node
/*eslint no-unused-vars: ["error", { "caughtErrors": "none" }]*/
const yaml = require('yaml');
const fs = require('fs');
const path = process.argv[2];
if (!path) {
console.error('missing path');
process.exit(1);
}
const segments = path.split('.');
function printProperty(file) {
let valuesYaml;
try {
valuesYaml = fs.readFileSync(file, 'utf-8');
} catch(error) {
return false;
}
const values = yaml.parse(valuesYaml);
let o = values;
for (let segment of segments) {
o = o[segment];
if (typeof o === "undefined" || o === null) return false;
}
if (typeof o === "object") {
console.log(JSON.stringify(o));
} else {
console.log(o);
}
return true;
}
printProperty('.values.yaml') || printProperty('deployment/kyma/values.yaml') || printProperty('chart/values.yaml') || process.exit(1);
================================================
FILE: .github/deployment/kyma/scripts/values.sh
================================================
function read-value() {
if [ -f .values.yaml ]; then
local VALUE="$(yaml2json <.values.yaml | jq -er "$1" || echo "")"
if [ "$VALUE" != "" -a "$VALUE" != "null" ]; then
echo "$VALUE"
return
fi
fi
local VALUE="$(helm inspect values ./chart --jsonpath="{$1}")"
echo "$VALUE"
}
function true-value() {
local VALUE="$(read-value "$1")"
if [ "$VALUE" == "" -o "$VALUE" == "false" ]; then
return 1
else
return 0
fi
}
function value() {
local VALUE="$(read-value "$1")"
if [ "$VALUE" == "" ]; then
echo >&2 "ERROR: Expect value for $1 in chart/values.yaml"
return 1
fi
echo "$VALUE"
}
function image() {
local REPOSITORY="$(value "$1.image.repository")"
local TAG="$(read-value "$1.image.tag")"
if [ "$TAG" != "" ]; then
echo "$REPOSITORY:$TAG"
else
echo "$REPOSITORY"
fi
}
================================================
FILE: .github/workflows/deploy-btp.yml
================================================
on:
workflow_call:
inputs:
btp-subdomain:
type: string
required: false
default: 284dfa06-4fb6-445e-809c-263b132a0840
btp-subaccount-id:
type: string
required: false
default: 1e4c6bcd-71c5-42c6-88ab-636e3a6dc12e
btp-cli-url:
type: string
required: false
default: https://cpcli.cf.sap.hana.ondemand.com
cf-api:
type: string
required: false
default: 'https://api.cf.eu12.hana.ondemand.com'
cf-org:
type: string
required: false
default: cap-sflight
cf-space:
type: string
required: true
role-collection:
type: string
required: true
mta-source-dir:
description: relative path to dir with mta.yaml
type: string
required: false
default: '.'
mtar-dir:
description: path to dir with mtar
type: string
required: false
default: gen
mtar-file:
description: file name of mtar
type: string
required: false
default: sflight.mtar
secrets:
username:
required: true
password:
required: true
grant-users:
description: List of users to assign the role role-collection to
required: false
outputs:
ui_app_url:
value: ${{ jobs.deployment.outputs.ui_app_url }}
jobs:
deployment:
name: To BTP
runs-on: ubuntu-latest
environment:
name: staging
url: ${{ steps.deploy.outputs.url }}
concurrency: staging
env:
CF_API: ${{ inputs.cf-api }}
CF_ORG: ${{ inputs.cf-org }}
CF_SPACE: ${{ inputs.cf-space }}
CF_USERNAME: ${{ secrets.username }}
CF_PASSWORD: ${{ secrets.password }}
GRANT_USERS: ${{ secrets.grant-users }}
outputs:
ui_app_url: ${{ steps.deploy.outputs.url }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 16
- uses: actions/cache@v4
id: cache
with:
path: ${{ inputs.mtar-dir }}/${{ inputs.mtar-file }}
key: ${{ runner.os }}-${{ github.head_ref || github.ref_name }}-${{ hashFiles('_i18n/**','app/**','db/**','srv/**','*.json','**/mta.yaml','pom.xml') }}
- name: MTA build
if: steps.cache.outputs.cache-hit != 'true'
run: npx mbt build -s ${{ inputs.mta-source-dir }} -t ${{ inputs.mtar-dir }} --mtar ${{ inputs.mtar-file }}
- name: CF Deploy
id: deploy
uses: ./.github/actions/cf-deploy
with:
createspace: true
mtafile: ${{ inputs.mtar-dir }}/${{ inputs.mtar-file }}
findurl_command: 'cf html5-list -di sflight-destination-service -u'
- name: Assign Roles
uses: ./.github/actions/btp
with:
cli_url: ${{ inputs.btp-cli-url }}
subdomain: ${{ inputs.btp-subdomain }}
subaccount_id: ${{ inputs.btp-subaccount-id }}
role_collection: ${{ inputs.role-collection }}
================================================
FILE: .github/workflows/maven.yml
================================================
# This workflow will build a Java project with Maven
# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven
name: Java CI
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
schedule:
- cron: 30 4 * * *
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
java-version: [21]
steps:
- uses: actions/checkout@v4
- name: Set up Java ${{ matrix.java-version }}
uses: actions/setup-java@v4
with:
java-version: ${{ matrix.java-version }}
distribution: 'sapmachine'
- run: npm ci
- name: Build with Maven
run: mvn -B clean verify
- name: Run integration tests - Manage Travels
working-directory: ./app/travel_processor
run: |
npm run test:java -- --ci
- name: Run integration tests - Analyze Bookings
working-directory: ./app/travel_analytics
run: |
npm run test:java -- --ci
================================================
FILE: .github/workflows/node.js.yml
================================================
# This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
name: Node.js CI
on:
workflow_dispatch:
push:
branches: [ main ]
pull_request:
branches: [ main ]
types: [ labeled, opened, synchronize, reopened ]
schedule:
- cron: 20 4 * * *
jobs:
build:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
node-version: [22.x, 20.x]
steps:
- uses: actions/checkout@v4
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- run: npm i -g @sap/cds-dk typescript tsx
- run: npm ci
- run: npx cds-typer "*"
- run: npm run lint
- run: npm run build --if-present
- run: npm run test
- run: npm run test:mocha
- name: Run integration tests - Manage Travels
working-directory: ./app/travel_processor
run: |
npm run test:node -- --ci
- name: Run integration tests - Analyze Bookings
working-directory: ./app/travel_analytics
run: |
npm run test:node -- --ci
#
### temporarily switch off deployment as it consistently fails
#
# deployment:
# name: Deploy
# if: ${{ github.event_name == 'push' || contains(github.event.pull_request.labels.*.name, 'deploy') }} # run only if label is set
# uses: ./.github/workflows/deploy-btp.yml
# with:
# cf-space: staging-${{ github.head_ref || github.ref_name }}
# role-collection: sflight-admin-staging-${{ github.head_ref || github.ref_name }} # must match to mta.yaml
# secrets:
# username: ${{ secrets.CF_USERNAME }}
# password: ${{ secrets.CF_PASSWORD }}
# grant-users: ${{ secrets.GRANT_USERS }}
#
# e2etest:
# name: E2E Tests
# runs-on: ubuntu-latest
# needs: deployment
# steps:
# - name: More UI tests
# run: |
# echo ${{ needs.deployment.outputs.ui_app_url }}
================================================
FILE: .gitignore
================================================
# CAP sflight
_out
*.db
connection.properties
default-*.json
*.sql
gen/
node_modules/
target/
.reloadtrigger
@cds-models/
# Web IDE, App Studio
.che/
.gen/
# MTA
*_mta_build_tmp
*.mtar
mta_archives/
/Makefile_*.mta
# Other
.DS_Store
.npmrc
*.orig
*.log
*.flattened-pom.xml
srv/src/main/resources/edmx/**
dist/
/app/resources/
/app/router/package-lock.json
# IDEs
.idea
*.iml
.project
.settings
.classpath
# .vscode/*
# IMPORTANT: Do not exclude .vscode please!
# Local dev
.env
.values.yaml
.cdsrc-private.json
# Exclude Helm chart for this example
/chart
# Experimental
xxx-*
xxx_*
xxx/*
@cds-models
================================================
FILE: .vscode/launch.json
================================================
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "java",
"name": "Launch SFlightApplication",
"request": "launch",
"mainClass": "com.sap.cap.sflight.SFlightApplication",
"projectName": "srv"
},
{
"name": "cds watch",
"command": "npx cds watch",
"env": {
"DEBUG": "sqlite"
},
"type": "node-terminal",
"request": "launch",
"skipFiles": [
"<node_internals>/**",
"**/node_modules/**",
"**/cds/lib/lazy.js",
"**/cds/lib/req/cls.js",
"**/odata-v4/okra/**"
]
}
]
}
================================================
FILE: LICENSE
================================================
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
================================================
FILE: LICENSES/Apache-2.0.txt
================================================
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.
"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:
(a) You must give any other recipients of the Work or Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License.
You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
================================================
FILE: README-Kyma.md
================================================
# Deployment to SAP Business Technology Platform - Kyma Runtime
- [Deployment to SAP Business Technology Platform - Kyma Runtime](#deployment-to-sap-business-technology-platform---kyma-runtime)
- [Preconditions](#preconditions)
- [Add Deployment Files](#add-deployment-files)
- [Configuration](#configuration)
- [Prepare Kubernetes Namespace](#prepare-kubernetes-namespace)
- [Create container registry secret](#create-container-registry-secret)
- [Create a secret for your HDI container](#create-a-secret-for-your-hdi-container)
- [Build - Node.js](#build---nodejs)
- [Build - Java](#build---java)
- [Build HTML5 application deployer image](#build-html5-application-deployer-image)
- [Push docker images](#push-docker-images)
- [Deployment](#deployment)
- [Access the UI](#access-the-ui)
**TIP:** You can find more information in the [Deploy Your CAP Application on SAP BTP Kyma Runtime](https://developers.sap.com/mission.btp-deploy-cap-kyma.html) tutorial and in the [Deploy to Kyma/K8s](https://cap.cloud.sap/docs/guides/deployment/deploy-to-kyma) guide of the CAP documentation.
## Preconditions
- BTP Subaccount with Kyma Runtime
- BTP Subaccount with Cloud Foundry Space
- HANA Cloud instance available for your Cloud Foundry space
- BTP Entitlements for: *HANA HDI Services & Container* plan *hdi-shared*, *Build Work Zone* plan *standard*
- Container Registry
- Command Line Tools: `kubectl`, `kubectl-oidc_login`, `pack`, `docker`, `helm`, `cf`
- Logged into Kyma Runtime (with `kubectl` CLI), Cloud Foundry space (with `cf` CLI) and Container Registry (with `docker login`)
- `@sap/cds-dk` >= 6.6.0
## Add Deployment Files
CAP tooling provides your a Helm chart for deployment to Kyma.
Add the CAP Helm chart with the required features to this project:
```bash
cds add helm
cds add html5-repo
```
## Prepare Kubernetes Namespace
1. Export the kubeconfig.yaml
```
set KUBECONFIG=~/.kube/cap-kyma-app-config
```
2. Setting the namespace
```
kubectl config set-context --current --namespace=<<NAMESPACE>>
```
### Create container registry secret
Create a secret `container-registry` with credentials to access the container registry:
```
bash .github/deployment/kyma/scripts/create-container-registry-secret.sh
```
The *Docker Server* is the full-qualified hostname of your container registry.
### Create a secret for your HDI container
This step is only required if you're using a BTP Trial account. If you're using a production or a free tier account then you can create HDI Container from Kyma directly by adding a [mapping to your Kyma namespace in your HANA Cloud Instance](https://blogs.sap.com/2022/12/15/consuming-sap-hana-cloud-from-the-kyma-environment/) and skip this step.
```
bash .github/deployment/kyma/scripts/create-db-secret.sh sflight-db
```
It will create a HDI container `sflight-db` instance on your currently targeted Cloud Foundry space and creates a secret `sflight-db` with the credentials in your current Kubernetes namespace.
Make the following changes to your _`chart/values.yaml`_.
```diff
srv:
bindings:
db:
- serviceInstanceName: hana
+ fromSecret: sflight-db
...
hana-deployer:
bindings:
hana:
- serviceInstanceName: hana
+ fromSecret: sflight-db
...
- hana:
- serviceOfferingName: hana
- servicePlanName: hdi-shared
```
## Configuration
Make the following changes to your _`chart/values.yaml`_ file:
1. Change value of `global.domain` key to your cluster domain.
2. Replace `<your-container-registry>` with your container registry.
3. Set the value of `SAP_CLOUD_SERVICE` key.
```diff
html5-apps-deployer:
env:
- SAP_CLOUD_SERVICE: null
+ SAP_CLOUD_SERVICE: sap.fe.cap.sflight
```
4. Add backend destinations required by HTML5 Apps Deployer.
```diff
- backendDestinations: {}
+ backendDestinations:
+ sflight-srv:
+ service: srv
```
5. Add your image registry secret created in [Create container registry secret](#create-container-registry-secret) step.
```diff
global:
domain: null
- imagePullSecret: {}
+ imagePullSecret:
+ name: container-registry
```
## Build - Node.js
Do the following steps if you want to deploy the **Node.js** application.
The `CDS_ENV=node` env variable needs to be provided to build for Node.js. The application will be built for Java by default.
```
CDS_ENV=node cds build --production
```
**Build data base deployer image:**
```bash
pack build $YOUR_CONTAINER_REGISTRY/sflight-hana-deployer \
--path gen/db \
--buildpack gcr.io/paketo-buildpacks/nodejs \
--builder paketobuildpacks/builder-jammy-base \
--env BP_NODE_RUN_SCRIPTS=""
```
(Replace `$YOUR_CONTAINER_REGISTRY` with the full-qualified hostname of your container registry)
**Build image for CAP service:**
```bash
pack build $YOUR_CONTAINER_REGISTRY/sflight-srv \
--path "gen/srv" \
--buildpack gcr.io/paketo-buildpacks/nodejs \
--builder paketobuildpacks/builder-jammy-base \
--env BP_NODE_RUN_SCRIPTS=""
```
## Build - Java
Do the following steps if you want to deploy the **Java** application.
**Build data base deployer image:**
```bash
cds build --production
```
```bash
pack build $YOUR_CONTAINER_REGISTRY/sflight-hana-deployer \
--path gen/db \
--buildpack gcr.io/paketo-buildpacks/nodejs \
--builder paketobuildpacks/builder-jammy-base \
--env BP_NODE_RUN_SCRIPTS=""
```
(Replace `$YOUR_CONTAINER_REGISTRY` with the full-qualified hostname of your container registry)
**Build image for CAP service:**
```bash
mvn package
```
```bash
pack build $YOUR_CONTAINER_REGISTRY/sflight-srv \
--path srv/target/*-exec.jar \
--buildpack gcr.io/paketo-buildpacks/sap-machine \
--buildpack gcr.io/paketo-buildpacks/java \
--builder paketobuildpacks/builder-jammy-base \
--env SPRING_PROFILES_ACTIVE=cloud \
--env BP_JVM_VERSION=17
```
## Build HTML5 application deployer image
```
bash .github/deployment/kyma/scripts/build-ui-image.sh
```
## Push docker images
You can push all the docker images to your docker registry, using:
```bash
docker push $YOUR_CONTAINER_REGISTRY/sflight-hana-deployer
docker push $YOUR_CONTAINER_REGISTRY/sflight-srv
docker push $YOUR_CONTAINER_REGISTRY/sflight-html5-deployer
```
## Deployment
```bash
helm install sflight ./chart --set-file xsuaa.jsonParameters=xs-security.json
```
## Access the UI
1. Create Build Work Zone subscription in the BTP Cockpit
2. Create a role collection `sflight`
3. Add role `admin` of `sflight.tXYZ` application to role collection
4. Add your user to the role collection
5. Goto **HTML5 Applications**
6. Start HTML5 application `sapfecaptravel`
Additionally, you can add the UI to a Build Work Zone site like it is described in in the last two steps of [this tutorial](https://developers.sap.com/tutorials/btp-app-kyma-launchpad-service.html#9aab2dd0-18ea-4ccd-bc44-24e87c845740).
================================================
FILE: README.md
================================================
# Welcome to the CAP SFLIGHT App
This is a sample app for the travel reference scenario, built with the [SAP Cloud Application Programming Model (CAP)](https://cap.cloud.sap) and [SAP Fiori Elements](https://experience.sap.com/fiori-design-web/smart-templates).
The purpose of this sample app is to:
* Demonstrate SAP Fiori annotations
* Demonstrate and compare SAP Fiori features on various stacks (CAP Node.js, CAP Java SDK, ABAP)
* Run UI test suites on various stacks

The app still contains some workarounds that are going to be addressed over time.
In some cases, the model and the handlers can be improved or simplified once further planned CAP features become available.
In other cases, the app itself could be improved. For example, calculation of the total price for a travel
currently simply sums up the single prices ignoring the currencies.
> For enabling all features of the Analytical List Page (ALP) in the Node.js runtime, we have switched on the new OData parser
(`odata_new_parser: true` in `package.json`), which is still in an **experimental state**.
Early adopters may use this feature in own projects on their own risk.
You can also use the ALP with the standard OData parser, but then some features like grouping in the table are not available.

[](https://api.reuse.software/info/github.com/SAP-samples/cap-sflight)
## Run locally
### Build and Run - Node.js Backend
Prerequisite:
```
npm i -g @sap/cds-dk
```
In the root folder of your project, run
```
npm ci
cds watch
```
#### Accessing the SAP Fiori Apps
Open these links in your browser:
* http://localhost:4004/sap.fe.cap.travel/index.html for processing the travel data
* http://localhost:4004/sap.fe.cap.travel_analytics/index.html for the [Analytical List Page](https://ui5.sap.com/#/topic/3d33684b08ca4490b26a844b6ce19b83) (ALP)
### Build and Run - Java Backend
In the root folder of your project, run
```
npm ci
npm run build:ui
mvn spring-boot:run
```
> At the moment, there is no watch mode for Fiori UI changes. Run `npm run build:ui` after each change there.
#### Accessing the SAP Fiori Apps
Open these links in your browser:
* http://localhost:4004/travel_processor/dist/index.html for processing the travel data
* http://localhost:4004/travel_analytics/dist/index.html for the [Analytical List Page](https://ui5.sap.com/#/topic/3d33684b08ca4490b26a844b6ce19b83) (ALP)
Log in with user `amy` and empty password.
### Integration Tests
To start OPA tests, open this link in your browser:
http://localhost:4004/travel_processor/webapp/test/integration/Opa.qunit.html
Test documentation is available at:
https://ui5.sap.com/#/api/sap.fe.test
## Deployment to SAP Business Technology Platform
The project contains a configuration for deploying the CAP services and the SAP Fiori app to the SAP Business Technology Platform (SAP BTP) using a managed application router. The app then becomes visible in the content manager of the SAP Build Work Zone.
The configuration file `mta.yaml` is for the Node.js backend of the app. If you want to deploy the Java backend, copy `mta-java.yaml` to `mta.yaml`.
### Prerequisites
#### SAP Business Technology Platform
- Create a [trial account on SAP BTP](https://www.sap.com/products/business-technology-platform/trial.html). See this [tutorial](https://developers.sap.com/tutorials/hcp-create-trial-account.html) for more information. Alternatively, you can use a sub-account in a productive environment.
- Get a [SAP Cloud Identity Services tenant](https://help.sap.com/docs/cloud-identity-services/cloud-identity-services/get-your-tenant?version=Cloud#get-trial-tenant) and [establish trust](https://help.sap.com/docs/btp/sap-business-technology-platform/establish-trust-and-federation-between-uaa-and-identity-authentication?version=Cloud)
- Subscribe to the [SAP Build Work Zone](https://developers.sap.com/tutorials/cp-portal-cloud-foundry-getting-started.html).
- Create an [SAP HANA Cloud Service instance](https://developers.sap.com/tutorials/hana-cloud-mission-trial-2.html) or use an existing one. Then, [provision an instance](https://developers.sap.com/tutorials/hana-cloud-mission-trial-3.html) - make sure to choose the Cloud Foundry Runtime environment.
#### Local Machine
- Install the Cloud Foundry command line interface (CLI). See this [tutorial](https://developers.sap.com/tutorials/cp-cf-download-cli.html) for more details.
- Install the [MultiApps CF CLI Plugin](https://github.com/cloudfoundry-incubator/multiapps-cli-plugin):
```shell
cf add-plugin-repo CF-Community https://plugins.cloudfoundry.org
cf install-plugin multiapps
```
- Install the [Cloud MTA Build Tool](https://github.com/SAP/cloud-mta-build-tool) globally:
```shell
npm install -g mbt
```
### Build the Project
Build the project from the command line:
```shell
mbt build
```
The build results will be stored in the directory `mta_archives`.
### Deploy
1. Log in to the target space.
2. Deploy the MTA archive using the CF CLI: `cf deploy mta_archives/capire.sflight_1.0.0.mtar`
### Assign Role Collection
Any authorized user has read access to the app. For further authorization, assign a role collection to your user in the SAP BTP Cockpit:
* `sflight-reviewer-{spacename}` for executing actions *Accept Travel*, *Reject Travel*, and *Deduct Discount*
* `sflight-processor-{spacename}` for full write access
### Integrate SFlight with SAP Build Work Zone, Standard Edition
CAP SFlight uses the managed AppRouter, which in case of a trial account, is provided by the Navigation Service in SAP Build Work Zone, Standard Edition. Please consult [this tutorial](https://developers.sap.com/tutorials/integrate-with-work-zone.html) to make sure that your Build Work Zone is configured correctly to serve the CAP SFlight Frontend.
### Local Development with a HANA Cloud Instance
You need to have access to a HANA Cloud instance and SAP BTP.
1. Deploy the HDI content to a HANA HDI container (which is newly created on first call): `cds deploy --to hana`.
2. Start the application with the Spring Profile `cloud`.
1. From Maven: `mvn spring-boot:run -Dspring-boot.run.profiles=cloud`
2. From your IDE with the JVM argument `-Dspring.profiles.active=cloud` or env variable `spring.profiles.active=cloud`
The running application is now connected to its own HDI container/schema. Please keep in mind that the credentials for
that HDI container are stored locally on your filesystem (default-env.json).
## Deployment to SAP Business Technology Platform - Kyma Runtime
The deployment to Kyma Runtime is explained in file [README-Kyma.md](./README-Kyma.md).
## Creating an SAP Fiori App from Scratch
If you want to implement an SAP Fiori app, follow these tutorials:
* [Create a List Report Object Page App with SAP Fiori Tools](https://developers.sap.com/group.fiori-tools-lrop.html)
* [Developing SAP Fiori applications with SAP Fiori Tools](https://help.sap.com/viewer/17d50220bcd848aa854c9c182d65b699/Latest/en-US)
## Get Support
In case you've a question, find a bug, or otherwise need support, use the [SAP Community](https://answers.sap.com/tags/9f13aee1-834c-4105-8e43-ee442775e5ce) to get more visibility.
## License
Copyright (c) 2021 SAP SE or an SAP affiliate company and cap-sflight contributors. This project is licensed under the Apache Software License, version 2.0 except as noted otherwise in the [LICENSE](LICENSES/Apache-2.0.txt) file.
================================================
FILE: REUSE.toml
================================================
version = 1
SPDX-PackageName = "cap-sflight"
SPDX-PackageSupplier = "<Steffen Weinstock (steffen.weinstock@sap.com)>"
SPDX-PackageDownloadLocation = "https://github.com/sap-samples/cap-sflight"
SPDX-PackageComment = "The code in this project may include calls to APIs (“API Calls”) of\n SAP or third-party products or services developed outside of this project\n (“External Products”).\n “APIs” means application programming interfaces, as well as their respective\n specifications and implementing code that allows software to communicate with\n other software.\n API Calls to External Products are not licensed under the open source license\n that governs this project. The use of such API Calls and related External\n Products are subject to applicable additional agreements with the relevant\n provider of the External Products. In no event shall the open source license\n that governs this project grant any rights in or to any External Products,or\n alter, expand or supersede any terms of the applicable additional agreements.\n If you have a valid license agreement with SAP for the use of a particular SAP\n External Product, then you may make use of any API Calls included in this\n project’s code for that SAP External Product, subject to the terms of such\n license agreement. If you do not have a valid license agreement for the use of\n a particular SAP External Product, then you may only make use of any API Calls\n in this project for that SAP External Product for your internal, non-productive\n and non-commercial test and evaluation of such API Calls. Nothing herein grants\n you any rights to use or access any SAP External Product, or provide any third\n parties the right to use of access any SAP External Product, through API Calls."
[[annotations]]
path = "**"
precedence = "aggregate"
SPDX-FileCopyrightText = "2021 SAP SE or an SAP affiliate company and cap-sflight contributors"
SPDX-License-Identifier = "Apache-2.0"
================================================
FILE: _i18n/i18n.properties
================================================
# This is the resource bundle for travel_processor
Travel=Travel
Travels=Travels
TravelUUID=Travel UUID
TravelID=Travel
AgencyID=Agency
AgencyName=Agency Name
TravelAgency=Travel Agency
BeginDate=Starting Date
EndDate=End Date
Dates=Dates
TotalPrice=Total Price
TravelStatus=Travel Status
CustomerID=Customer
CustomerName=Customer Name
BookingFee=Booking Fee
Description=Description
Price=Price
Prices=Prices
ProductPrice=Product Price
ProductID=Product
OverallStatus=Overall Status
GeneralInformation=General Information
GoGreen=Trees-4-Tickets
GreenFee=Green Flight Fee
TreesPlanted=Trees Planted
Sustainability=Sustainability
Booking=Booking
Bookings=Bookings
BookingDate=Booking Date
BookingID=Booking ID
BookingNumber=Booking Number
FlightPrice=Flight Price
ConnectionID=Flight Number
FlightDate=Flight Date
FlightNumber=Flight Number
BookingStatus=Booking Status
Airline=Airline
AirlineID=Airline
Name=Name
AirlineNumber=Airline Number
Status=Status
DepartureAirport=Departure Airport
ArrivalAirport=Arrival Airport
Distance=Distance
BookingSupplement=Booking Supplement
BookingSupplements=Booking Supplements
BookingSupplementID=Booking. Supp. Number
SupplementDescription=Supplement Description
SupplementID=Supplement
Supplement=Supplement
SupplementType=Supplement Type
Passenger=Passenger
LastName=Last Name
PlaneType=Plane Type
MaximumSeats=Maximum Seats
OccupiedSeats=Occupied Seats
FirstName=First name
Title=Title
Street=Street
PostalCode=Postal Code
City=City
CountryCode=Country Code
CurrencyCode=Currency Code
PhoneNumber=Phone Number
EMailAddress=EMail Address
WebAddress=Web Address
Flight=Flight
CopyTravel=Copy Travel
AcceptTravel=Accept Travel
RejectTravel=Reject Travel
DeductDiscount=Deduct Discount
MinPrice=Minimal Price
AvgPrice=Average Price
MaxPrice=Maximum Price
TotalPrice=Total Price
BookingDetails=Booking Details
================================================
FILE: _i18n/i18n_de.properties
================================================
# This is the resource bundle for travel_processor
Travel=Reise
Travels=Reisen
TravelUUID=Reise-UUID
TravelID=Reise
AgencyID=Agentur
AgencyName=Agenturname
TravelAgency=Veranstalter
BeginDate=Startdatum
EndDate=Enddatum
Dates=Termine
TotalPrice=Gesamtpreis
TravelStatus=Reisestatus
CustomerID=Kunde
CustomerName=Name des Kunden
BookingFee=Reservierungsgebühr
Description=Beschreibung
Price=Preis
Prices=Preise
ProductPrice=Produktpreis
ProductID=Produkt
OverallStatus=Gesamtstatus
GeneralInformation=Allgemeine Informationen
Booking=Buchung
Bookings=Buchungen
BookingDate=Buchungsdatum
BookingID=Buchungsnummer
BookingNumber=Buchungsnummer
FlightPrice=Flugpreis
ConnectionID=Flugnummer
FlightDate=Flugdatum
FlightNumber=Flugnummer
BookingStatus=Buchungsstatus
Airline=Fluggesellschaft
AirlineID=Fluggesellschaft
Name=Name
AirlineNumber=Fluggesellschaftsnummer
Status=Status
DepartureAirport=Abflughafen
ArrivalAirport=Zielflughafen
Distance=Distanz
BookingSupplement=Buchungszusatz
BookingSupplements=Buchungszusätze
BookingSupplementID=Buchungszusatz
SupplementDescription=Zusatzbeschreibung
SupplementID=Zusatz-ID
Supplement=Zusatz
SupplementType=Zusatzart
Passenger=Passagier
LastName=Nachname
PlaneType=Flugzeugtyp
MaximumSeats=Maximale Sitzplätze
OccupiedSeats=Belegte Plätze
FirstName=Vorname
Title=Titel
Street=Straße
PostalCode=Postleitzahl
City=Ort
CountryCode=Ländercode
CurrencyCode=Währungscode
PhoneNumber=Telefonnummer
EMailAddress=E-Mail-Addresse
WebAddress=Web-Adresse
Flight=Flug
CopyTravel=Reise kopieren
AcceptTravel=Reise akzeptieren
RejectTravel=Reise ablehnen
DeductDiscount=Rabatt abziehen
MinPrice=Niedrigster Preis
AvgPrice=Durchschnittspreis
MaxPrice=Höchster Preis
TotalPrice=Gesamtpreis
BookingDetails=Buchungsdetails
================================================
FILE: _i18n/i18n_en.properties
================================================
# This is the resource bundle for travel_processor
Travel=Travel
Travels=Travels
TravelUUID=Travel UUID
TravelID=Travel
AgencyID=Agency
AgencyName=Agency Name
TravelAgency=Travel Agency
BeginDate=Starting Date
EndDate=End Date
Dates=Dates
TotalPrice=Total Price
TravelStatus=Travel Status
CustomerID=Customer
CustomerName=Customer Name
BookingFee=Booking Fee
Description=Description
Price=Price
Prices=Prices
ProductPrice=Product Price
ProductID=Product
OverallStatus=Overall Status
GeneralInformation=General Information
Booking=Booking
Bookings=Bookings
BookingDate=Booking Date
BookingID=Booking ID
BookingNumber=Booking Number
FlightPrice=Flight Price
ConnectionID=Flight Number
FlightDate=Flight Date
FlightNumber=Flight Number
BookingStatus=Booking Status
Airline=Airline
AirlineID=Airline
Name=Name
AirlineNumber=Airline Number
Status=Status
DepartureAirport=Departure Airport
ArrivalAirport=Arrival Airport
Distance=Distance
BookingSupplement=Booking Supplement
BookingSupplements=Booking Supplements
BookingSupplementID=Booking. Supp. Number
SupplementDescription=Supplement Description
SupplementID=Supplement
Supplement=Supplement
SupplementType=Supplement Type
Passenger=Passenger
LastName=Last Name
PlaneType=Plane Type
MaximumSeats=Maximum Seats
OccupiedSeats=Occupied Seats
FirstName=First name
Title=Title
Street=Street
PostalCode=Postal Code
City=City
CountryCode=Country Code
CurrencyCode=Currency Code
PhoneNumber=Phone Number
EMailAddress=EMail Address
WebAddress=Web Address
Flight=Flight
CopyTravel=Copy Travel
AcceptTravel=Accept Travel
RejectTravel=Reject Travel
DeductDiscount=Deduct Discount
MinPrice=Minimal Price
AvgPrice=Average Price
MaxPrice=Maximum Price
TotalPrice=Total Price
BookingDetails=Booking Details
================================================
FILE: _i18n/i18n_fr.properties
================================================
# This is the resource bundle for travel_processor
Travel=Voyage
Travels=Voyages
TravelUUID=UUID voyage
TravelID=Voyage
AgencyID=Agence
AgencyName=Nom de l'agence
TravelAgency=Travel Agency
BeginDate=Date de début
EndDate=Date de fin
Dates=Dates
TotalPrice=Prix total
TravelStatus=Statut du voyage
CustomerID=Client
CustomerName=Nom du client
BookingFee=Frais de réservation
Description=Description
Price=Prix
Prices=Prix
ProductPrice=Prix du produit
ProductID=Produit
OverallStatus=Statut global
GeneralInformation=Informations générales
Booking=Réservation
Bookings=Réservations
BookingDate=Date d'inscription
BookingID=ID réservation
BookingNumber=Numéro de réservation
FlightPrice=Flight Price
ConnectionID=Flight Number
FlightDate=Date vol
FlightNumber=Nº vol
BookingStatus=Booking Status
Airline=Airline
AirlineID=Airline
Name=Nom
AirlineNumber=Numéro de compagnie aérienne
Status=Statut
DepartureAirport=Aéroport de départ
ArrivalAirport=Aéroport de destination
Distance=Distance
BookingSupplement=Supplément de réservation
BookingSupplements=Réservation de suppléments
BookingSupplementID=Nº Supplément de réservation
SupplementDescription=Description du supplément
SupplementID=Supplément.
Supplement=Supplément
SupplementType=Type de supplément
Passenger=Passager
LastName=Last Name
PlaneType=Type d'avion
MaximumSeats=Nombre maximal de places
OccupiedSeats=Places occup.
FirstName=Prénom
Title=Titre
Street=Rue
PostalCode=Code postal
City=Ville
CountryCode=Code pays
CurrencyCode=Currency Code
PhoneNumber=Phone Number
EMailAddress=Numéro de téléphone
WebAddress=Adresse e-mail
Flight=Vol
CopyTravel=Copier le voyage
AcceptTravel=Accepter le voyage
RejectTravel=Refuser le voyage
DeductDiscount=Déduire remise
MinPrice=Prix minimum
AvgPrice=Prix moyen
MaxPrice=Prix maximum
TotalPrice=Prix total
BookingDetails=Les détails de réservation
================================================
FILE: app/.karma/karma-cap-middleware.js
================================================
const spawn = require("cross-spawn"),
HttpProxy = require("http-proxy");
function spawnServer(cmd, args, cwd, fnIsReady) {
return new Promise((resolve, reject) => {
const proc = spawn(cmd, args, {
cwd,
env: Object.assign({ PORT: 0 }, process.env),
stdio: ["ignore", "pipe", "inherit"],
});
const checkServerReady = (data) => {
const targetUrl = fnIsReady(data.toString());
if (targetUrl) {
proc.stdout.removeListener("data", checkServerReady);
resolve(targetUrl);
}
};
proc.on("close", reject);
proc.stdout.on("data", (data) => {
process.stdout.write(data.toString())
checkServerReady(data)
});
// clean up sub process
process.on("exit", () => {
if (proc) proc.kill("SIGKILL");
});
});
}
function createKarmaMiddleware(serverUrl, auth) {
const proxyOptions = {
target: serverUrl,
auth: auth ? `${auth.user}:${auth.password}` : undefined,
};
const middleware = (logFactory) => {
const log = logFactory.create("cap-server");
const proxy = new HttpProxy(proxyOptions);
proxy.on("error", (data) => log.error(data.toString()));
return (req, res) => proxy.web(req, res);
};
middleware.$inject = ["logger"];
return { "middleware:cap-proxy": ["factory", middleware] };
}
async function java() {
const isReady = (data) => {
const started = data.match(/started on port (?<port>\d+)/);
if (started) return new URL(`http://localhost:${started.groups.port}`);
};
const serverUrl = await spawnServer(
"mvn", ["spring-boot:run", "-B", "-Dspring-boot.run.jvmArguments=-Dserver.port=0"],
"../..",
isReady
);
return createKarmaMiddleware(serverUrl, { user: "admin", password: "admin" });
}
async function node() {
const isReady = (data) => {
const started = data.match(/server listening on {.*url:.*'(?<url>.+)'.*}/);
if (started) {
return new URL(started.groups.url);
}
};
const serverUrl = await spawnServer("cds", ["serve"], "../..", isReady);
return createKarmaMiddleware(serverUrl, { user: "admin", password: "admin" });
}
module.exports = { java, node };
================================================
FILE: app/.karma/karma.conf.js
================================================
const puppeteer = require("puppeteer"),
cap = require("./karma-cap-middleware");
process.env.CHROME_BIN = puppeteer.executablePath();
module.exports = async (config) => {
// start the CAP server (either specify CLI arg --server=node or --server=java)
let capMiddleware;
if (config.server === "node") {
capMiddleware = cap.node();
} else if (config.server === "java") {
capMiddleware = cap.java();
} else {
throw new Error(`Unknown server type: ${config.server}`);
}
config.set({
frameworks: ["ui5"],
preprocessors: {
"webapp/**/*.ts": ["ui5-transpile"],
},
logLevel: "INFO", // log errors only. Change to "DEBUG" for more verbosity
proxies: {
"/base/webapp/": "/",
},
ui5: {
failOnEmptyTestPage: true,
},
plugins: [...config.plugins, await capMiddleware],
middleware: ["cap-proxy"],
browsers: config.ci ? ["ChromeHeadlessNoSandbox"] : ["Chrome"],
customLaunchers: {
ChromeHeadlessNoSandbox: {
base: 'ChromeHeadless',
flags: ['--no-sandbox']
}
},
singleRun: config.ci || config.singleRun || false
});
};
================================================
FILE: app/common.cds
================================================
using { Currency } from '../db/common';
// Workarounds for overly strict OData libs and clients
annotate cds.UUID with @UI.Hidden @odata.Type : 'Edm.String';
annotate Currency with @Common.UnitSpecificScale : 'Decimals';
================================================
FILE: app/labels.cds
================================================
using { sap.fe.cap.travel as schema } from '../db/schema';
//
// annotations that control rendering of fields and labels
//
annotate schema.Travel with @title: '{i18n>Travel}' {
TravelUUID @UI.Hidden;
TravelID @title: '{i18n>TravelID}' @Common.Text: Description;
BeginDate @title: '{i18n>BeginDate}';
EndDate @title: '{i18n>EndDate}';
Description @title: '{i18n>Description}';
BookingFee @title: '{i18n>BookingFee}' @Measures.ISOCurrency: (CurrencyCode.code);
TotalPrice @title: '{i18n>TotalPrice}' @Measures.ISOCurrency: (CurrencyCode.code);
GoGreen @title: '{i18n>GoGreen}';
GreenFee @title: '{i18n>GreenFee}' @Measures.ISOCurrency: (CurrencyCode.code);
TreesPlanted @title: '{i18n>TreesPlanted}';
TravelStatus @title: '{i18n>TravelStatus}' @Common.Text: TravelStatus.name @Common.TextArrangement: #TextOnly;
to_Customer @title: '{i18n>CustomerID}' @Common.Text: to_Customer.LastName;
to_Agency @title: '{i18n>AgencyID}' @Common.Text: to_Agency.Name;
}
annotate schema.TravelStatus with {
code @Common.Text: name @Common.TextArrangement: #TextOnly
}
annotate schema.Booking with @title: '{i18n>Booking}' {
BookingUUID @UI.Hidden;
to_Travel @UI.Hidden;
BookingID @title: '{i18n>BookingID}';
BookingDate @title: '{i18n>BookingDate}';
ConnectionID @title: '{i18n>ConnectionID}';
CurrencyCode @title: '{i18n>CurrencyCode}';
FlightDate @title: '{i18n>FlightDate}';
FlightPrice @title: '{i18n>FlightPrice}' @Measures.ISOCurrency: (CurrencyCode.code);
BookingStatus @title: '{i18n>BookingStatus}' @Common.Text: BookingStatus.name @Common.TextArrangement: #TextOnly;
to_Carrier @title: '{i18n>AirlineID}' @Common.Text: to_Carrier.Name;
to_Customer @title: '{i18n>CustomerID}' @Common.Text: to_Customer.LastName;
}
annotate schema.BookingStatus with {
code @Common.Text : name @Common.TextArrangement: #TextOnly
}
annotate schema.BookingSupplement with @title: '{i18n>BookingSupplement}' {
BookSupplUUID @UI.Hidden;
to_Booking @UI.Hidden;
to_Travel @UI.Hidden;
to_Supplement @title: '{i18n>SupplementID}' @Common.Text: to_Supplement.Description;
Price @title: '{i18n>Price}' @Measures.ISOCurrency: (CurrencyCode.code);
BookingSupplementID @title: '{i18n>BookingSupplementID}';
CurrencyCode @title: '{i18n>CurrencyCode}';
}
annotate schema.TravelAgency with @title: '{i18n>TravelAgency}' {
AgencyID @title: '{i18n>AgencyID}' @Common.Text: Name;
Name @title: '{i18n>AgencyName}';
Street @title: '{i18n>Street}';
PostalCode @title: '{i18n>PostalCode}';
City @title: '{i18n>City}';
CountryCode @title: '{i18n>CountryCode}';
PhoneNumber @title: '{i18n>PhoneNumber}';
EMailAddress @title: '{i18n>EMailAddress}';
WebAddress @title: '{i18n>WebAddress}';
}
annotate schema.Passenger with @title: '{i18n>Passenger}' {
CustomerID @title: '{i18n>CustomerID}' @Common.Text: LastName;
FirstName @title: '{i18n>FirstName}';
LastName @title: '{i18n>LastName}';
Title @title: '{i18n>Title}';
Street @title: '{i18n>Street}';
PostalCode @title: '{i18n>PostalCode}';
City @title: '{i18n>City}';
CountryCode @title: '{i18n>CountryCode}';
PhoneNumber @title: '{i18n>PhoneNumber}';
EMailAddress @title: '{i18n>EMailAddress}';
}
annotate schema.Airline with @title: '{i18n>Airline}' {
AirlineID @title: '{i18n>AirlineID}' @Common.Text: Name;
Name @title: '{i18n>Name}';
CurrencyCode @title: '{i18n>CurrencyCode}';
}
annotate schema.Flight with @title: '{i18n>Flight}' {
AirlineID @title: '{i18n>AirlineID}';
FlightDate @title: '{i18n>FlightDate}';
ConnectionID @title: '{i18n>ConnectionID}';
CurrencyCode @title: '{i18n>CurrencyCode}';
Price @title: '{i18n>Price}' @Measures.ISOCurrency: (CurrencyCode.code);
PlaneType @title: '{i18n>PlaneType}';
MaximumSeats @title: '{i18n>MaximumSeats}';
OccupiedSeats @title: '{i18n>OccupiedSeats}';
}
annotate schema.Supplement with @title: '{i18n>Supplement}' {
SupplementID @title: '{i18n>SupplementID}' @Common.Text: Description;
Price @title: '{i18n>Price}' @Measures.ISOCurrency: (CurrencyCode.code);
CurrencyCode @title: '{i18n>CurrencyCode}';
Description @title: '{i18n>Description}';
}
================================================
FILE: app/services.cds
================================================
using from './travel_processor/capabilities';
using from './travel_processor/field-control';
using from './travel_processor/layouts';
using from './travel_analytics/annotations';
================================================
FILE: app/travel_analytics/annotations.cds
================================================
using AnalyticsService as service from '../../srv/analytics-service';
annotate service.Bookings with @(
Aggregation.CustomAggregate #FlightPrice : 'Edm.Decimal',
Aggregation.CustomAggregate #CurrencyCode_code : 'Edm.String',
Common.SemanticKey : [ID],
) {
ID @ID : 'ID';
FlightPrice @Aggregation.default: #SUM;
CurrencyCode_code @Aggregation.default: #MAX;
};
annotate service.Bookings with @Aggregation.ApplySupported : {
Transformations : [
'aggregate',
'topcount',
'bottomcount',
'identity',
'concat',
'groupby',
'filter',
'search'
],
GroupableProperties : [
TravelID,
BookingID,
CombinedID,
ConnectionID,
FlightDate,
CurrencyCode_code,
status,
airline,
],
AggregatableProperties : [
{Property : status },
{Property : FlightPrice },
{Property : ID },
],
};
annotate service.Bookings with @(
Analytics.AggregatedProperty #countBookings :
{
Name : 'countBookings',
AggregationMethod : 'countdistinct',
AggregatableProperty : ID,
@Common.Label : '{i18n>Bookings}'
},
Analytics.AggregatedProperty #minPrice :
{
Name : 'minPrice',
AggregationMethod : 'min',
AggregatableProperty : FlightPrice,
@Common.Label : '{i18n>MinPrice}'
},
Analytics.AggregatedProperty #maxPrice :
{
Name : 'maxPrice',
AggregationMethod : 'max',
AggregatableProperty : FlightPrice,
@Common.Label : '{i18n>MaxPrice}'
},
Analytics.AggregatedProperty #avgPrice :
{
Name : 'avgPrice',
AggregationMethod : 'average',
AggregatableProperty : FlightPrice,
@Common.Label : '{i18n>AvgPrice}'
},
// measure "sum of prices" is available by default (but name/label doesn't indicate summing -> ?)
// Analytics.AggregatedProperty #sumPrice :
// {
// Name : 'sumPrice',
// AggregationMethod : 'sum',
// AggregatableProperty : FlightPrice,
// @Common.Label : '{i18n>TotalPrice}'
// }
);
annotate service.Bookings with @UI.LineItem : [
{
Value : TravelID,
@UI.Importance : #High,
@HTML5.CssDefaults: {width:'8em'},
}, {
Value : BookingID,
Label : '{i18n>Booking}',
@UI.Importance : #High,
@HTML5.CssDefaults: {width:'8em'},
}, {
Value : airline,
@UI.Importance : #High,
@HTML5.CssDefaults: {width:'14em'},
}, {
Value : ConnectionID,
@UI.Importance : #High,
@HTML5.CssDefaults: {width:'8em'},
}, {
Value : FlightDate,
@UI.Importance : #High,
}, {
Value : FlightPrice,
@UI.Importance : #High,
@HTML5.CssDefaults: {width:'12em'},
}, {
Value : status,
@UI.Importance : #High,
@HTML5.CssDefaults: {width:'8em'},
}
];
annotate service.Bookings with @UI.Chart : {
Title : '{i18n>Bookings}',
ChartType : #Column,
DynamicMeasures : [
'@Analytics.AggregatedProperty#minPrice',
'@Analytics.AggregatedProperty#maxPrice',
'@Analytics.AggregatedProperty#avgPrice'
],
Dimensions : [airline],
MeasureAttributes : [{
DynamicMeasure : '@Analytics.AggregatedProperty#minPrice',
Role : #Axis1
}],
DimensionAttributes : [{
Dimension : airline,
Role : #Category
}],
};
annotate service.Bookings with @UI.PresentationVariant : {
GroupBy : [ // default grouping in table
airline,
status
],
Total : [ // default aggregation in table
FlightPrice
],
Visualizations : [
'@UI.Chart',
'@UI.LineItem',
]
};
//
// Visual Filters
//
annotate service.Bookings with @(
UI.PresentationVariant #pvAirline : {
Visualizations : ['@UI.Chart#chartAirline']
},
UI.Chart #chartAirline : {
ChartType : #Bar,
DynamicMeasures : [
'@Analytics.AggregatedProperty#countBookings',
],
Dimensions : [airline],
MeasureAttributes : [{
DynamicMeasure : '@Analytics.AggregatedProperty#countBookings',
Role : #Axis1,
}],
DimensionAttributes : [{
Dimension : airline,
Role : #Category
}],
}
) {
airline @(
Common.ValueList #vlAirline : {
CollectionPath : 'Bookings',
PresentationVariantQualifier : 'pvAirline',
Parameters : [{
$Type : 'Common.ValueListParameterInOut',
LocalDataProperty : airline,
ValueListProperty : 'airline'
}]
},
Common.ValueList : {
CollectionPath : 'Airline',
Parameters : [{
$Type : 'Common.ValueListParameterInOut',
LocalDataProperty : airline,
ValueListProperty : 'AirlineID',
}, {
$Type : 'Common.ValueListParameterDisplayOnly',
ValueListProperty : 'Name',
}]
}
)
};
annotate service.Bookings with @(
UI.PresentationVariant #pvStatus : {
Visualizations : ['@UI.Chart#chartStatus']
},
UI.Chart #chartStatus : {
ChartType : #Bar,
DynamicMeasures : [
'@Analytics.AggregatedProperty#countBookings',
],
Dimensions : [status],
MeasureAttributes : [{
DynamicMeasure : '@Analytics.AggregatedProperty#countBookings',
Role : #Axis1,
}],
DimensionAttributes : [{
Dimension : status,
Role : #Category
}]
}
) {
status @(
Common.ValueList #vlStatus : {
CollectionPath : 'Bookings',
PresentationVariantQualifier : 'pvStatus',
Parameters : [{
$Type : 'Common.ValueListParameterInOut',
LocalDataProperty : status,
ValueListProperty : 'status'
}]
},
Common.ValueList : {
CollectionPath : 'BookingStatus',
Parameters : [{
$Type : 'Common.ValueListParameterInOut',
LocalDataProperty : status,
ValueListProperty : 'code',
}]
},
//Common.ValueListWithFixedValues : true
)
};
annotate service.Bookings with @(
UI.PresentationVariant #pvFlightDate : {
SortOrder : [{
Property : FlightDate,
Descending : true
}],
Visualizations : ['@UI.Chart#chartFlightDate']
},
UI.Chart #chartFlightDate : {
Title : 'Bookings over FlightDate',
ChartType : #Line,
DynamicMeasures : [
'@Analytics.AggregatedProperty#countBookings',
],
Dimensions : [FlightDate],
MeasureAttributes : [{
DynamicMeasure : '@Analytics.AggregatedProperty#countBookings',
Role : #Axis1,
}],
DimensionAttributes : [{
Dimension : FlightDate,
Role : #Category
}]
}
) {
FlightDate @Common.ValueList #vlFlightDate : {
CollectionPath : 'Bookings',
PresentationVariantQualifier : 'pvFlightDate',
Parameters : [{
$Type : 'Common.ValueListParameterInOut',
LocalDataProperty : FlightDate,
ValueListProperty : 'FlightDate'
}]
};
};
//
// KPI
//
annotate service.Bookings with @(
UI.KPI #myKPI1 : {
DataPoint : {
Value : FlightPrice,
Title : 'TOT',
Description : '{i18n>Total Price}',
CriticalityCalculation : {
ImprovementDirection: #Maximize,
AcceptanceRangeLowValue: 26000000,
ToleranceRangeLowValue: 24000000,
DeviationRangeLowValue: 22000000
}
},
Detail : {
DefaultPresentationVariant : {
Visualizations : [
'@UI.Chart#kpi1'
],
},
},
SelectionVariant : {
SelectOptions : [{
PropertyName : FlightPrice,
Ranges : [{
Sign : #E,
Option : #EQ,
Low : 0,
}, ],
}],
}
},
UI.Chart #kpi1 : {
ChartType : #Line,
Measures : [FlightPrice],
Dimensions : [FlightDate],
MeasureAttributes : [{
Measure : FlightPrice,
Role : #Axis1
}],
DimensionAttributes : [{
Dimension : FlightDate,
Role : #Category
}]
},
);
//
// Detail page
//
annotate service.Bookings with @UI : {
Identification : [
{ Value : CombinedID },
],
HeaderInfo : {
TypeName : '{i18n>Bookings}',
TypeNamePlural : '{i18n>Bookings}',
Title : { Value : CombinedID },
Description : { Value : CombinedID }
},
Facets : [{
$Type : 'UI.CollectionFacet',
Label : '{i18n>BookingDetails}',
ID : 'Booking',
Facets : [{
$Type : 'UI.ReferenceFacet',
ID : 'TravelData',
Target : '@UI.FieldGroup#TravelInformation',
Label : '{i18n>Travel}'
}, {
$Type : 'UI.ReferenceFacet',
ID : 'BookingData',
Target : '@UI.FieldGroup#BookingInformation',
Label : '{i18n>Booking}'
}, {
$Type : 'UI.ReferenceFacet',
ID : 'FlightData',
Target : '@UI.FieldGroup#FlightInformation',
Label : '{i18n>Flight}'
}]
}],
FieldGroup #TravelInformation : { Data : [
{ Value : to_Travel.TravelID,
Label : '{i18n>TravelID}' },
{ Value : to_Travel.Description },
{ Value : to_Travel.to_Agency.Name, },
{ Value : to_Travel.CustomerName, },
{ Value : to_Travel.TravelStatus.code,
Label : '{i18n>Status}' }, // why does the label not come from below?
]},
FieldGroup #BookingInformation : { Data : [
{ Value : BookingID,
Label : '{i18n>BookingID}' },
{ Value : BookingDate },
{ Value : FlightDate, },
{ Value : FlightPrice },
{ Value : status,
Label : '{i18n>Status}' },
]},
FieldGroup #FlightInformation : { Data : [
{ Value : airline, },
{ Value : to_Carrier.AirlinePicURL, },
{ Value : ConnectionID },
// Java doesn't work with these association paths
// { Value : to_Flight.PlaneType },
// { Value : to_Flight.to_Connection.DepartureAirport.AirportID,
// Label: '{i18n>DepartureAirport}' },
// { Value : to_Flight.to_Connection.DestinationAirport.AirportID,
// Label: '{i18n>ArrivalAirport}' },
// { Value : to_Flight.to_Connection.Distance, },
// Workaround:
{ Value : PlaneType },
{ Value : DepAirport, },
{ Value : DestAirport },
{ Value : Distance, },
]},
};
// determines the order of visual filters
annotate service.Bookings with @UI.SelectionFields : [
FlightDate,
status,
airline
];
================================================
FILE: app/travel_analytics/karma.conf.js
================================================
module.exports = require("../.karma/karma.conf.js")
================================================
FILE: app/travel_analytics/package.json
================================================
{
"name": "travel-analytics",
"version": "1.0.0",
"private": true,
"description": "SFlight ALP",
"main": "webapp/index.html",
"engines": {
"node": ">=18"
},
"scripts": {
"build": "ui5 build preload --clean-dest --include-task=generateCachebusterInfo",
"start": "ui5 serve",
"test": "npm run test:node && npm run test:java",
"test:java": "karma start --server=java --single-run",
"test:node": "karma start --server=node --single-run",
"ts-typecheck": "tsc --noEmit",
"prestart": "npm run ts-typecheck",
"prebuild": "npm run ts-typecheck",
"deploy-config": "npx -p @sap/ux-ui5-tooling fiori add deploy-config cf"
},
"keywords": [
"ui5",
"openui5",
"sapui5"
],
"devDependencies": {
"@sap/ux-ui5-tooling": "1",
"@sapui5/types": "^1.139.0",
"@typescript-eslint/eslint-plugin": "^8",
"@typescript-eslint/parser": "^8",
"@ui5/cli": "^4.0.0",
"karma": "^6.4.3",
"karma-chrome-launcher": "^3.2.0",
"karma-ui5": "^4.0.1",
"karma-ui5-transpile": "^3.5.1",
"puppeteer": "^24",
"typescript": "^5.1.6",
"ui5-middleware-simpleproxy": "^3.2.16",
"ui5-task-zipper": "^3.1.4",
"ui5-tooling-transpile": "^3.3.7"
}
}
================================================
FILE: app/travel_analytics/tsconfig.json
================================================
{
"compilerOptions": {
"target": "es2022",
"module": "es2022",
"skipLibCheck": true,
"allowJs": true,
"strict": true,
"strictPropertyInitialization": false,
"moduleResolution": "node",
"rootDir": "./webapp",
"outDir": "./dist",
"baseUrl": "./",
"paths": {
"sap/fe/cap/travel_analytics/*": [
"./webapp/*"
]
},
"typeRoots": [
"./node_modules/@types",
"./node_modules/@sapui5/types",
"../../node_modules/@types",
"../../node_modules/@sapui5/types"
]
},
"include": [
"./webapp/**/*"
]
}
================================================
FILE: app/travel_analytics/ui5.yaml
================================================
# yaml-language-server: $schema=https://sap.github.io/ui5-tooling/schema/ui5.yaml.json
specVersion: "3.0"
metadata:
name: sap.fe.cap.travel_analytics
type: application
resources:
configuration:
propertiesFileSourceEncoding: UTF-8
builder:
resources:
excludes:
- "/test/**"
- "/localService/**"
customTasks:
- name: ui5-task-zipper
afterTask: generateCachebusterInfo
configuration:
archiveName: travel-analytics
additionalFiles:
- xs-app.json
- name: ui5-tooling-transpile-task
afterTask: replaceVersion
# configuration:
# debug: true
server:
customMiddleware:
- name: ui5-middleware-simpleproxy
mountPath: /analytics
afterMiddleware: compression
configuration:
baseUri: http://localhost:4004/analytics
username: admin # dummy credentials for local testing
password: admin # dummy credentials for local testing
- name: fiori-tools-appreload
afterMiddleware: compression
- name: ui5-tooling-transpile-middleware
afterMiddleware: compression
configuration:
# debug: true
excludePatterns:
- /Component-preload.js
================================================
FILE: app/travel_analytics/webapp/Component.ts
================================================
import BaseComponent from "sap/fe/core/AppComponent";
/**
* @namespace sap.fe.cap.travel_analytics
*/
export default class Component extends BaseComponent {
public static metadata = {
manifest: "json"
};
/**
* The component is initialized by UI5 automatically during the startup of the app and calls the init method once.
* @public
* @override
*/
//public init() : void {
// super.init();
//}
}
================================================
FILE: app/travel_analytics/webapp/i18n/i18n.properties
================================================
# This is the resource bundle for travel_processor
#Texts for manifest.json
#XTIT: Application name
title=Analyze Bookings
#YDES: Application description
description=Analyze Bookings
================================================
FILE: app/travel_analytics/webapp/i18n/i18n_de.properties
================================================
# This is the resource bundle for travel_processor
#Texts for manifest.json
#XTIT: Application name
title=Buchungen analysieren
#YDES: Application description
description=Buchungen analysieren
================================================
FILE: app/travel_analytics/webapp/i18n/i18n_en.properties
================================================
# This is the resource bundle for travel_processor
#Texts for manifest.json
#XTIT: Application name
title=Analyze Bookings
#YDES: Application description
description=Analyze Bookings
================================================
FILE: app/travel_analytics/webapp/i18n/i18n_fr.properties
================================================
# This is the resource bundle for travel_processor
#Texts for manifest.json
#XTIT: Application name
title=Analyser les réservations
#YDES: Application description
description=Analyser les réservations
================================================
FILE: app/travel_analytics/webapp/index.html
================================================
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title></title>
<style>
html,
body,
body > div,
#container,
#container-uiarea {
height: 100%;
}
</style>
<script
id="sap-ui-bootstrap"
src="https://ui5.sap.com/1.139.0/resources/sap-ui-core.js"
data-sap-ui-theme="sap_horizon"
data-sap-ui-oninit="module:sap/ui/core/ComponentSupport"
data-sap-ui-resourceroots='{ "sap.fe.cap.travel_analytics": "./" }'
data-sap-ui-compatVersion="edge"
data-sap-ui-async="true"
data-sap-ui-preload="async"
data-sap-ui-flexibilityServices='[{"connector": "SessionStorageConnector"}]'
data-sap-ui-frameOptions="trusted"
data-sap-ui-libs="sap.m, sap.fe.core"
></script>
</head>
<body class="sapUiBody sapUiSizeCompact" id="content">
<div
data-sap-ui-component
data-name="sap.fe.cap.travel_analytics"
data-id="container"
data-settings='{"id" : "sap.fe.cap.travel_analytics"}'
data-handle-validation="true"
></div>
</body>
</html>
================================================
FILE: app/travel_analytics/webapp/manifest.json
================================================
{
"_version": "1.42.0",
"sap.app": {
"id": "sap.fe.cap.travel_analytics",
"type": "application",
"title": "{{title}}",
"description": "{{description}}",
"i18n": {
"bundleName": "sap.fe.cap.travel_analytics.i18n.i18n",
"supportedLocales": [
"en",
"de",
"fr"
],
"fallbackLocale": "en"
},
"applicationVersion": {
"version": "1.0.0"
},
"dataSources": {
"mainService": {
"uri": "analytics/",
"type": "OData",
"settings": {
"odataVersion": "4.0"
}
}
},
"resources": "resources.json",
"sourceTemplate": {
"id": "@sap/generator-fiori:lrop",
"version": "1.7.5",
"toolsId": "14e5ba92-7ef6-40ce-9b6b-9eff19d0c9a2"
},
"crossNavigation": {
"inbounds": {
"travel-alp-inbound": {
"signature": {
"parameters": {},
"additionalParameters": "allowed"
},
"semanticObject": "booking",
"action": "process",
"title": "Analyze Travels",
"subTitle": "Analyze travels",
"icon": "sap-icon://flight"
}
}
}
},
"sap.ui": {
"technology": "UI5",
"icons": {
"icon": "sap-icon://flight",
"favIcon": "sap-icon://flight",
"phone": "sap-icon://flight",
"phone@2": "sap-icon://flight",
"tablet": "sap-icon://flight",
"tablet@2": "sap-icon://flight"
},
"deviceTypes": {
"desktop": true,
"tablet": true,
"phone": true
}
},
"sap.ui5": {
"flexEnabled": false,
"dependencies": {
"minUI5Version": "1.139.0",
"libs": {
"sap.m": {},
"sap.ui.core": {},
"sap.ushell": {},
"sap.fe.templates": {}
}
},
"contentDensities": {
"compact": true,
"cozy": true
},
"models": {
"i18n": {
"type": "sap.ui.model.resource.ResourceModel",
"settings": {
"bundleName": "sap.fe.cap.travel_analytics.i18n.i18n",
"supportedLocales": [
"en",
"de",
"fr"
],
"fallbackLocale": "en"
}
},
"": {
"dataSource": "mainService",
"preload": true,
"settings": {
"synchronizationMode": "None",
"operationMode": "Server",
"autoExpandSelect": true,
"earlyRequests": true
}
},
"@i18n": {
"type": "sap.ui.model.resource.ResourceModel",
"uri": "i18n/i18n.properties"
}
},
"resources": {
"css": []
},
"routing": {
"config": {},
"routes": [
{
"pattern": ":?query:",
"name": "BookingsList",
"target": "BookingsList"
},
{
"pattern": "Bookings({key}):?query:",
"name": "BookingsObjectPage",
"target": "BookingsObjectPage"
}
],
"targets": {
"BookingsList": {
"type": "Component",
"id": "BookingsList",
"name": "sap.fe.templates.ListReport",
"options": {
"settings": {
"entitySet": "Bookings",
"variantManagement": "Page",
"initialLoad": "Enabled",
"navigation": {
"Bookings": {
"detail": {
"route": "BookingsObjectPage"
}
}
},
"views": {
"paths": [
{
"primary": [
{
"annotationPath": "@com.sap.vocabularies.UI.v1.PresentationVariant"
}
],
"secondary": [
{
"annotationPath": "@com.sap.vocabularies.UI.v1.PresentationVariant"
}
],
"defaultPath": "both"
}
]
},
"controlConfiguration": {
"@com.sap.vocabularies.UI.v1.LineItem": {
"tableSettings": {
"type": "AnalyticalTable",
"enableExport": true
}
},
"@com.sap.vocabularies.UI.v1.SelectionFields": {
"layout": "CompactVisual",
"initialLayout": "Visual",
"filterFields": {
"status": {
"label": "Status",
"availability": "Default",
"visualFilter": {
"valueList": "com.sap.vocabularies.Common.v1.ValueList#vlStatus"
}
},
"airline": {
"label": "Status",
"availability": "Default",
"visualFilter": {
"valueList": "com.sap.vocabularies.Common.v1.ValueList#vlAirline"
}
},
"FlightDate": {
"label": "Flight Date",
"availability": "Default",
"visualFilter": {
"valueList": "com.sap.vocabularies.Common.v1.ValueList#vlFlightDate"
}
}
}
}
},
"keyPerformanceIndicators": {
"KPI1": {
"entitySet": "Bookings",
"qualifier": "myKPI1"
}
}
}
}
},
"BookingsObjectPage": {
"type": "Component",
"id": "BookingsObjectPage",
"name": "sap.fe.templates.ObjectPage",
"options": {
"settings": {
"entitySet": "Bookings",
"editableHeaderContent": false
}
}
}
}
}
},
"sap.fiori": {
"registrationIds": [],
"archeType": "transactional"
},
"sap.cloud": {
"public": true,
"service": "sap.fe.cap.sflight"
}
}
================================================
FILE: app/travel_analytics/webapp/test/flpSandbox.html
================================================
<!DOCTYPE html>
<html lang="en">
<!-- Copyright (c) 2015 SAP AG, All Rights Reserved -->
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>{{appTitle}}</title>
<!-- Bootstrap the unified shell in sandbox mode for standalone usage.
The renderer is specified in the global Unified Shell configuration object "sap-ushell-config".
The fiori2 renderer will render the shell header allowing, for instance,
testing of additional application setting buttons.
The navigation target resolution service is configured in a way that the empty URL hash is
resolved to our own application.
This example uses relative path references for the SAPUI5 resources and test-resources;
it might be necessary to adapt them depending on the target runtime platform.
The sandbox platform is restricted to development or demo use cases and must NOT be used
for productive scenarios.
-->
<script type="text/javascript">
window["sap-ushell-config"] = {
defaultRenderer: "fiori2",
bootstrapPlugins: {
RuntimeAuthoringPlugin: {
component: "sap.ushell.plugins.rta",
config: {
validateAppVersion: false,
},
},
},
renderers: {
fiori2: {
componentData: {
config: {
search: "hidden",
},
},
},
},
applications: {
"sapfecapsflightsflightanalytics-tile": {
title: "SFlight Analytics",
description: "SFlight ALP",
additionalInformation:
"SAPUI5.Component=sap.fe.cap.travel_analytics",
applicationType: "URL",
url: "../",
},
},
};
</script>
<script
src="https://sapui5.hana.ondemand.com/test-resources/sap/ushell/bootstrap/sandbox.js"
id="sap-ushell-bootstrap"
></script>
<!-- Bootstrap the UI5 core library -->
<script
id="sap-ui-bootstrap"
src="https://ui5.sap.com/1.139.0/resources/sap-ui-core.js"
data-sap-ui-libs=""
data-sap-ui-async="true"
data-sap-ui-preload="async"
data-sap-ui-theme="sap_horizon"
data-sap-ui-compatVersion="edge"
data-sap-ui-language="en"
data-sap-ui-resourceroots='{"sap.fe.cap.travel_analytics": "../"}'
data-sap-ui-frameOptions="allow"
>
// NON-SECURE setting for testing environment
</script>
<script>
sap.ui.getCore().attachInit(async () => {
const renderer = await sap.ushell.Container.createRenderer(true);
renderer.placeAt('content');
});
</script>
</head>
<!-- UI Content -->
<body class="sapUiBody" id="content"></body>
</html>
================================================
FILE: app/travel_analytics/webapp/test/integration/Opa.qunit.html
================================================
<!DOCTYPE html>
<html lang="en">
<head>
<title>Integration tests</title>
<meta charset="UTF-8" content="IE=edge" http-equiv="X-UA-Compatible" />
<meta content="text/html;charset=UTF-8" http-equiv="Content-Type" />
<meta content="width=device-width, initial-scale=1.0" name="viewport" />
<script
id="sap-ui-bootstrap"
src="https://ui5.sap.com/1.139.0/resources/sap-ui-core.js"
data-sap-ui-animation="false"
data-sap-ui-compatVersion="edge"
data-sap-ui-resourceroots='{
"sap.fe.cap.travel_analytics": "../../"
}'
data-sap-ui-theme="sap_horizon"
data-sap-ui-libs="sap.m, sap.fe.core"
></script>
<link
rel="stylesheet"
type="text/css"
href="https://ui5.sap.com/1.139.0/resources/sap/ui/thirdparty/qunit-2.css"
/>
<script src="https://ui5.sap.com/1.139.0/resources/sap/ui/thirdparty/qunit-2.js"></script>
<script src="https://ui5.sap.com/1.139.0/resources/sap/ui/qunit/qunit-junit.js"></script>
<script src="Opa.qunit.js"></script>
</head>
<body>
<div id="qunit"></div>
<div id="qunit-fixture"></div>
</body>
</html>
================================================
FILE: app/travel_analytics/webapp/test/integration/Opa.qunit.js
================================================
sap.ui.require(
[
"sap/fe/test/JourneyRunner",
"sap/fe/cap/travel_analytics/test/integration/OpaJourney",
"sap/fe/cap/travel_analytics/test/integration/pages/BookingsList",
"sap/fe/cap/travel_analytics/test/integration/pages/BookingsObjectPage",
],
function (JourneyRunner, opaJourney, BookingsList, BookingsObjectPage) {
"use strict";
const runner = new JourneyRunner({
// start index.html in web folder
launchUrl:
sap.ui.require.toUrl("sap/fe/cap/travel_analytics") + "/index.html",
});
runner.run(
{
pages: {
onTheBookingsList: BookingsList,
onTheBookingsObjectPage: BookingsObjectPage,
},
},
opaJourney.run
);
}
);
================================================
FILE: app/travel_analytics/webapp/test/integration/OpaJourney.js
================================================
/* global QUnit */
sap.ui.define(["sap/ui/test/opaQunit"], function (opaTest) {
"use strict";
return {
run: function () {
QUnit.module("Travel Analytics Tests");
opaTest("Start application", function (Given, When, Then) {
Given.iStartMyApp();
Then.onTheBookingsList.iSeeThisPage();
});
opaTest("Teardown", function (Given /*, When, Then*/) {
// Cleanup
Given.iTearDownMyApp();
});
},
};
});
================================================
FILE: app/travel_analytics/webapp/test/integration/pages/BookingsList.js
================================================
sap.ui.define(["sap/fe/test/ListReport"], function (ListReport) {
"use strict";
const CustomPageDefinitions = {
actions: {},
assertions: {},
};
return new ListReport(
{
appId: "sap.fe.cap.travel_analytics",
componentId: "BookingsList",
entitySet: "Bookings",
},
CustomPageDefinitions
);
});
================================================
FILE: app/travel_analytics/webapp/test/integration/pages/BookingsObjectPage.js
================================================
sap.ui.define(["sap/fe/test/ObjectPage"], function (ObjectPage) {
"use strict";
const CustomPageDefinitions = {
actions: {},
assertions: {},
};
return new ObjectPage(
{
appId: "sap.fe.cap.travel_analytics",
componentId: "BookingsObjectPage",
entitySet: "Bookings",
},
CustomPageDefinitions
);
});
================================================
FILE: app/travel_analytics/webapp/test/testsuite.qunit.html
================================================
<!DOCTYPE html>
<html lang="en">
<head>
<title>QUnit test suite</title>
<script src="../resources/sap/ui/qunit/qunit-redirect.js"></script>
<script src="testsuite.qunit.js" data-sap-ui-testsuite></script>
</head>
<body></body>
</html>
================================================
FILE: app/travel_analytics/webapp/test/testsuite.qunit.js
================================================
/* global window, parent, location */
window.suite = function () {
"use strict";
const oSuite = new parent.jsUnitTestSuite(),
sContextPath = location.pathname.substring(
0,
location.pathname.lastIndexOf("/") + 1
);
oSuite.addTestPage(sContextPath + "integration/Opa.qunit.html");
return oSuite;
};
================================================
FILE: app/travel_analytics/xs-app.json
================================================
{
"welcomeFile": "/index.html",
"authenticationMethod": "route",
"routes": [
{
"source": "^/analytics/(.*)$",
"target": "/analytics/$1",
"destination": "sflight-srv",
"authenticationType": "xsuaa",
"csrfProtection": false
},
{
"source": "^(.*)$",
"target": "$1",
"service": "html5-apps-repo-rt",
"authenticationType": "xsuaa"
}
]
}
================================================
FILE: app/travel_analytics/xs-security.json
================================================
{
"scopes": [
{
"name": "$XSAPPNAME.reviewer",
"description": "reviewer"
},
{
"name": "$XSAPPNAME.processor",
"description": "processor"
},
{
"name": "$XSAPPNAME.admin",
"description": "admin"
}
],
"attributes": [],
"role-templates": [
{
"name": "reviewer",
"description": "generated",
"scope-references": [
"$XSAPPNAME.reviewer"
],
"attribute-references": []
},
{
"name": "processor",
"description": "generated",
"scope-references": [
"$XSAPPNAME.processor"
],
"attribute-references": []
},
{
"name": "admin",
"description": "generated",
"scope-references": [
"$XSAPPNAME.admin"
],
"attribute-references": []
}
]
}
================================================
FILE: app/travel_processor/capabilities.cds
================================================
using TravelService from '../../srv/travel-service';
annotate TravelService.Travel with @odata.draft.enabled;
annotate TravelService.Travel with @Common.SemanticKey: [TravelID];
annotate TravelService.Booking with @Common.SemanticKey: [BookingID];
annotate TravelService.BookingSupplement with @Common.SemanticKey: [BookingSupplementID];
================================================
FILE: app/travel_processor/field-control.cds
================================================
using TravelService from '../../srv/travel-service';
//
// annotations that control the behavior of fields and actions
//
using { sap.fe.cap.travel.TravelStatus } from '../../db/schema';
// As we use field control based on travel status for many elements, we do the computation in a calculated element
// right here instead of repeating the same expression in multiple annotations
extend TravelStatus with {
// can't use UInt8 (which would automatically be mapped to Edm.Byte) because it's not supported on H2
fieldControl: Int16 @odata.Type:'Edm.Byte' enum {Inapplicable = 0; ReadOnly = 1; Optional = 3; Mandatory = 7;}
= (code = #Accepted ? #ReadOnly : #Mandatory );
}
annotate TravelService.Travel with @(Common : {
SideEffects: {
SourceProperties: [BookingFee],
TargetProperties: ['TotalPrice', 'GreenFee', 'TreesPlanted']
},
SideEffects #GoGreen:{
SourceProperties: [GoGreen],
TargetProperties: ['TotalPrice', 'GreenFee', 'TreesPlanted']
}
}){
BookingFee @Common.FieldControl : TravelStatus.fieldControl;
BeginDate @Common.FieldControl : TravelStatus.fieldControl;
EndDate @Common.FieldControl : TravelStatus.fieldControl;
to_Agency @Common.FieldControl : TravelStatus.fieldControl;
to_Customer @Common.FieldControl : TravelStatus.fieldControl;
} actions {
rejectTravel @(
Core.OperationAvailable : ($self.TravelStatus.code != #Canceled),
Common.SideEffects.TargetProperties : ['in/TravelStatus_code'],
);
acceptTravel @(
Core.OperationAvailable : ($self.TravelStatus.code != #Accepted),
Common.SideEffects.TargetProperties : ['in/TravelStatus_code'],
);
deductDiscount @(
Core.OperationAvailable : ($self.TravelStatus.code = #Open),
Common.SideEffects.TargetProperties : ['in/TotalPrice', 'in/BookingFee'],
);
}
annotate TravelService.Travel @(
Common.SideEffects#ReactonItemCreationOrDeletion : {
SourceEntities : [
to_Booking
],
TargetProperties : ['TotalPrice'
]
}
);
annotate TravelService.Booking with @UI.CreateHidden : (to_Travel.TravelStatus.code != #Open);
annotate TravelService.Booking with @UI.DeleteHidden : (to_Travel.TravelStatus.code != #Open);
annotate TravelService.Booking {
BookingDate @Core.Computed;
ConnectionID @Common.FieldControl : to_Travel.TravelStatus.fieldControl;
FlightDate @Common.FieldControl : to_Travel.TravelStatus.fieldControl;
FlightPrice @Common.FieldControl : to_Travel.TravelStatus.fieldControl;
BookingStatus @Common.FieldControl : to_Travel.TravelStatus.fieldControl;
to_Carrier @Common.FieldControl : to_Travel.TravelStatus.fieldControl;
to_Customer @Common.FieldControl : to_Travel.TravelStatus.fieldControl;
};
annotate TravelService.Booking with @(
Capabilities.NavigationRestrictions : {
RestrictedProperties : [
{
NavigationProperty : to_BookSupplement,
InsertRestrictions : {
Insertable : (to_Travel.TravelStatus.code = #Open)
},
DeleteRestrictions : {
Deletable : (to_Travel.TravelStatus.code = #Open)
}
}
]
}
);
annotate TravelService.BookingSupplement {
Price @Common.FieldControl : to_Travel.TravelStatus.fieldControl;
to_Supplement @Common.FieldControl : to_Travel.TravelStatus.fieldControl;
to_Booking @Common.FieldControl : to_Travel.TravelStatus.fieldControl;
to_Travel @Common.FieldControl : to_Travel.TravelStatus.fieldControl;
};
================================================
FILE: app/travel_processor/karma.conf.js
================================================
module.exports = require("../.karma/karma.conf.js")
================================================
FILE: app/travel_processor/layouts.cds
================================================
using TravelService from '../../srv/travel-service';
//
// annotations that control the Fiori layout
//
annotate TravelService.Travel with @UI : {
Identification : [
{ $Type : 'UI.DataFieldForAction', Action : 'TravelService.acceptTravel', Label : '{i18n>AcceptTravel}' },
{ $Type : 'UI.DataFieldForAction', Action : 'TravelService.rejectTravel', Label : '{i18n>RejectTravel}' },
{ $Type : 'UI.DataFieldForAction', Action : 'TravelService.deductDiscount', Label : '{i18n>DeductDiscount}' }
],
HeaderInfo : {
TypeName : '{i18n>Travel}',
TypeNamePlural : '{i18n>Travels}',
Title : {
$Type : 'UI.DataField',
Value : Description
},
Description : {
$Type : 'UI.DataField',
Value : TravelID
}
},
PresentationVariant : {
Text : 'Default',
Visualizations : ['@UI.LineItem'],
SortOrder : [{
$Type : 'Common.SortOrderType',
Property : TravelID,
Descending : true
}]
},
SelectionFields : [
to_Agency_AgencyID,
to_Customer_CustomerID,
TravelStatus_code
],
LineItem : [
{ $Type : 'UI.DataFieldForAction', Action : 'TravelService.acceptTravel', Label : '{i18n>AcceptTravel}' },
{ $Type : 'UI.DataFieldForAction', Action : 'TravelService.rejectTravel', Label : '{i18n>RejectTravel}' },
{ $Type : 'UI.DataFieldForAction', Action : 'TravelService.deductDiscount', Label : '{i18n>DeductDiscount}' },
{
Value : TravelID,
@UI.Importance : #High
},
{
Value : (to_Agency.AgencyID),
@HTML5.CssDefaults: {width:'16em'}
},
{
Value : (to_Customer.CustomerID),
@UI.Importance : #High,
@HTML5.CssDefaults: {width:'14em'}
},
{ Value : BeginDate, @HTML5.CssDefaults: {width:'9em'} },
{ Value : EndDate, @HTML5.CssDefaults: {width:'9em'} },
{ Value : BookingFee, @HTML5.CssDefaults: {width:'10em'} },
{ Value : TotalPrice, @HTML5.CssDefaults: {width:'12em'} },
{
Value : (TravelStatus.code),
Criticality : (TravelStatus.code = #Open ? 2 : (TravelStatus.code = #Accepted ? 3 : 0)),
@UI.Importance : #High,
@HTML5.CssDefaults: {width:'10em'}
}
],
Facets : [{
$Type : 'UI.CollectionFacet',
Label : '{i18n>GeneralInformation}',
ID : 'Travel',
Facets : [
{ // travel details
$Type : 'UI.ReferenceFacet',
ID : 'TravelData',
Target : '@UI.FieldGroup#TravelData',
Label : '{i18n>GeneralInformation}'
},
{ // price information
$Type : 'UI.ReferenceFacet',
ID : 'PriceData',
Target : '@UI.FieldGroup#PriceData',
Label : '{i18n>Prices}'
},
{ // date information
$Type : 'UI.ReferenceFacet',
ID : 'DateData',
Target : '@UI.FieldGroup#DateData',
Label : '{i18n>Dates}'
},
{
$Type : 'UI.ReferenceFacet',
Label : '{i18n>Sustainability}',
ID : 'i18nSustainability',
Target: '@UI.FieldGroup#i18nSustainability',
}
]
}],
FieldGroup#TravelData : { Data : [
{ Value : TravelID },
{ Value : (to_Agency.AgencyID) },
{ Value : (to_Customer.CustomerID) },
{ Value : Description },
{
$Type : 'UI.DataField',
Value : (TravelStatus.code),
Criticality : (TravelStatus.code = #Open ? 2 : (TravelStatus.code = #Accepted ? 3 : 0)),
Label : '{i18n>Status}' // label only necessary if differs from title of element
}
]},
FieldGroup #DateData : {Data : [
{ $Type : 'UI.DataField', Value : BeginDate },
{ $Type : 'UI.DataField', Value : EndDate }
]},
FieldGroup #PriceData : {Data : [
{ $Type : 'UI.DataField', Value : BookingFee },
{ $Type : 'UI.DataField', Value : TotalPrice },
{ $Type : 'UI.DataField', Value : (CurrencyCode.code) }
]},
FieldGroup #i18nSustainability: {
$Type: 'UI.FieldGroupType',
Data : [
{
$Type: 'UI.DataField',
Value: GoGreen,
},
{
$Type: 'UI.DataField',
Value: GreenFee,
},
{
$Type: 'UI.DataField',
Value: TreesPlanted,
},
],
}
};
annotate TravelService.Booking with @UI : {
Identification : [
{ Value : BookingID },
],
HeaderInfo : {
TypeName : '{i18n>Bookings}',
TypeNamePlural : '{i18n>Bookings}',
Title : { Value : to_Customer.LastName },
Description : { Value : BookingID }
},
PresentationVariant : {
Visualizations : ['@UI.LineItem'],
SortOrder : [{
$Type : 'Common.SortOrderType',
Property : BookingID,
Descending : false
}]
},
SelectionFields : [],
LineItem : [
{ Value : to_Carrier.AirlinePicURL, Label : ' '},
{ Value : BookingID },
{ Value : BookingDate },
{ Value : (to_Customer.CustomerID) },
{ Value : (to_Carrier.AirlineID ) },
{ Value : ConnectionID, Label : '{i18n>FlightNumber}' },
{ Value : FlightDate },
{ Value : FlightPrice },
{ Value : (BookingStatus.code),
Criticality : (BookingStatus.code = #New ? 2 : (BookingStatus.code = #Booked ? 3 : 0)),
}
],
Facets : [{
$Type : 'UI.CollectionFacet',
Label : '{i18n>GeneralInformation}',
ID : 'Booking',
Facets : [{ // booking details
$Type : 'UI.ReferenceFacet',
ID : 'BookingData',
Target : '@UI.FieldGroup#GeneralInformation',
Label : '{i18n>Booking}'
}, { // flight details
$Type : 'UI.ReferenceFacet',
ID : 'FlightData',
Target : '@UI.FieldGroup#Flight',
Label : '{i18n>Flight}'
}]
}, { // supplements list
$Type : 'UI.ReferenceFacet',
ID : 'SupplementsList',
Target : 'to_BookSupplement/@UI.PresentationVariant',
Label : '{i18n>BookingSupplements}'
}],
FieldGroup #GeneralInformation : { Data : [
{ Value : BookingID },
{ Value : BookingDate, },
{ Value : (to_Customer.CustomerID) },
{ Value : BookingDate, },
{ Value : (BookingStatus.code),
Criticality : (BookingStatus.code = #New ? 2 : (BookingStatus.code = #Booked ? 3 : 0)),
}
]},
FieldGroup #Flight : { Data : [
{ Value : (to_Carrier.AirlineID) },
{ Value : ConnectionID },
{ Value : FlightDate },
{ Value : FlightPrice }
]},
};
annotate TravelService.BookingSupplement with @UI : {
Identification : [
{ Value : BookingSupplementID }
],
HeaderInfo : {
TypeName : '{i18n>BookingSupplement}',
TypeNamePlural : '{i18n>BookingSupplements}',
Title : { Value : BookingSupplementID },
Description : { Value : BookingSupplementID }
},
PresentationVariant : {
Text : 'Default',
Visualizations : ['@UI.LineItem'],
SortOrder : [{
$Type : 'Common.SortOrderType',
Property : BookingSupplementID,
Descending : false
}]
},
LineItem : [
{ Value : BookingSupplementID },
{ Value : (to_Supplement.SupplementID), Label : '{i18n>ProductID}' },
{ Value : Price, Label : '{i18n>ProductPrice}' }
],
};
================================================
FILE: app/travel_processor/package.json
================================================
{
"name": "travel-processor",
"version": "1.0.0",
"private": true,
"main": "webapp/index.html",
"engines": {
"node": ">=18"
},
"scripts": {
"build": "ui5 build preload --clean-dest --include-task=generateCachebusterInfo",
"start": "ui5 serve",
"test": "npm run test:node && npm run test:java",
"test:java": "karma start --server=java --single-run",
"test:node": "karma start --server=node --single-run",
"ts-typecheck": "tsc --noEmit",
"prestart": "npm run ts-typecheck",
"prebuild": "npm run ts-typecheck",
"deploy-config": "npx -p @sap/ux-ui5-tooling fiori add deploy-config cf"
},
"keywords": [
"ui5",
"openui5",
"sapui5"
],
"devDependencies": {
"@sap/ux-ui5-tooling": "1",
"@sapui5/types": "^1.139.0",
"@typescript-eslint/eslint-plugin": "^8",
"@typescript-eslint/parser": "^8",
"@ui5/cli": "^4.0.0",
"karma": "^6.4.3",
"karma-chrome-launcher": "^3.2.0",
"karma-ui5": "^4.0.1",
"karma-ui5-transpile": "^3.5.1",
"puppeteer": "^24",
"typescript": "^5.1.6",
"ui5-middleware-simpleproxy": "^3.2.16",
"ui5-task-zipper": "^3.1.4",
"ui5-tooling-transpile": "^3.3.7"
}
}
================================================
FILE: app/travel_processor/tsconfig.json
================================================
{
"compilerOptions": {
"target": "es2022",
"module": "es2022",
"skipLibCheck": true,
"allowJs": true,
"strict": true,
"strictPropertyInitialization": false,
"moduleResolution": "node",
"rootDir": "./webapp",
"outDir": "./dist",
"baseUrl": "./",
"paths": {
"sap/fe/cap/travel_processor/*": [
"./webapp/*"
]
},
"typeRoots": [
"./node_modules/@types",
"./node_modules/@sapui5/types",
"../../node_modules/@types",
"../../node_modules/@sapui5/types"
]
},
"include": [
"./webapp/**/*"
]
}
================================================
FILE: app/travel_processor/ui5.yaml
================================================
# yaml-language-server: $schema=https://sap.github.io/ui5-tooling/schema/ui5.yaml.json
specVersion: "3.0"
metadata:
name: sap.fe.cap.travel
type: application
resources:
configuration:
propertiesFileSourceEncoding: UTF-8
builder:
resources:
excludes:
- "/test/**"
- "/localService/**"
customTasks:
- name: ui5-task-zipper
afterTask: generateCachebusterInfo
configuration:
archiveName: travel-processor
additionalFiles:
- xs-app.json
- name: ui5-tooling-transpile-task
afterTask: replaceVersion
# configuration:
# debug: true
server:
customMiddleware:
- name: ui5-middleware-simpleproxy
mountPath: /processor
afterMiddleware: compression
configuration:
baseUri: http://localhost:4004/processor
username: admin # dummy credentials for local testing
password: admin # dummy credentials for local testing
- name: fiori-tools-appreload
afterMiddleware: compression
- name: ui5-tooling-transpile-middleware
afterMiddleware: compression
configuration:
# debug: true
excludePatterns:
- /Component-preload.js
================================================
FILE: app/travel_processor/webapp/Component.ts
================================================
import BaseComponent from "sap/fe/core/AppComponent";
/**
* @namespace sap.fe.cap.travel
*/
export default class Component extends BaseComponent {
public static metadata = {
manifest: "json"
};
/**
* The component is initialized by UI5 automatically during the startup of the app and calls the init method once.
* @public
* @override
*/
//public init() : void {
// super.init();
//}
}
================================================
FILE: app/travel_processor/webapp/changes/changes-bundle.json
================================================
{}
================================================
FILE: app/travel_processor/webapp/changes/flexibility-bundle.json
================================================
{
"compVariants": [],
"changes": []
}
================================================
FILE: app/travel_processor/webapp/ext/controller/ControllerExtension.d.ts
================================================
/**
* Helper to be able to define how to get the page specific extension API when writing a controller extension.
*/
declare module 'sap/ui/core/mvc/ControllerExtension' {
export default class ControllerExtension<API> {
static overrides: unknown;
base: {
getExtensionAPI(): API;
}
}
}
================================================
FILE: app/travel_processor/webapp/ext/controller/ObjectPageExtension.controller.ts
================================================
import ControllerExtension from "sap/ui/core/mvc/ControllerExtension";
import ExtensionAPI from "sap/fe/templates/ObjectPage/ExtensionAPI";
import Context from "sap/ui/model/odata/v4/Context";
import Dialog from "sap/m/Dialog";
/**
* @namespace sap.fe.cap.travel.ext.controller
* @controller
*/
export default class ObjectPageExtension extends ControllerExtension<ExtensionAPI> {
async openDialog(resolve: (value: PromiseLike<null> | null) => void, reject: (reason?: any) => void, context: Context) {
//try catch ensures errors in floating promises are handled properly
try {
let approveDialog = (await this.base.getExtensionAPI().loadFragment({
id: "myFragment",
initialBindingContext: context,
name: "sap.fe.cap.travel.ext.fragment.Trees4Tickets"
})) as Dialog;
//Dialog Save button
approveDialog.getBeginButton().attachPress(function () {
approveDialog.close().destroy();
resolve(null);
});
//Dialog Cancel button
approveDialog.getEndButton().attachPress(function () {
approveDialog.close().destroy();
reject(null);
});
//consider dialog closing with ESC
approveDialog.attachAfterClose(function () {
approveDialog.destroy();
reject(null);
});
approveDialog.open();
} catch (error) {
reject(null);
}
}
static overrides = {
/**
* Called when a controller is instantiated and its View controls (if available) are already created.
* Can be used to modify the View before it is displayed, to bind event handlers and do other one-time initialization.
* @memberOf sap.fe.cap.travel.ext.controller.ObjectPageExtension
*/
onInit(this: ObjectPageExtension) {
// you can access the Fiori elements extensionAPI via this.base.getExtensionAPI
//const model = this.base.getExtensionAPI().getModel();
},
editFlow: {
onBeforeSave(this: ObjectPageExtension) {
const context = this.base
.getExtensionAPI()
.getBindingContext() as Context;
if (!context.getProperty("GoGreen")) {
//void intentionally discards returned floating promise
return new Promise<null>((resolve, reject) => { void this.openDialog(resolve, reject, context); });
}
return undefined;
}
}
};
}
================================================
FILE: app/travel_processor/webapp/ext/fragment/CustomSection.fragment.xml
================================================
<core:FragmentDefinition xmlns:core="sap.ui.core" xmlns="sap.m" xmlns:l="sap.ui.layout" xmlns:macros="sap.fe.macros">
<VBox core:require="{ handler: 'sap/fe/cap/travel/ext/fragment/CustomSection'}">
<MessageStrip text="All bookings for travel {TravelID} got confirmed by the agency." showIcon="true" class="sapUiSmallMarginBottom">
</MessageStrip>
<l:Grid hSpacing="1" containerQuery="true" defaultSpan="L12 M12 S12">
<l:content>
<macros:Table metaPath="to_Booking/@com.sap.vocabularies.UI.v1.LineItem" id="bookingTable"/>
</l:content>
</l:Grid>
</VBox>
</core:FragmentDefinition>
================================================
FILE: app/travel_processor/webapp/ext/fragment/CustomSection.ts
================================================
import ExtensionAPI from 'sap/fe/core/ExtensionAPI';
import UI5Event from 'sap/ui/base/Event';
import MessageToast from 'sap/m/MessageToast';
/**
* Generated event handler.
*
* @param this reference to the 'this' that the event handler is bound to.
* @param event the event object provided by the event provider
*/
export function onPress(this: ExtensionAPI, event: UI5Event) {
MessageToast.show("Custom handler invoked.");
}
================================================
FILE: app/travel_processor/webapp/ext/fragment/Trees4Tickets.fragment.xml
================================================
<core:FragmentDefinition
xmlns="sap.m"
xmlns:macros="sap.fe.macros"
xmlns:l="sap.ui.layout"
xmlns:f="sap.ui.layout.form"
xmlns:core="sap.ui.core">
<Dialog title="Opt for Trees-4-Tickets" icon="sap-icon://flight"
id="Trees4Tickets" contentWidth="38rem">
<content>
<MessageStrip
text="Plant some trees with an additional 10 % on your booking fee"
type="Success"
showIcon='{GoGreen}'
class="sapUiSmallMarginTop sapUiSmallMarginBottom sapUiSmallMarginBeginEnd">
</MessageStrip>
<f:Form>
<f:layout>
<f:ColumnLayout
columnsXL="2"
columnsL="2"
columnsM="2"/>
</f:layout>
<f:formContainers>
<f:FormContainer>
<f:formElements>
<macros:FormElement metaPath="TotalPrice" id="formElementMacroID1" />
<macros:FormElement metaPath="GoGreen" id="formElementMacroID2" />
</f:formElements>
</f:FormContainer>
<f:FormContainer>
<f:formElements>
<macros:FormElement metaPath="GreenFee" visible="{GoGreen}" id="formElementMacroID3" />
<macros:FormElement metaPath="TreesPlanted" visible="{GoGreen}" id="formElementMacroID4" />
</f:formElements>
</f:FormContainer>
</f:formContainers>
</f:Form>
</content>
<beginButton>
<Button id="Save" text="Save" type="Emphasized"/>
</beginButton>
<endButton>
<Button id="Cancel" text="Cancel"/>
</endButton>
</Dialog>
</core:FragmentDefinition>
================================================
FILE: app/travel_processor/webapp/i18n/i18n.properties
================================================
# This is the resource bundle for travel_processor
#Texts for manifest.json
#XTIT: Application name
title=Manage Travels
#YDES: Application description
description=Manage Travels
================================================
FILE: app/travel_processor/webapp/i18n/i18n_de.properties
================================================
# This is the resource bundle for travel_processor
#Texts for manifest.json
#XTIT: Application name
title=Reisen verwalten
#YDES: Application description
description=Reisen verwalten
================================================
FILE: app/travel_processor/webapp/i18n/i18n_en.properties
================================================
# This is the resource bundle for travel_processor
#Texts for manifest.json
#XTIT: Application name
title=Manage Travels
#YDES: Application description
description=Manage Travels
================================================
FILE: app/travel_processor/webapp/i18n/i18n_fr.properties
================================================
# This is the resource bundle for travel_processor
#Texts for manifest.json
#XTIT: Application name
title=Gérer les voyages
#YDES: Application description
description=Gérer les voyages
================================================
FILE: app/travel_processor/webapp/index.html
================================================
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title></title>
<style>
html,
body,
body > div,
#container,
#container-uiarea {
height: 100%;
}
</style>
<script
id="sap-ui-bootstrap"
src="https://ui5.sap.com/1.139.0/resources/sap-ui-core.js"
data-sap-ui-theme="sap_horizon"
data-sap-ui-oninit="module:sap/ui/core/ComponentSupport"
data-sap-ui-resourceroots='{ "sap.fe.cap.travel": "." }'
data-sap-ui-compatVersion="edge"
data-sap-ui-async="true"
data-sap-ui-preload="async"
data-sap-ui-flexibilityServices='[{"connector": "SessionStorageConnector"}]'
data-sap-ui-xx-componentPreload="off"
data-sap-ui-libs="sap.m, sap.fe.core"
></script>
</head>
<body class="sapUiBody sapUiSizeCompact" id="content">
<div
data-sap-ui-component
data-name="sap.fe.cap.travel"
data-id="container"
data-settings='{"id" : "sap.fe.cap.travel"}'
data-handle-validation="true"
></div>
</body>
</html>
================================================
FILE: app/travel_processor/webapp/manifest.json
================================================
{
"_version": "1.32.0",
"sap.app": {
"id": "sap.fe.cap.travel",
"type": "application",
"title": "{{title}}",
"description": "{{description}}",
"i18n": {
"bundleName": "sap.fe.cap.travel.i18n.i18n",
"supportedLocales": [
"en",
"de",
"fr"
],
"fallbackLocale": "en"
},
"applicationVersion": {
"version": "1.0.0"
},
"dataSources": {
"mainService": {
"uri": "processor/",
"type": "OData",
"settings": {
"odataVersion": "4.0"
}
}
},
"offline": false,
"resources": "resources.json",
"sourceTemplate": {
"id": "ui5template.fiorielements.v4.lrop",
"version": "1.0.0"
},
"crossNavigation": {
"inbounds": {
"travel-inbound": {
"signature": {
"parameters": {},
"additionalParameters": "allowed"
},
"semanticObject": "travel",
"action": "process",
"title": "Process Travels",
"subTitle": "Process travels",
"icon": "sap-icon://flight"
}
}
}
},
"sap.ui": {
"technology": "UI5",
"icons": {
"icon": "sap-icon://flight",
"favIcon": "sap-icon://flight",
"phone": "sap-icon://flight",
"phone@2": "sap-icon://flight",
"tablet": "sap-icon://flight",
"tablet@2": "sap-icon://flight"
},
"deviceTypes": {
"desktop": true,
"tablet": true,
"phone": true
}
},
"sap.ui5": {
"flexEnabled": false,
"resources": {
"js": [],
"css": []
},
"dependencies": {
"minUI5Version": "1.139.0",
"libs": {
"sap.ui.core": {},
"sap.fe.templates": {}
}
},
"models": {
"i18n": {
"type": "sap.ui.model.resource.ResourceModel",
"settings": {
"bundleName": "sap.fe.cap.travel.i18n.i18n",
"supportedLocales": [
"en",
"de",
"fr"
],
"fallbackLocale": "en"
}
},
"": {
"dataSource": "mainService",
"preload": true,
"settings": {
"synchronizationMode": "None",
"operationMode": "Server",
"autoExpandSelect": true,
"earlyRequests": true
}
},
"@i18n": {
"type": "sap.ui.model.resource.ResourceModel",
"uri": "i18n/i18n.properties"
}
},
"routing": {
"routes": [
{
"pattern": ":?query:",
"name": "TravelList",
"target": "TravelList"
},
{
"pattern": "Travel({key}):?query:",
"name": "TravelObjectPage",
"target": "TravelObjectPage"
},
{
"pattern": "Travel({key})/to_Booking({key2}):?query:",
"name": "BookingObjectPage",
"target": "BookingObjectPage"
}
],
"targets": {
"TravelList": {
"type": "Component",
"id": "TravelList",
"name": "sap.fe.templates.ListReport",
"options": {
"settings": {
"entitySet": "Travel",
"controlConfiguration": {
"@com.sap.vocabularies.UI.v1.SelectionFields": {
"useSemanticDateRange": true
}
},
"variantManagement": "Page",
"initialLoad": "Enabled",
"navigation": {
"Travel": {
"detail": {
"route": "TravelObjectPage"
}
}
}
}
}
},
"TravelObjectPage": {
"type": "Component",
"id": "TravelObjectPage",
"name": "sap.fe.templates.ObjectPage",
"options": {
"settings": {
"entitySet": "Travel",
"navigation": {
"to_Booking": {
"detail": {
"route": "BookingObjectPage"
}
}
},
"controlConfiguration": {
"to_Booking/@com.sap.vocabularies.UI.v1.LineItem": {
"tableSettings": {
"type": "ResponsiveTable",
"personalization": {
"column": true,
"sort": false
},
"creationMode": {
"name": "Inline",
"createAtEnd": true
}
}
}
},
"editableHeaderContent": false,
"content": {
"body": {
"sections": {
"CustomSection": {
"template": "sap.fe.cap.travel.ext.fragment.CustomSection",
"position": {
"placement": "After",
"anchor": "Travel"
},
"title": "My Itinerary"
}
}
}
}
}
}
},
"BookingObjectPage": {
"type": "Component",
"id": "BookingObjectPage",
"name": "sap.fe.templates.ObjectPage",
"options": {
"settings": {
"entitySet": "Booking",
"editableHeaderContent": false
}
}
}
},
"config": {}
},
"contentDensities": {
"compact": true,
"cozy": true
},
"extends": {
"extensions": {
"sap.ui.controllerExtensions": {
"sap.fe.templates.ObjectPage.ObjectPageController": {
"controllerName": "sap.fe.cap.travel.ext.controller.ObjectPageExtension"
}
}
}
}
},
"sap.platform.abap": {
"_version": "1.1.0",
"uri": ""
},
"sap.platform.hcp": {
"_version": "1.1.0",
"uri": ""
},
"sap.fiori": {
"_version": "1.1.0",
"registrationIds": [],
"archeType": "transactional"
},
"sap.cloud": {
"public": true,
"service": "sap.fe.cap.sflight"
}
}
================================================
FILE: app/travel_processor/webapp/test/integration/Opa.qunit.html
================================================
<!DOCTYPE html>
<html lang="en">
<head>
<title>Integration tests</title>
<meta charset="UTF-8" content="IE=edge" http-equiv="X-UA-Compatible" />
<meta content="text/html;charset=UTF-8" http-equiv="Content-Type" />
<meta content="width=device-width, initial-scale=1.0" name="viewport" />
<script
id="sap-ui-bootstrap"
src="https://ui5.sap.com/1.139.0/resources/sap-ui-core.js"
data-sap-ui-animation="false"
data-sap-ui-compatVersion="edge"
data-sap-ui-resourceroots='{
"sap.fe.cap.travel": "../../"
}'
data-sap-ui-theme="sap_horizon"
data-sap-ui-libs="sap.m, sap.fe.core"
></script>
<link
rel="stylesheet"
type="text/css"
href="https://ui5.sap.com/1.139.0/resources/sap/ui/thirdparty/qunit-2.css"
/>
<script src="https://ui5.sap.com/1.139.0/resources/sap/ui/thirdparty/qunit-2.js"></script>
<script src="https://ui5.sap.com/1.139.0/resources/sap/ui/qunit/qunit-junit.js"></script>
<script src="Opa.qunit.js"></script>
</head>
<body>
<div id="qunit"></div>
<div id="qunit-fixture"></div>
</body>
</html>
================================================
FILE: app/travel_processor/webapp/test/integration/Opa.qunit.js
================================================
sap.ui.require(
[
"sap/fe/test/JourneyRunner",
"sap/fe/cap/travel/test/integration/pages/MainListReport",
"sap/fe/cap/travel/test/integration/pages/MainObjectPage",
"sap/fe/cap/travel/test/integration/pages/ItemObjectPage",
"sap/fe/cap/travel/test/integration/OpaJourney",
],
function (
JourneyRunner,
MainListReport,
MainObjectPage,
ItemObjectPage,
Journey
) {
"use strict";
const runner = new JourneyRunner({
// start index.html in web folder
launchUrl: sap.ui.require.toUrl("sap/fe/cap/travel") + "/index.html",
opaConfig: { timeout: 30 },
});
runner.run(
{
pages: {
onTheMainPage: MainListReport,
onTheDetailPage: MainObjectPage,
onTheDetailItemPage: ItemObjectPage,
},
},
Journey.run
);
}
);
================================================
FILE: app/travel_processor/webapp/test/integration/OpaJourney.js
================================================
/* global QUnit */
sap.ui.define(["sap/ui/test/opaQunit"], function (opaTest) {
"use strict";
const Journey = {
start: function () {
QUnit.module("Travel Processor Tests");
opaTest("#000: Start", function (Given) {
Given.iResetTestData().and.iStartMyApp("", { "sap-language": "EN" });
});
return Journey;
},
test: function () {
opaTest(
"#1: ListReport: Check List Report Page loads",
function (Given, When, Then) {
Then.onTheMainPage.iSeeThisPage();
}
);
opaTest(
"#2: Object Page: Check Object Page loads",
function (Given, When, Then) {
When.onTheMainPage.onTable().iPressRow({ Travel: "4,133" });
Then.onTheDetailPage.iSeeThisPage();
When.iNavigateBack();
Then.onTheMainPage.iSeeThisPage();
}
);
opaTest("#3: List report: Create travel", function (Given, When, Then) {
Then.onTheMainPage.iSeeThisPage();
Then.onTheMainPage.onTable().iCheckAction("Create", { enabled: true });
// Click on Create button
When.onTheMainPage.onTable().iExecuteAction("Create");
Then.onTheDetailPage.iSeeObjectPageInEditMode();
When.onTheDetailPage.iGoToSection("General Information");
// Value help Agency ID
When.onTheDetailPage
.onForm({ section: "Travel", fieldGroup: "TravelData" })
.iOpenValueHelp({ property: "to_Agency_AgencyID" });
When.onTheDetailPage.onValueHelpDialog().iSelectRows({ 0: "070006" });
// Value help Customer ID
When.onTheDetailPage
.onForm({ section: "Travel", fieldGroup: "TravelData" })
.iOpenValueHelp({ property: "to_Customer_CustomerID" });
When.onTheDetailPage.onValueHelpDialog().iSelectRows({ 0: "000001" });
// Starting date
const dateFormat = new Intl.DateTimeFormat("en", {
month: "short",
day: "numeric",
year: "numeric",
});
const startDate = new Date();
startDate.setUTCDate(startDate.getUTCDate() + 1);
When.onTheDetailPage
.onForm({ section: "Travel", fieldGroup: "DateData" })
.iChangeField(
{ property: "BeginDate" },
dateFormat.format(startDate)
);
// End date
const endDate = new Date(startDate);
endDate.setUTCDate(endDate.getUTCDate() + 7);
When.onTheDetailPage
.onForm({ section: "Travel", fieldGroup: "DateData" })
.iChangeField({ property: "EndDate" }, dateFormat.format(endDate));
// Booking fee
When.onTheDetailPage
.onForm({ section: "Travel", fieldGroup: "PriceData" })
.iChangeField({ property: "BookingFee" }, "50.00");
// Currency
When.onTheDetailPage
.onForm({ section: "Travel", fieldGroup: "PriceData" })
.iChangeField({ property: "CurrencyCode_code" }, "EUR");
// Description
When.onTheDetailPage
.onForm({ section: "Travel", fieldGroup: "TravelData" })
.iChangeField({ property: "Description" }, "Travel for deletion");
// Save all
Then.onTheDetailPage.onFooter().iCheckDraftStateSaved();
When.onTheDetailPage.onFooter().iExecuteSave();
When.onTheDetailPage.onActionDialog().iConfirm();
Then.onTheDetailPage.iSeeThisPage().and.iSeeObjectPageInDisplayMode();
When.iNavigateBack();
});
opaTest("#4: List report: Delete travel", function (Given, When, Then) {
Then.onTheMainPage.iSeeThisPage();
Then.onTheMainPage
.onTable()
.iCheckDelete({ visible: true, enabled: false });
// select row to be deleted
Given.onTheMainPage
.onTable()
.iSelectRows({ Travel: "Travel for deletion" });
Then.onTheMainPage
.onTable()
.iCheckDelete({ visible: true, enabled: true });
When.onTheMainPage.onTable().iExecuteDelete();
When.onTheMainPage.onDialog().iConfirm();
Then.onTheMainPage
.onTable()
.iCheckDelete({ visible: true, enabled: false });
});
opaTest(
"#5: List report: Check actions (Accept, Reject)",
function (Given, When, Then) {
Then.onTheMainPage.iSeeThisPage();
// Check that bound action is inactive without selection
Then.onTheMainPage
.onTable()
.iCheckAction(
{ service: "TravelService", action: "acceptTravel" },
{ visible: true, enabled: false }
);
// select first row
Given.onTheMainPage.onTable().iSelectRows({ Travel: "4,132" });
// Check that bound action is now active after selection
Then.onTheMainPage
.onTable()
.iCheckAction(
{ service: "TravelService", action: "acceptTravel" },
{ visible: true, enabled: true }
);
// check that "Travel status" is Open
Then.onTheMainPage
.onTable()
.iCheckRows({ Travel: "4,132", "Travel Status": "Open" }, 1);
// trigger action
When.onTheMainPage.onTable().iExecuteAction({
service: "TravelService",
action: "acceptTravel",
});
// check that "Travel status" is now Accepted
Then.onTheMainPage
.onTable()
.iCheckRows({ Travel: "4,132", "Travel Status": "Accepted" }, 1);
// unselect first row
Given.onTheMainPage.onTable().iSelectRows({ Travel: "4,132" });
// select 2nd row
Given.onTheMainPage.onTable().iSelectRows({ Travel: "4,131" });
// check that "Travel status" is Open
Then.onTheMainPage
.onTable()
.iCheckRows({ Travel: "4,131", "Travel Status": "Open" }, 1);
// trigger action
When.onTheMainPage.onTable().iExecuteAction({
service: "TravelService",
action: "rejectTravel",
});
// check that "Travel status" is Open
Then.onTheMainPage
.onTable()
.iCheckRows({ Travel: "4,131", "Travel Status": "Canceled" }, 1);
// unselect 2nd row
Given.onTheMainPage.onTable().iSelectRows({ Travel: "4,131" });
Then.onTheMainPage.iSeeThisPage();
}
);
opaTest(
"#6: Object Page: Check details for accepted travel",
function (Given, When, Then) {
// Open travelk with travel status = "Accepted"
When.onTheMainPage.onTable().iPressRow({ Travel: "4,133" });
Then.onTheDetailPage.iSeeThisPage();
When.onTheDetailPage.onHeader().iExecuteAction("Edit");
Then.onTheDetailPage.iSeeObjectPageInEditMode();
When.onTheDetailPage.iGoToSection("My Itinerary");
// Check buttons for bookings
Then.onTheDetailPage
.onTable({
id: "sap.fe.cap.travel::TravelObjectPage--fe::CustomSubSection::CustomSection--bookingTable-content",
})
.iCheckDelete({ visible: false, enabled: false })
.and.iCheckCreate({ visible: false, enabled: false });
// Check fields
When.onTheDetailPage.iGoToSection("Travel");
// Starting date
Then.onTheDetailPage
.onForm({ section: "Travel", fieldGroup: "DateData" })
.iCheckField(
{ property: "BeginDate" },
{ value: "Dec 11, 2025" },
{ editable: false }
);
// Booking fee
Then.onTheDetailPage
.onForm({ section: "Travel", fieldGroup: "PriceData" })
.iCheckField({ property: "BookingFee" }, { editable: false });
When.onTheDetailPage.iGoToSection("General Information");
When.onTheDetailPage
.onTable({
id: "sap.fe.cap.travel::TravelObjectPage--fe::CustomSubSection::CustomSection--bookingTable-content",
})
.iPressRow({ BookingID: "1" });
Then.onTheDetailItemPage.iSeeThisPage();
// Check fields
When.onTheDetailItemPage.iGoToSection("General Information");
// Flight Price
Then.onTheDetailItemPage
.onForm({ section: "Booking", fieldGroup: "FlightData" })
.iCheckField({ property: "FlightPrice" }, { editable: false });
// Flight Number
Then.onTheDetailItemPage
.onForm({ section: "Booking", fieldGroup: "FlightData" })
.iCheckField(
{ property: "ConnectionID" },
{ value: "0018" },
{ editable: false }
);
// Check buttons for booking supplements
When.onTheDetailItemPage.iGoToSection("Booking Supplement");
When.onTheDetailItemPage
.onTable({ property: "to_BookSupplement" })
.iSelectRows({ BookingSupplementID: "1" });
Then.onTheDetailItemPage
.onTable({ property: "to_BookSupplement" })
.iCheckDelete({ visible: true, enabled: false })
.and.iCheckCreate({ visible: true, enabled: false });
When.iNavigateBack();
Then.onTheDetailPage.iSeeThisPage();
When.onTheDetailPage.onFooter().iExecuteCancel();
Then.onTheDetailPage.iSeeObjectPageInDisplayMode();
When.iNavigateBack();
Then.onTheMainPage.iSeeThisPage();
}
);
opaTest(
"#7: Object Page: Check details for open travel",
function (Given, When, Then) {
// Open travel with travel status = "open"
When.onTheMainPage.onTable().iPressRow({ Travel: "4,129" });
Then.onTheDetailPage.iSeeThisPage();
When.onTheDetailPage.onHeader().iExecuteAction("Edit");
Then.onTheDetailPage.iSeeObjectPageInEditMode();
When.onTheDetailPage.iGoToSection("My Itinerary");
// Check buttons
When.onTheDetailPage
.onTable({
id: "sap.fe.cap.travel::TravelObjectPage--fe::CustomSubSection::CustomSection--bookingTable-content",
})
.iSelectRows({ BookingID: "2" });
Then.onTheDetailPage
.onTable({
id: "sap.fe.cap.travel::TravelObjectPage--fe::CustomSubSection::CustomSection--bookingTable-content",
})
.iCheckDelete({ visible: true, enabled: true })
.and.iCheckCreate({ visible: true, enabled: true });
// Check fields
When.onTheDetailPage.iGoToSection("Travel");
// Starting date
Then.onTheDetailPage
.onForm({ section: "Travel", fieldGroup: "DateData" })
.iCheckField(
{ property: "BeginDate" },
{ value: "Dec 11, 2025" },
{ editable: true }
);
// Booking fee
Then.onTheDetailPage
.onForm({ section: "Travel", fieldGroup: "PriceData" })
.iCheckField({ property: "BookingFee" }, { editable: true });
When.onTheDetailPage.iGoToSection("General Information");
When.onTheDetailPage
.onTable({
id: "sap.fe.cap.travel::TravelObjectPage--fe::CustomSubSection::CustomSection--bookingTable-content",
})
.iPressRow({ BookingID: "2" });
Then.onTheDetailItemPage.iSeeThisPage();
// Check fields
When.onTheDetailItemPage.iGoToSection("General Information");
// Flight Price
Then.onTheDetailItemPage
.onForm({ section: "Booking", fieldGroup: "FlightData" })
.iCheckField({ property: "FlightPrice" }, { editable: true });
// Flight Number
Then.onTheDetailItemPage
.onForm({ section: "Booking", fieldGroup: "FlightData" })
.iCheckField(
{ property: "ConnectionID" },
{ value: "0018" },
{ editable: true }
);
// Check buttons for booking supplements
When.onTheDetailItemPage.iGoToSection("Booking Supplements");
When.onTheDetailItemPage
.onTable({ property: "to_BookSupplement" })
.iSelectRows({ BookingSupplementID: "1" });
Then.onTheDetailItemPage
.onTable({ property: "to_BookSupplement" })
.iCheckDelete({ visible: true, enabled: true })
.and.iCheckCreate({ visible: true, enabled: true });
When.iNavigateBack();
Then.onTheDetailPage.iSeeThisPage();
When.onTheDetailPage.onFooter().iExecuteCancel();
Then.onTheDetailPage.iSeeObjectPageInDisplayMode();
When.iNavigateBack();
Then.onTheMainPage.iSeeThisPage();
}
);
return Journey;
},
end: function () {
opaTest("#999: Tear down", function (Given) {
Given.iTearDownMyApp();
});
return Journey;
},
};
Journey.run = function () {
Journey.start().test().end();
};
return Journey;
});
================================================
FILE: app/travel_processor/webapp/test/integration/pages/ItemObjectPage.js
================================================
sap.ui.define(["sap/fe/test/ObjectPage"], function (ObjectPage) {
"use strict";
// OPTIONAL
const AdditionalCustomObjectPageDefinition = {
actions: {},
assertions: {},
};
return new ObjectPage(
{
appId: "sap.fe.cap.travel",
componentId: "BookingObjectPage",
entitySet: "Booking",
},
AdditionalCustomObjectPageDefinition
);
});
================================================
FILE: app/travel_processor/webapp/test/integration/pages/MainListReport.js
================================================
sap.ui.define(["sap/fe/test/ListReport"], function (ListReport) {
"use strict";
// OPTIONAL
const AdditionalCustomListReportDefinition = {
actions: {},
assertions: {},
};
return new ListReport(
{
appId: "sap.fe.cap.travel",
componentId: "TravelList",
entitySet: "Travel",
},
AdditionalCustomListReportDefinition
);
});
================================================
FILE: app/travel_processor/webapp/test/integration/pages/MainObjectPage.js
================================================
sap.ui.define(["sap/fe/test/ObjectPage"], function (ObjectPage) {
"use strict";
// OPTIONAL
const AdditionalCustomObjectPageDefinition = {
actions: {},
assertions: {},
};
return new ObjectPage(
{
appId: "sap.fe.cap.travel",
componentId: "TravelObjectPage",
entitySet: "Travel",
},
AdditionalCustomObjectPageDefinition
);
});
================================================
FILE: app/travel_processor/webapp/test/testsuite.qunit.html
================================================
<!DOCTYPE html>
<html lang="en">
<head>
<title>Testsuite</title>
<script src="/resources/sap/ui/qunit/qunit-redirect.js"></script>
<script src="./testsuite.qunit.js" data-sap-ui-testsuite></script>
</head>
<body></body>
</html>
================================================
FILE: app/travel_processor/webapp/test/testsuite.qunit.js
================================================
/* global window, parent, location */
window.suite = function () {
"use strict";
const oSuite = new parent.jsUnitTestSuite();
const sContextPath = location.pathname.substring(
0,
location.pathname.lastIndexOf("/")
);
oSuite.addTestPage(`${sContextPath}/integration/Opa.qunit.html`);
return oSuite;
};
================================================
FILE: app/travel_processor/xs-app.json
================================================
{
"welcomeFile": "/index.html",
"authenticationMethod": "route",
"routes": [
{
"source": "^/processor/(.*)$",
"target": "/processor/$1",
"destination": "sflight-srv",
"authenticationType": "xsuaa",
"csrfProtection": false
},
{
"source": "^(.*)$",
"target": "$1",
"service": "html5-apps-repo-rt",
"authenticationType": "xsuaa"
}
]
}
================================================
FILE: app/travel_processor/xs-security.json
================================================
{
"scopes": [
{
"name": "$XSAPPNAME.reviewer",
"description": "reviewer"
},
{
"name": "$XSAPPNAME.processor",
"description": "processor"
},
{
"name": "$XSAPPNAME.admin",
"description": "admin"
}
],
"attributes": [],
"role-templates": [
{
"name": "reviewer",
"description": "generated",
"scope-references": [
"$XSAPPNAME.reviewer"
],
"attribute-references": []
},
{
"name": "processor",
"description": "generated",
"scope-references": [
"$XSAPPNAME.processor"
],
"attribute-references": []
},
{
"name": "admin",
"description": "generated",
"scope-references": [
"$XSAPPNAME.admin"
],
"attribute-references": []
}
]
}
================================================
FILE: app/value-helps.cds
================================================
using { sap.fe.cap.travel as schema } from '../db/schema';
//
// annotations for value helps
//
annotate schema.Travel {
TravelStatus @Common.ValueListWithFixedValues;
to_Agency @Common.ValueList: {
CollectionPath : 'TravelAgency',
Label : '',
Parameters : [
{$Type: 'Common.ValueListParameterInOut', LocalDataProperty: to_Agency_AgencyID, ValueListProperty: 'AgencyID'}, // local data property is the foreign key
{$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'Name'},
{$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'Street'},
{$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'PostalCode'},
{$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'City'},
{$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'CountryCode_code'},
{$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'PhoneNumber'},
{$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'EMailAddress'},
{$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'WebAddress'}
]
};
to_Customer @Common.ValueList: {
CollectionPath : 'Passenger',
Label : 'Customer ID',
Parameters : [
{$Type: 'Common.ValueListParameterInOut', LocalDataProperty: to_Customer_CustomerID, ValueListProperty: 'CustomerID'},
{$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'FirstName'},
{$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'LastName'},
{$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'Title'},
{$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'Street'},
{$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'PostalCode'},
{$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'City'},
{$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'CountryCode_code'},
{$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'PhoneNumber'},
{$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'EMailAddress'}
]
};
CurrencyCode @Common.ValueList: {
CollectionPath : 'Currencies',
Label : '',
Parameters : [
{$Type: 'Common.ValueListParameterInOut', LocalDataProperty: CurrencyCode_code, ValueListProperty: 'code'},
{$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'name'},
{$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'descr'},
{$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'symbol'},
{$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'minor'}
]
};
}
annotate schema.Booking {
BookingStatus @Common.ValueListWithFixedValues;
to_Customer @Common.ValueList: {
CollectionPath : 'Passenger',
Label : '',
Parameters : [
{$Type: 'Common.ValueListParameterInOut', LocalDataProperty: to_Customer_CustomerID, ValueListProperty: 'CustomerID'},
{$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'FirstName'},
{$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'LastName'},
{$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'Title'},
{$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'Street'},
{$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'PostalCode'},
{$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'City'},
{$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'CountryCode_code'},
{$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'PhoneNumber'},
{$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'EMailAddress'}
]
};
to_Carrier @Common.ValueList: {
CollectionPath : 'Airline',
Label : '',
Parameters : [
{$Type: 'Common.ValueListParameterInOut', LocalDataProperty: to_Carrier_AirlineID, ValueListProperty: 'AirlineID'},
{$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'Name'},
{$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'CurrencyCode_code'}
]
};
ConnectionID @Common.ValueList: {
CollectionPath : 'Flight',
Label : '',
Parameters : [
{$Type: 'Common.ValueListParameterInOut', LocalDataProperty: to_Carrier_AirlineID, ValueListProperty: 'AirlineID'},
{$Type: 'Common.ValueListParameterInOut', LocalDataProperty: ConnectionID, ValueListProperty: 'ConnectionID'},
{$Type: 'Common.ValueListParameterInOut', LocalDataProperty: FlightDate, ValueListProperty: 'FlightDate'},
{$Type: 'Common.ValueListParameterInOut', LocalDataProperty: FlightPrice, ValueListProperty: 'Price'},
{$Type: 'Common.ValueListParameterInOut', LocalDataProperty: CurrencyCode_code, ValueListProperty: 'CurrencyCode_code'},
{$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'to_Airline/Name'},
{$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'PlaneType'},
{$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'MaximumSeats'},
{$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'OccupiedSeats'}
],
PresentationVariantQualifier: 'SortOrderPV' // use presentation variant to sort by FlightDate desc
};
FlightDate @Common.ValueList: {
CollectionPath : 'Flight',
Label : '',
Parameters : [
{$Type: 'Common.ValueListParameterInOut', LocalDataProperty: to_Carrier_AirlineID, ValueListProperty: 'AirlineID'},
{$Type: 'Common.ValueListParameterInOut', LocalDataProperty: ConnectionID, ValueListProperty: 'ConnectionID'},
{$Type: 'Common.ValueListParameterInOut', LocalDataProperty: FlightDate, ValueListProperty: 'FlightDate'},
{$Type: 'Common.ValueListParameterInOut', LocalDataProperty: FlightPrice, ValueListProperty: 'Price'},
{$Type: 'Common.ValueListParameterInOut', LocalDataProperty: CurrencyCode_code, ValueListProperty: 'CurrencyCode_code'},
{$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'to_Airline/Name'},
{$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'PlaneType'},
{$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'MaximumSeats'},
{$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'OccupiedSeats'}
]
};
CurrencyCode @Common.ValueList: {
CollectionPath : 'Currencies',
Label : '',
Parameters : [
{$Type: 'Common.ValueListParameterInOut', LocalDataProperty: CurrencyCode_code, ValueListProperty: 'code'},
{$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'name'},
{$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'descr'},
{$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'symbol'},
{$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'minor'}
]
};
}
annotate schema.BookingSupplement {
to_Supplement @Common.ValueList: {
CollectionPath : 'Supplement',
Label : '',
Parameters : [
{$Type: 'Common.ValueListParameterInOut', LocalDataProperty: to_Supplement_SupplementID, ValueListProperty: 'SupplementID'},
{$Type: 'Common.ValueListParameterInOut', LocalDataProperty: Price, ValueListProperty: 'Price'},
{$Type: 'Common.ValueListParameterInOut', LocalDataProperty: CurrencyCode_code, ValueListProperty: 'CurrencyCode_code'},
{$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'Description'}
]
};
CurrencyCode @Common.ValueList: {
CollectionPath : 'Currencies',
Label : '',
Parameters : [
{$Type: 'Common.ValueListParameterInOut', LocalDataProperty: CurrencyCode_code, ValueListProperty: 'code'},
{$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'name'},
{$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'descr'},
{$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'symbol'},
{$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'minor'}
]
};
}
annotate schema.Flight with @UI.PresentationVariant#SortOrderPV : { // used in ValueList for Bookings:ConnectionId above
SortOrder : [{
Property : FlightDate,
Descending : true
}]
}
{
AirlineID @Common.ValueList: {
CollectionPath : 'Airline',
Label : '',
Parameters : [
{$Type: 'Common.ValueListParameterInOut', LocalDataProperty: AirlineID, ValueListProperty: 'AirlineID'},
{$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'Name'},
{$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'CurrencyCode'}
]
};
ConnectionID @Common.ValueList: {
CollectionPath : 'FlightConnection',
Label : '',
Parameters : [
{$Type: 'Common.ValueListParameterInOut', LocalDataProperty: AirlineID, ValueListProperty: 'AirlineID'},
{$Type: 'Common.ValueListParameterInOut', LocalDataProperty: ConnectionID, ValueListProperty: 'ConnectionID'},
{$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'AirlineID_Text'},
{$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'DepartureAirport'},
{$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'DestinationAirport'},
{$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'DepartureTime'},
{$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'ArrivalTime'},
{$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'Distance'},
{$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'DistanceUnit'}
]
};
};
annotate schema.FlightConnection {
AirlineID @Common.ValueList: {
CollectionPath : 'Airline',
Label : '',
Parameters : [
{$Type: 'Common.ValueListParameterInOut', LocalDataProperty: AirlineID, ValueListProperty: 'CarrierID'},
{$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'AirlineID'},
{$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'Name'},
{$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'CurrencyCode'}
]
};
DepartureAirport @Common.ValueList: {
CollectionPath : 'Airport',
Label : '',
Parameters : [
{$Type: 'Common.ValueListParameterInOut', LocalDataProperty: DepartureAirport_AirportID, ValueListProperty: 'Airport_ID'}, // here FK is required
{$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'AirportID'},
{$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'Name'},
{$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'City'},
{$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'CountryCode'}
]
};
DestinationAirport @Common.ValueList: {
CollectionPath : 'Airport',
Label : '',
Parameters : [
{$Type: 'Common.ValueListParameterInOut', LocalDataProperty: DestinationAirport_AirportID, ValueListProperty: 'Airport_ID'}, // here FK is required
{$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'AirportID'},
{$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'Name'},
{$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'City'},
{$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'CountryCode'}
]
};
}
annotate schema.Passenger {
CountryCode @Common.ValueList : {
CollectionPath : 'Countries',
Label : '',
Parameters : [
{$Type: 'Common.ValueListParameterInOut', LocalDataProperty : CountryCode_code, ValueListProperty : 'code'},
{$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty : 'name'},
{$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty : 'descr'}
]
};
}
annotate schema.TravelAgency {
CountryCode @Common.ValueList: {
CollectionPath : 'Countries',
Label : '',
Parameters : [
{$Type: 'Common.ValueListParameterInOut', LocalDataProperty: CountryCode_code, ValueListProperty: 'code'},
{$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty : 'name'},
{$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty : 'descr'}
]
};
}
================================================
FILE: db/common.cds
================================================
using { sap, managed } from '@sap/cds/common';
extend sap.common.Currencies with {
// Currencies.code = ISO 4217 alphabetic three-letter code
// with the first two letters being equal to ISO 3166 alphabetic country codes
// See also:
// [1] https://www.iso.org/iso-4217-currency-codes.html
// [2] https://www.currency-iso.org/en/home/tables/table-a1.html
// [3] https://www.ibm.com/support/knowledgecenter/en/SSZLC2_7.0.0/com.ibm.commerce.payments.developer.doc/refs/rpylerl2mst97.htm
numcode : Integer;
exponent : Integer; //> e.g. 2 --> 1 Dollar = 10^2 Cent
minor : String; //> e.g. 'Cent'
}
aspect custom.managed {
createdAt : managed:createdAt;
createdBy : managed:createdBy;
LastChangedAt : managed:modifiedAt;
LastChangedBy : managed:modifiedBy;
}
================================================
FILE: db/data/sap.common-Countries.csv
================================================
code;name;descr
AU;Australia;Commonwealth of Australia
CA;Canada;Canada
CN;China;People's Republic of China (PRC)
FR;France;French Republic
DE;Germany;Federal Republic of Germany
IN;India;Republic of India
IL;Israel;State of Israel
MM;Myanmar;Republic of the Union of Myanmar
GB;United Kingdom;United Kingdom of Great Britain and Northern Ireland
US;United States;United States of America (USA)
EU;European Union;European Union
IT;Italy;Italy
SG;Singapore;Singapore
AT;Austria;Austria
SE;Sweden;Sweden
CH;Switzerland;Switzerland
RU;Russia;Russia
JP;Japan;Japan
ZA;South Africa;South Africa
ES;Spain;Spain
BE;Belgium;Belgium
SI;Slovenia;Slovenia
NL;Netherlands;Netherlands
MX;Mexico;Mexico
BR;Brazil;Brazil
CU;Cuba;Cuba
ZW;Zimbabwe;Zimbabwe
MY;Malaysia;Malaysia
TH;Thailand;Thailand
================================================
FILE: db/data/sap.common-Countries.texts.csv
================================================
code;locale;name;descr
AU;de;Australien;Commonwealth Australien
CA;de;Kanada;Canada
CN;de;China;Volksrepublik China
FR;de;Frankreich;Republik Frankreich
DE;de;Deutschland;Bundesrepublik Deutschland
IN;de;Indien;Republik Indien
IL;de;Israel;Staat Israel
MM;de;Myanmar;Republik der Union Myanmar
GB;de;Vereinigtes Königreich;Vereinigtes Königreich Großbritannien und Nordirland
US;de;Vereinigte Staaten;Vereinigte Staaten von Amerika
EU;de;Europäische Union;Europäische Union
IT;de;Italien;Italien
SG;de;Singapur;Singapur
AT;de;Österreich;Österreich
SE;de;Schweden;Schweden
CH;de;Schweiz;Schweiz
RU;de;Russland;Russland
JP;de;Japan;Japan
ZA;de;Südafrika;Südafrika
ES;de;Spanien;Spanien
BE;de;Belgien;Belgien
SI;de;Slowenien;Slowenien
NL;de;Niederlande;Niederlande
MX;de;Mexiko;Mexiko
BR;de;Brasilien;Brasilien
CU;de;Kuba;Kuba
ZW;de;Simbabwe;Simbabwe
MY;de;Malaysia;Malaysia
TH;de;Thailand;Thailand
AU;fr;Australie;Commonwealth d'Australie
CA;fr;Canada;Canada
CN;fr;Chine;République populaire de Chine (RPC)
FR;fr;France;République française
DE;fr;Allemagne;République fédérale d'Allemagne
IN;fr;Inde;République de l'Inde
IL;fr;Israël;État d'Israël
MM;fr;Birmanie;République de l'Union du Myanmar
GB;fr;Royaume-Uni;Royaume-Uni et Irlande du Nord
US;fr;États-Unis;États-Unis d'Amérique (États-Unis)
EU;fr;Union Européenne;Union Européenne
IT;fr;Italie;Italie
SG;fr;Singapour;Singapour
AT;fr;Autriche;Autriche
SE;fr;Suède;Suède
CH;fr;Suisse;Suisse
RU;fr;Russie;Russie
JP;fr;Japon;Japon
ZA;fr;Afrique du Sud;Afrique du Sud
ES;fr;Espagne;Espagne
BE;fr;Belgique;Belgique
SI;fr;Slovénie;Slovénie
NL;fr;Pays-Bas;Pays-Bas
MX;fr;Mexique;Mexique
BR;fr;Brésil;Brésil
CU;fr;Cuba;Cuba
ZW;fr;Zimbabwe;Zimbabwe
MY;fr;Malaisie;Malaisie
TH;fr;Thaïlande;Thaïlande
AU;en;Australie;Commonwealth d'Australie
CA;en;Canada;Canada
CN;en;China;People's Republic of China (PRC)
FR;en;France;French Republic
DE;en;Germany;Federal Republic of Germany
IN;en;India;Republic of India
IL;en;Israel;State of Israel
MM;en;Myanmar;Republic of the Union of Myanmar
GB;en;United Kingdom;United Kingdom of Great Britain and Northern Ireland
US;en;United States;United States of America (USA)
EU;en;European Union;European Union
IT;en;Italy;Italy
SG;en;Singapore;Singapore
AT;en;Austria;Austria
SE;en;Sweden;Sweden
CH;en;Switzerland;Switzerland
RU;en;Russia;Russia
JP;en;Japan;Japan
ZA;en;South Africa;South Africa
ES;en;Spain;Spain
BE;en;Belgium;Belgium
SI;en;Slovenia;Slovenia
NL;en;Netherlands;Netherlands
MX;en;Mexico;Mexico
BR;en;Brazil;Brazil
CU;en;Cuba;Cuba
ZW;en;Zimbabwe;Zimbabwe
MY;en;Malaysia;Malaysia
TH;en;Thailand;Thailand
================================================
FILE: db/data/sap.common-Currencies.csv
================================================
code;symbol;name;descr;numcode;minor;exponent
EUR;€;Euro;European Euro;978;Cent;2
USD;$;US Dollar;United States Dollar;840;Cent;2
CAD;$;Canadian Dollar;Canadian Dollar;124;Cent;2
AUD;$;Australian Dollar;Australian Dollar;036;Cent;2
GBP;£;British Pound;Great Britain Pound;826;Penny;2
ILS;₪;Shekel;Israeli New Shekel;376;Agorat;2
INR;₹;Rupee;Indian Rupee;356;Paise;2
QAR;﷼;Riyal;Katar Riyal;356;Dirham;2
SAR;﷼;Riyal;Saudi Riyal;682;Halala;2
JPY;¥;Yen;Japanese Yen;392;Sen;2
CNY;¥;Yuan;Chinese Yuan Renminbi;156;Jiao;1
SGD;S$;Singapore Dollar;Singapore Dollar;702;Cent;2
ZAR;R;Rand;South African Rand;710;Cent;2
================================================
FILE: db/data/sap.common-Currencies.texts.csv
================================================
code;locale;name;descr
EUR;de;Euro;European Euro
USD;de;US-Dollar;United States Dollar
CAD;de;Kanadischer Dollar;Kanadischer Dollar
AUD;de;Australischer Dollar;Australischer Dollar
GBP;de;Pfund;Britische Pfund
ILS;de;Schekel;Israelische Schekel
INR;de;Rupie;Indische Rupie
QAR;de;RiYal;Riyal (Katar)
SAR;de;RiYal;Riyal (Saudi Arabien)
JPY;de;Yen;Japanische Yen
CNY;de;Renminbi Yuan;Chinesische Renminbi Yuan
SGD;de;Singapurischer Dollar;Singapurischer Dollar
ZAR;de;Rand;Südafrikanische Rand
EUR;fr;euro;de la Zone euro
USD;fr;dollar;dollar des États-Unis
CAD;fr;dollar canadien;dollar canadien
AUD;fr;dollar australien;dollar australien
GBP;fr;livre sterling;pound sterling
ILS;fr;Shekel;shekel israelien
INR;fr;Roupie;Roupie
QAR;fr;Rial;Rial
SAR;fr;Riyal;Riyal
JPY;fr;Yen;Yen
CNY;fr;yuan renminbi;yuan renminbi
SGD;fr;Singapour Dollar;Singapour Dollar
ZAR;fr;Rand;Rand
EUR;en;euro;euro
USD;en;dollar;US dollar
CAD;en;canadien dollar;canadien dollar
AUD;en;australien dollar;australien dollar
GBP;en;pound sterling;pound sterling
ILS;en;Shekel;shekel
INR;en;Rupie;indian rupie
QAR;en;Riyal;qatar riyal
SAR;en;Riyal;saudi arabian Riyal
JPY;en;Yen;japanese yen
CNY;en;Yuan;chinese yuan
SGD;en;Singapore dollar;Singapore dollar
ZAR;en;Rand;south african rand
================================================
FILE: db/data/sap.fe.cap.travel-Airline.csv
================================================
AirlineID;Name;CurrencyCode_code;AirlinePicURL
GA;Green Albatros;CAD;https://raw.githubusercontent.com/SAP-samples/fiori-elements-opensap/main/week1/images/airlines/Green-Albatross-logo.png
FA;Fly Africa;ZAR;https://raw.githubusercontent.com/SAP-samples/fiori-elements-opensap/main/week1/images/airlines/Fly-Africa-logo.png
EA;European Airlines;EUR;https://raw.githubusercontent.com/SAP-samples/fiori-elements-opensap/main/week1/images/airlines/European-Airlines-logo.png
OC;Oceania;USD;https://raw.githubusercontent.com/SAP-samples/fiori-elements-opensap/main/week1/images/airlines/Oceania-logo.png
SW;Sunset Wings;USD;https://raw.githubusercontent.com/SAP-samples/fiori-elements-opensap/main/week1/images/airlines/Sunset-Wings-logo.png
================================================
FILE: db/data/sap.fe.cap.travel-Airport.csv
================================================
AirportID;Name;City;CountryCode_code
FRA;Frankfurt Airport;Frankfurt/Main;DE
HAM;Hamburg Airport;Hamburg;DE
MUC;Munich Airport;Munich;DE
SXF;Berlin Schönefeld Airport;Berlin;DE
THF;Berlin Tempelhof Airport;Berlin;DE
TXL;Berlin Tegel Airport;Berlin;DE
CDG;Charles de Gaulle Airport;Paris;FR
ORY;Orly Airport;Paris;FR
VIE;Vienna International Airport;Vienna;AT
ZRH;Zürich Airport;Zurich;CH
RTM;Rotterdam The Hague Airport;Rotterdam;NL
FCO;Leonardo da Vinci–Fiumicino Airport;Rome;IT
VCE;Venice Marco Polo Airport;Venice;IT
LCY;London City Airport;London;GB
LGW;Gatwick Airport;London;GB
LHR;Heathrow Airport;London;GB
MAD;Adolfo Suárez Madrid–Barajas Airport;Madrid;ES
VKO;Vnukovo International Airport;Moscow;RU
SVO;Sheremetyevo International Airport;Moscow;RU
JFK;John F. Kennedy International Airport;New York City, New York;US
BNA;Nashville International Airport;Nashville, Tennessee;US
BOS;Logan International Airport;Boston, Massachusetts;US
ELP;El Paso International Airport;El Paso, Texas;US
DEN;Denver International Airport;Denver, Colorado;US
HOU;William P. Hobby Airport;Houston, Texas;US
LAS;McCarran International Airport;Las Vegas, Nevada;US
LAX;Los Angeles International Airport;Los Angeles, California;US
MCI;Kansas City International Airport;Kansas City, Missouri;US
MIA;Miami International Airport;Miami, Florida;US
SFO;San Francisco International Airport;San Francisco, California;US
EWR;Newark Liberty International Airport;Newark, New Jersey;US
YOW;Ottawa Macdonald–Cartier Int. Airport;Ottawa, Ontario;CA
ACA;General Juan N. Álvarez Int. Airport;Acapulco, Guerrero;MX
GIG;Rio de Janeiro–Galeão Int. Airport;Rio de Janeiro;BR
HAV;José Martí International Airport;Havana;CU
ASP;Alice Springs Airport;Alice Springs, Northern Territory;AU
ACE;Lanzarote Airport;Lanzarote, Canary Islands;ES
HRE;Harare International Airport;Harare;ZW
GCJ;Grand Central Airport;Johannesburg;ZA
NRT;Narita International Airport;Tokyo, Honshu;JP
ITM;Osaka International Airport;Osaka, Honshu;JP
KIX;Kansai International Airport;Osaka, Honshu;JP
HIJ;Hiroshima Airport;Hiroshima, Honshu;JP
SIN;Singapore Changi Airport;Singapore;SG
KUL;Kuala Lumpur International Airport;Kuala Lumpur;MY
HKG;Hong Kong International Airport;Hongkong;CN
BKK;Suvarnabhumi Airport;Bangkok;TH
================================================
FILE: db/data/sap.fe.cap.travel-Booking.csv
================================================
BookingUUID;to_Travel_TravelUUID;BookingID;BookingDate;to_Customer_CustomerID;to_Carrier_AirlineID;ConnectionID;FlightDate;FlightPrice;CurrencyCode_code;BookingStatus_code;LastChangedAt
7A757221A8E4645C17002DF03754AB66;52657221A8E4645C17002DF03754AB66;0001;2025-02-12;000099;SW;1537;2025-02-14;438.00;USD;N;2025-01-31T19:48:08Z
7B757221A8E4645C17002DF03754AB66;52657221A8E4645C17002DF03754AB66;0002;2025-02-12;000660;SW;1537;2025-02-14;438.00;USD;N;2025-01-31T19:48:08Z
7C757221A8E4645C17002DF03754AB66;53657221A8E4645C17002DF03754AB66;0001;2025-01-25;000093;SW;1537;2025-02-14;438.00;USD;N;2025-01-31T21:07:58Z
7D757221A8E4645C17002DF03754AB66;53657221A8E4645C17002DF03754AB66;0002;2025-01-25;000706;SW;1537;2025-02-14;438.00;USD;N;2025-01-31T21:07:58Z
7E757221A8E4645C17002DF03754AB66;53657221A8E4645C17002DF03754AB66;0003;2025-01-28;000093;GA;0322;2025-02-16;438.00;USD;X;2025-01-31T21:07:58Z
7F757221A8E4645C17002DF03754AB66;53657221A8E4645C17002DF03754AB66;0004;2025-01-28;000706;GA;0322;2025-02-16;438.00;USD;N;2025-01-31T21:07:58Z
80757221A8E4645C17002DF03754AB66;53657221A8E4645C17002DF03754AB66;0005;2025-12-08;000093;SW;1537;2025-12-12;438.00;USD;N;2025-01-31T21:07:58Z
81757221A8E4645C17002DF03754AB66;53657221A8E4645C17002DF03754AB66;0006;2025-12-08;000706;SW;1537;2025-12-12;438.00;USD;N;2025-01-31T21:07:58Z
82757221A8E4645C17002DF03754AB66;53657221A8E4645C17002DF03754AB66;0007;2025-12-01;000093;GA;0322;2025-12-14;438.00;USD;N;2025-01-31T21:07:58Z
83757221A8E4645C17002DF03754AB66;53657221A8E4645C17002DF03754AB66;0008;2025-12-01;000706;GA;0322;2025-12-14;438.00;USD;N;2025-01-31T21:07:58Z
84757221A8E4645C17002DF03754AB66;54657221A8E4645C17002DF03754AB66;0001;2025-02-08;000665;SW;1537;2025-02-14;438.00;USD;N;2025-01-30T03:37:18Z
85757221A8E4645C17002DF03754AB66;54657221A8E4645C17002DF03754AB66;0002;2025-02-14;000665;GA;0322;2025-02-16;438.00;USD;N;2025-01-30T03:37:18Z
86757221A8E4645C17002DF03754AB66;54657221A8E4645C17002DF03754AB66;0003;2025-12-01;000665;SW;1537;2025-12-12;438.00;USD;N;2025-01-30T03:37:18Z
87757221A8E4645C17002DF03754AB66;54657221A8E4645C17002DF03754AB66;0004;2025-12-09;000665;GA;0322;2025-12-14;438.00;USD;N;2025-01-30T03:37:18Z
88757221A8E4645C17002DF03754AB66;55657221A8E4645C17002DF03754AB66;0001;2025-02-11;000161;SW;1537;2025-02-14;438.00;USD;X;2025-02-11T21:16:11Z
89757221A8E4645C17002DF03754AB66;55657221A8E4645C17002DF03754AB66;0002;2025-02-11;000461;SW;1537;2025-02-14;438.00;USD;N;2025-02-11T21:16:11Z
8A757221A8E4645C17002DF03754AB66;56657221A8E4645C17002DF03754AB66;0001;2025-02-03;000072;SW;1537;2025-02-14;438.00;USD;N;2025-01-30T15:57:59Z
8B757221A8E4645C17002DF03754AB66;56657221A8E4645C17002DF03754AB66;0002;2025-02-03;000399;SW;1537;2025-02-14;438.00;USD;N;2025-01-30T15:57:59Z
8C757221A8E4645C17002DF03754AB66;56657221A8E4645C17002DF03754AB66;0003;2025-02-03;000203;SW;1537;2025-02-14;438.00;USD;N;2025-01-30T15:57:59Z
8D757221A8E4645C17002DF03754AB66;56657221A8E4645C17002DF03754AB66;0004;2025-01-27;000072;GA;0322;2025-02-16;438.00;USD;N;2025-01-30T15:57:59Z
8E757221A8E4645C17002DF03754AB66;56657221A8E4645C17002DF03754AB66;0005;2025-01-27;000399;GA;0322;2025-02-16;438.00;USD;N;2025-01-30T15:57:59Z
8F757221A8E4645C17002DF03754AB66;56657221A8E4645C17002DF03754AB66;0006;2025-01-27;000203;GA;0322;2025-02-16;438.00;USD;N;2025-01-30T15:57:59Z
90757221A8E4645C17002DF03754AB66;56657221A8E4645C17002DF03754AB66;0007;2025-11-22;000072;SW;1537;2025-12-12;438.00;USD;N;2025-01-30T15:57:59Z
91757221A8E4645C17002DF03754AB66;56657221A8E4645C17002DF03754AB66;0008;2025-11-22;000399;SW;1537;2025-12-12;438.00;USD;N;2025-01-30T15:57:59Z
92757221A8E4645C17002DF03754AB66;56657221A8E4645C17002DF03754AB66;0009;2025-11-22;000203;SW;1537;2025-12-12;438.00;USD;X;2025-01-30T15:57:59Z
93757221A8E4645C17002DF03754AB66;56657221A8E4645C17002DF03754AB66;0010;2025-11-24;000072;GA;0322;2025-12-14;438.00;USD;N;2025-01-30T15:57:59Z
94757221A8E4645C17002DF03754AB66;56657221A8E4645C17002DF03754AB66;0011;2025-11-24;000399;GA;0322;2025-12-14;438.00;USD;N;2025-01-30T15:57:59Z
95757221A8E4645C17002DF03754AB66;56657221A8E4645C17002DF03754AB66;0012;2025-11-24;000203;GA;0322;2025-12-
gitextract_co2fgw5z/ ├── .cdsrc.json ├── .devcontainer/ │ ├── cds-dk/ │ │ ├── devcontainer-feature.json │ │ └── install.sh │ ├── cf-deploy/ │ │ ├── devcontainer-feature.json │ │ └── install.sh │ └── devcontainer.json ├── .github/ │ ├── actions/ │ │ ├── btp/ │ │ │ ├── Dockerfile │ │ │ ├── action.yml │ │ │ └── entrypoint.sh │ │ └── cf-deploy/ │ │ ├── Dockerfile │ │ ├── action.yml │ │ └── entrypoint.sh │ ├── dependabot.yml │ ├── deployment/ │ │ └── kyma/ │ │ └── scripts/ │ │ ├── build-ui-image.sh │ │ ├── create-container-registry-secret.sh │ │ ├── create-db-secret.sh │ │ ├── format-kyma-secret.js │ │ ├── prepareUiFiles.js │ │ ├── value.js │ │ └── values.sh │ └── workflows/ │ ├── deploy-btp.yml │ ├── maven.yml │ └── node.js.yml ├── .gitignore ├── .vscode/ │ └── launch.json ├── LICENSE ├── LICENSES/ │ └── Apache-2.0.txt ├── README-Kyma.md ├── README.md ├── REUSE.toml ├── _i18n/ │ ├── i18n.properties │ ├── i18n_de.properties │ ├── i18n_en.properties │ └── i18n_fr.properties ├── app/ │ ├── .karma/ │ │ ├── karma-cap-middleware.js │ │ └── karma.conf.js │ ├── common.cds │ ├── labels.cds │ ├── services.cds │ ├── travel_analytics/ │ │ ├── annotations.cds │ │ ├── karma.conf.js │ │ ├── package.json │ │ ├── tsconfig.json │ │ ├── ui5.yaml │ │ ├── webapp/ │ │ │ ├── Component.ts │ │ │ ├── i18n/ │ │ │ │ ├── i18n.properties │ │ │ │ ├── i18n_de.properties │ │ │ │ ├── i18n_en.properties │ │ │ │ └── i18n_fr.properties │ │ │ ├── index.html │ │ │ ├── manifest.json │ │ │ └── test/ │ │ │ ├── flpSandbox.html │ │ │ ├── integration/ │ │ │ │ ├── Opa.qunit.html │ │ │ │ ├── Opa.qunit.js │ │ │ │ ├── OpaJourney.js │ │ │ │ └── pages/ │ │ │ │ ├── BookingsList.js │ │ │ │ └── BookingsObjectPage.js │ │ │ ├── testsuite.qunit.html │ │ │ └── testsuite.qunit.js │ │ ├── xs-app.json │ │ └── xs-security.json │ ├── travel_processor/ │ │ ├── capabilities.cds │ │ ├── field-control.cds │ │ ├── karma.conf.js │ │ ├── layouts.cds │ │ ├── package.json │ │ ├── tsconfig.json │ │ ├── ui5.yaml │ │ ├── webapp/ │ │ │ ├── Component.ts │ │ │ ├── changes/ │ │ │ │ ├── changes-bundle.json │ │ │ │ └── flexibility-bundle.json │ │ │ ├── ext/ │ │ │ │ ├── controller/ │ │ │ │ │ ├── ControllerExtension.d.ts │ │ │ │ │ └── ObjectPageExtension.controller.ts │ │ │ │ └── fragment/ │ │ │ │ ├── CustomSection.fragment.xml │ │ │ │ ├── CustomSection.ts │ │ │ │ └── Trees4Tickets.fragment.xml │ │ │ ├── i18n/ │ │ │ │ ├── i18n.properties │ │ │ │ ├── i18n_de.properties │ │ │ │ ├── i18n_en.properties │ │ │ │ └── i18n_fr.properties │ │ │ ├── index.html │ │ │ ├── manifest.json │ │ │ └── test/ │ │ │ ├── integration/ │ │ │ │ ├── Opa.qunit.html │ │ │ │ ├── Opa.qunit.js │ │ │ │ ├── OpaJourney.js │ │ │ │ └── pages/ │ │ │ │ ├── ItemObjectPage.js │ │ │ │ ├── MainListReport.js │ │ │ │ └── MainObjectPage.js │ │ │ ├── testsuite.qunit.html │ │ │ └── testsuite.qunit.js │ │ ├── xs-app.json │ │ └── xs-security.json │ └── value-helps.cds ├── db/ │ ├── common.cds │ ├── data/ │ │ ├── sap.common-Countries.csv │ │ ├── sap.common-Countries.texts.csv │ │ ├── sap.common-Currencies.csv │ │ ├── sap.common-Currencies.texts.csv │ │ ├── sap.fe.cap.travel-Airline.csv │ │ ├── sap.fe.cap.travel-Airport.csv │ │ ├── sap.fe.cap.travel-Booking.csv │ │ ├── sap.fe.cap.travel-BookingStatus.csv │ │ ├── sap.fe.cap.travel-BookingStatus.texts.csv │ │ ├── sap.fe.cap.travel-BookingSupplement.csv │ │ ├── sap.fe.cap.travel-Flight.csv │ │ ├── sap.fe.cap.travel-FlightConnection.csv │ │ ├── sap.fe.cap.travel-Passenger.csv │ │ ├── sap.fe.cap.travel-Supplement.csv │ │ ├── sap.fe.cap.travel-Supplement.texts.csv │ │ ├── sap.fe.cap.travel-SupplementType.csv │ │ ├── sap.fe.cap.travel-Travel.csv │ │ ├── sap.fe.cap.travel-TravelAgency.csv │ │ ├── sap.fe.cap.travel-TravelStatus.csv │ │ └── sap.fe.cap.travel-TravelStatus.texts.csv │ ├── master-data.cds │ ├── package.json │ └── schema.cds ├── eslint.config.js ├── mta-java.yaml ├── mta.yaml ├── native-build-env.json ├── package.json ├── pom.xml ├── srv/ │ ├── analytics-service.cds │ ├── pom.xml │ ├── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── com/ │ │ │ │ └── sap/ │ │ │ │ └── cap/ │ │ │ │ └── sflight/ │ │ │ │ ├── SFlightApplication.java │ │ │ │ ├── processor/ │ │ │ │ │ ├── AcceptRejectHandler.java │ │ │ │ │ ├── CreationHandler.java │ │ │ │ │ ├── DeductDiscountHandler.java │ │ │ │ │ ├── IllegalTravelDateException.java │ │ │ │ │ ├── IllegalTravelStatusException.java │ │ │ │ │ ├── RecalculatePriceHandler.java │ │ │ │ │ └── UpdateFlightSeatsHandler.java │ │ │ │ ├── security/ │ │ │ │ │ └── WebSecurityConfig.java │ │ │ │ └── ui/ │ │ │ │ └── RedirectFilter.java │ │ │ └── resources/ │ │ │ ├── META-INF/ │ │ │ │ └── native-image/ │ │ │ │ └── resource-config.json │ │ │ ├── application.yml │ │ │ └── messages.properties │ │ └── test/ │ │ ├── java/ │ │ │ └── com/ │ │ │ └── sap/ │ │ │ └── cap/ │ │ │ └── sflight/ │ │ │ ├── SFlightApplicationTest.java │ │ │ └── processor/ │ │ │ ├── TravelSmokeTest.java │ │ │ └── UpdateFlightSeatsHandlerServiceIntegrationTest.java │ │ └── resources/ │ │ └── META-INF/ │ │ └── native-image/ │ │ └── reflect-config.json │ ├── travel-service.cds │ └── travel-service.ts ├── test/ │ ├── odata.test.ts │ ├── requests.http │ └── setup.ts └── tsconfig.json
SYMBOL INDEX (88 symbols across 23 files)
FILE: .github/deployment/kyma/scripts/prepareUiFiles.js
function prepareUiFiles (line 5) | function prepareUiFiles(path, options) {
function throwError (line 63) | function throwError(msg) {
function getPackageJsonInclude (line 67) | function getPackageJsonInclude() {
function getUI5DeployTemplateYaml (line 90) | function getUI5DeployTemplateYaml() {
function getXsAppTemplateJson (line 117) | function getXsAppTemplateJson() {
function getSrvDestination (line 152) | function getSrvDestination(destinations) {
FILE: .github/deployment/kyma/scripts/value.js
function printProperty (line 15) | function printProperty(file) {
FILE: app/.karma/karma-cap-middleware.js
function spawnServer (line 4) | function spawnServer(cmd, args, cwd, fnIsReady) {
function createKarmaMiddleware (line 33) | function createKarmaMiddleware(serverUrl, auth) {
function java (line 52) | async function java() {
function node (line 66) | async function node() {
FILE: app/travel_analytics/webapp/Component.ts
class Component (line 6) | class Component extends BaseComponent {
FILE: app/travel_processor/webapp/Component.ts
class Component (line 6) | class Component extends BaseComponent {
FILE: app/travel_processor/webapp/ext/controller/ControllerExtension.d.ts
class ControllerExtension (line 5) | class ControllerExtension<API> {
FILE: app/travel_processor/webapp/ext/controller/ObjectPageExtension.controller.ts
class ObjectPageExtension (line 10) | class ObjectPageExtension extends ControllerExtension<ExtensionAPI> {
method openDialog (line 11) | async openDialog(resolve: (value: PromiseLike<null> | null) => void, r...
method onInit (line 46) | onInit(this: ObjectPageExtension) {
method onBeforeSave (line 51) | onBeforeSave(this: ObjectPageExtension) {
FILE: app/travel_processor/webapp/ext/fragment/CustomSection.ts
function onPress (line 11) | function onPress(this: ExtensionAPI, event: UI5Event) {
FILE: srv/src/main/java/com/sap/cap/sflight/SFlightApplication.java
class SFlightApplication (line 6) | @SpringBootApplication
method main (line 9) | public static void main(String[] args) {
FILE: srv/src/main/java/com/sap/cap/sflight/processor/AcceptRejectHandler.java
class AcceptRejectHandler (line 29) | @Component
method AcceptRejectHandler (line 40) | public AcceptRejectHandler(DraftService draftService, PersistenceServi...
method beforeAcceptTravel (line 45) | @Before(entity = Travel_.CDS_NAME)
method beforeRejectTravel (line 50) | @Before(entity = Travel_.CDS_NAME)
method beforeAcceptOrRejectTravel (line 55) | private void beforeAcceptOrRejectTravel(CqnSelect select, UserInfo use...
method onRejectTravel (line 72) | @On(entity = Travel_.CDS_NAME)
method onAcceptTravel (line 81) | @On(entity = Travel_.CDS_NAME)
method updateStatusForTravelId (line 90) | private void updateStatusForTravelId(String travelUUID, String newStat...
method checkIfTravelHasExceptedStatus (line 103) | private void checkIfTravelHasExceptedStatus(Travel travel) {
method checkIfTravelIsLockedByAnotherUser (line 111) | private void checkIfTravelIsLockedByAnotherUser(Travel travel, UserInf...
FILE: srv/src/main/java/com/sap/cap/sflight/processor/CreationHandler.java
class CreationHandler (line 31) | @Component
method CreationHandler (line 39) | public CreationHandler(PersistenceService persistenceService, DraftSer...
method setBookingDateIfNotProvided (line 44) | @Before(event = { CqnService.EVENT_CREATE, CqnService.EVENT_UPDATE, Dr...
method saveComputedValues (line 55) | @Before(event = DraftService.EVENT_DRAFT_SAVE, entity = Travel_.CDS_NAME)
method checkTravelEndDateIsAfterBeginDate (line 75) | @Before(event = { CqnService.EVENT_CREATE, CqnService.EVENT_UPDATE }, ...
method calculateTravelIdBeforeCreation (line 90) | @Before(event = CqnService.EVENT_CREATE, entity = Travel_.CDS_NAME)
method fillBookingIdsBeforeCreationAndUpdate (line 100) | @Before(event = { CqnService.EVENT_CREATE, CqnService.EVENT_UPDATE, },...
method addBookingSupplementIds (line 108) | private void addBookingSupplementIds(Travel travel) {
method addBookingIds (line 131) | private void addBookingIds(Travel travel) {
method initialTravelId (line 145) | @Before(event = DraftService.EVENT_DRAFT_NEW, entity = Travel_.CDS_NAME)
method initialBookingSupplementId (line 150) | @Before(event = DraftService.EVENT_DRAFT_NEW, entity = BookingSuppleme...
FILE: srv/src/main/java/com/sap/cap/sflight/processor/DeductDiscountHandler.java
class DeductDiscountHandler (line 24) | @Component
method DeductDiscountHandler (line 31) | public DeductDiscountHandler(DraftService draftService, PersistenceSer...
method deductDiscount (line 36) | @On(entity = Travel_.CDS_NAME)
FILE: srv/src/main/java/com/sap/cap/sflight/processor/IllegalTravelDateException.java
class IllegalTravelDateException (line 6) | public class IllegalTravelDateException extends ServiceException {
method IllegalTravelDateException (line 7) | public IllegalTravelDateException(String message, Object... parameters) {
FILE: srv/src/main/java/com/sap/cap/sflight/processor/IllegalTravelStatusException.java
class IllegalTravelStatusException (line 6) | public class IllegalTravelStatusException extends ServiceException {
method IllegalTravelStatusException (line 7) | public IllegalTravelStatusException(String message, Object... paramete...
FILE: srv/src/main/java/com/sap/cap/sflight/processor/RecalculatePriceHandler.java
class RecalculatePriceHandler (line 40) | @Component
method RecalculatePriceHandler (line 47) | public RecalculatePriceHandler(DraftService draftService, PersistenceS...
method disableUpdateAndCreateForBookingAndBookingSupplement (line 52) | @Before(event = {CqnService.EVENT_CREATE, CqnService.EVENT_UPDATE}, en...
method calculateTotalPriceForTravel (line 57) | private static BigDecimal calculateTotalPriceForTravel(CqnService db, ...
method calculateNewTotalPriceForActiveTravel (line 97) | @After(event = {CqnService.EVENT_UPDATE, CqnService.EVENT_CREATE}, ent...
method recalculateTravelPriceIfTravelWasUpdated (line 121) | @After(event = { DraftService.EVENT_DRAFT_PATCH }, entity = Travel_.CD...
method recalculateTravelPriceIfFlightPriceWasUpdated (line 129) | @After(event = { DraftService.EVENT_DRAFT_PATCH, DraftService.EVENT_DR...
method recalculateTravelPriceIfPriceWasUpdated (line 138) | @After(event = { DraftService.EVENT_DRAFT_NEW, DraftService.EVENT_DRAF...
method calculateAndPatchNewTotalPriceForDraft (line 149) | private BigDecimal calculateAndPatchNewTotalPriceForDraft(final String...
FILE: srv/src/main/java/com/sap/cap/sflight/processor/UpdateFlightSeatsHandler.java
class UpdateFlightSeatsHandler (line 41) | @Component
method UpdateFlightSeatsHandler (line 51) | public UpdateFlightSeatsHandler(PersistenceService persistenceService) {
type Status (line 55) | enum Status {
method updateSeatsDiffProc (line 60) | @Before(event = { "CREATE", "UPDATE", "DELETE" }, entity = Travel_.CDS...
method handleUpdatedTravelWithDiffProcessor (line 96) | private void handleUpdatedTravelWithDiffProcessor(EventContext context...
method getOldStateTravel (line 126) | private Travel getOldStateTravel(String travelUUID) {
method getTravelUuidFromDeleteCqn (line 135) | private String getTravelUuidFromDeleteCqn(EventContext context) {
method updateSeatsOnFlights (line 141) | void updateSeatsOnFlights(Map<Status, List<Flight>> flightsStatus) {
method getFlights (line 166) | Map<Status, List<Flight>> getFlights(Map<Status, List<Booking>> bookin...
class BookingDiffVisitor (line 200) | private static class BookingDiffVisitor implements DiffVisitor {
method BookingDiffVisitor (line 205) | public BookingDiffVisitor(Map<Status, List<Booking>> modifications, ...
method changed (line 211) | @Override
method added (line 228) | @Override
method removed (line 241) | @Override
FILE: srv/src/main/java/com/sap/cap/sflight/security/WebSecurityConfig.java
class WebSecurityConfig (line 12) | @Configuration
method configure (line 17) | @Bean
FILE: srv/src/main/java/com/sap/cap/sflight/ui/RedirectFilter.java
class RedirectFilter (line 20) | @Component
method doFilter (line 24) | @Override
FILE: srv/src/test/java/com/sap/cap/sflight/SFlightApplicationTest.java
class SFlightApplicationTest (line 6) | @SpringBootTest
method contextLoads (line 9) | @Test
FILE: srv/src/test/java/com/sap/cap/sflight/processor/TravelSmokeTest.java
class TravelSmokeTest (line 16) | @SpringBootTest
method testReadTravels (line 23) | @Test
FILE: srv/src/test/java/com/sap/cap/sflight/processor/UpdateFlightSeatsHandlerServiceIntegrationTest.java
class UpdateFlightSeatsHandlerServiceIntegrationTest (line 30) | @SpringBootTest
method testUpdateFlightHandlerForAddedBooking (line 42) | @Test
method testUpdateFlightHandlerForDeletedBooking (line 81) | @Test
method testUpdateFlightHandlerForUpdatedBooking (line 116) | @Test
method addRequiredDataToTravel (line 167) | private static void addRequiredDataToTravel(Travel travel) {
FILE: srv/travel-service.ts
class TravelService (line 6) | class TravelService extends cds.ApplicationService { init() {
method init (line 6) | init() {
FILE: test/odata.test.ts
function expect_totals (line 166) | async function expect_totals (expected, _active = 'IsActiveEntity=false') {
Condensed preview — 148 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (4,736K chars).
[
{
"path": ".cdsrc.json",
"chars": 3,
"preview": "{}\n"
},
{
"path": ".devcontainer/cds-dk/devcontainer-feature.json",
"chars": 465,
"preview": "{\n \"id\": \"cds-dk\",\n \"version\": \"1.0.0\",\n \"name\": \"cds-dk\",\n \"description\": \"Install @sap/cds-dk (via npm)\",\n"
},
{
"path": ".devcontainer/cds-dk/install.sh",
"chars": 132,
"preview": "#!/bin/sh\nset -e\n\nexport VERSION_SPECIFIER=\"${VERSION:+@${VERSION}}\"\n\n. ${NVM_DIR}/nvm.sh\n\nnpm i -g @sap/cds-dk${VERSION"
},
{
"path": ".devcontainer/cf-deploy/devcontainer-feature.json",
"chars": 223,
"preview": "{\n \"id\": \"cf-deploy\",\n \"version\": \"1.0.1\",\n \"name\": \"cf-deploy\",\n \"description\": \"Install tooling for deploy"
},
{
"path": ".devcontainer/cf-deploy/install.sh",
"chars": 523,
"preview": "#!/bin/sh\nset -e\n\n# Homebrew\nif ! type brew >/dev/null 2>&1; then\n NONINTERACTIVE=1 su vscode -s /bin/bash -c \"$(curl"
},
{
"path": ".devcontainer/devcontainer.json",
"chars": 653,
"preview": "// For format details, see https://aka.ms/devcontainer.json\n{\n\t\"name\": \"CAP Node.js & TypeScript & Java\",\n\t\"image\": \"mcr"
},
{
"path": ".github/actions/btp/Dockerfile",
"chars": 394,
"preview": "FROM buildpack-deps:stretch-curl\n\n# https://blogs.sap.com/2021/09/01/sap-tech-bytes-btp-cli-installation/\nRUN curl -s -O"
},
{
"path": ".github/actions/btp/action.yml",
"chars": 543,
"preview": "name: 'BTP'\ndescription: Performs BTP operations\ninputs:\n cli_url:\n description: The API endpoint for the CLI\n re"
},
{
"path": ".github/actions/btp/entrypoint.sh",
"chars": 779,
"preview": "#!/bin/bash\nset -e\n\nINPUT_USERNAME=${INPUT_USERNAME:-$CF_USERNAME}\nINPUT_PASSWORD=${INPUT_PASSWORD:-$CF_PASSWORD}\nbtp lo"
},
{
"path": ".github/actions/cf-deploy/Dockerfile",
"chars": 182,
"preview": "FROM ppiper/cf-cli:v11\n\n# needed for cf to find its config\n# (GH resets HOME to /github/workspace)\nENV CF_HOME=$HOME\n\nCO"
},
{
"path": ".github/actions/cf-deploy/action.yml",
"chars": 1153,
"preview": "name: 'CF Deploy MTA'\ndescription: Deploys an MTA file to Cloud Foundry\ninputs:\n mtafile:\n description: Path to your"
},
{
"path": ".github/actions/cf-deploy/entrypoint.sh",
"chars": 668,
"preview": "#!/bin/bash\nset -e\n\ncf_opts=\ncf api ${INPUT_API:-$CF_API} ${cf_opts}\n\nINPUT_USERNAME=${INPUT_USERNAME:-$CF_USERNAME}\nINP"
},
{
"path": ".github/dependabot.yml",
"chars": 494,
"preview": "version: 2\n\nupdates:\n\n- package-ecosystem: npm\n directory: /\n versioning-strategy: increase-if-necessary\n schedule:\n "
},
{
"path": ".github/deployment/kyma/scripts/build-ui-image.sh",
"chars": 1537,
"preview": "#!/bin/bash\n\nset -e\ncd \"$(dirname \"$(npm root)\")\"\nDIR=\"$(pwd)/.github\"\n\nnpm install --no-save yaml\n\nfunction value() {\n "
},
{
"path": ".github/deployment/kyma/scripts/create-container-registry-secret.sh",
"chars": 593,
"preview": "#!/bin/bash\n\nread -p \"Docker Server: \" DOCKER_SERVER\n\nread -p \"User ($USER): \" DOCKER_USER\nif [ \"$DOCKER_USER\" == \"\" ]; "
},
{
"path": ".github/deployment/kyma/scripts/create-db-secret.sh",
"chars": 2286,
"preview": "#!/bin/bash\n\nset -e\ncd \"$(dirname \"$(dirname \"$0\")\")\"\n. ./scripts/values.sh\n\nif true-value 2>/dev/null .saas_registry.en"
},
{
"path": ".github/deployment/kyma/scripts/format-kyma-secret.js",
"chars": 373,
"preview": "const key=JSON.parse(process.argv[4].replace(/^.*/, \"\"));\nconst credentials=key.credentials /* new cfcli? */ || key;\ncon"
},
{
"path": ".github/deployment/kyma/scripts/prepareUiFiles.js",
"chars": 4993,
"preview": "\nconst Path = require('path');\nconst posixJoin = Path.posix.join;\n\nfunction prepareUiFiles(path, options) {\n const { "
},
{
"path": ".github/deployment/kyma/scripts/value.js",
"chars": 912,
"preview": "#!/usr/bin/env node\n/*eslint no-unused-vars: [\"error\", { \"caughtErrors\": \"none\" }]*/\n\nconst yaml = require('yaml');\ncons"
},
{
"path": ".github/deployment/kyma/scripts/values.sh",
"chars": 936,
"preview": "\nfunction read-value() {\n if [ -f .values.yaml ]; then\n local VALUE=\"$(yaml2json <.values.yaml | jq -er \"$1\" |"
},
{
"path": ".github/workflows/deploy-btp.yml",
"chars": 3017,
"preview": "on:\n workflow_call:\n inputs:\n btp-subdomain:\n type: string\n required: false\n default: 284d"
},
{
"path": ".github/workflows/maven.yml",
"chars": 1006,
"preview": "# This workflow will build a Java project with Maven\n# For more information see: https://help.github.com/actions/languag"
},
{
"path": ".github/workflows/node.js.yml",
"chars": 2101,
"preview": "# This workflow will do a clean install of node dependencies, build the source code and run tests across different versi"
},
{
"path": ".gitignore",
"chars": 611,
"preview": "# CAP sflight\n_out\n*.db\nconnection.properties\ndefault-*.json\n*.sql\ngen/\nnode_modules/\ntarget/\n.reloadtrigger\n@cds-models"
},
{
"path": ".vscode/launch.json",
"chars": 804,
"preview": "{\n // Use IntelliSense to learn about possible attributes.\n // Hover to view descriptions of existing attributes.\n //"
},
{
"path": "LICENSE",
"chars": 10142,
"preview": "\n Apache License\n Version 2.0, January 2004\n "
},
{
"path": "LICENSES/Apache-2.0.txt",
"chars": 10280,
"preview": "Apache License\nVersion 2.0, January 2004\nhttp://www.apache.org/licenses/\n\nTERMS AND CONDITIONS FOR USE, REPRODUCTION, AN"
},
{
"path": "README-Kyma.md",
"chars": 6934,
"preview": "# Deployment to SAP Business Technology Platform - Kyma Runtime\n\n- [Deployment to SAP Business Technology Platform - Kym"
},
{
"path": "README.md",
"chars": 7642,
"preview": "\n\n# Welcome to the CAP SFLIGHT App\n\nThis is a sample app for the travel reference scenario, built with the [SAP Cloud Ap"
},
{
"path": "REUSE.toml",
"chars": 1947,
"preview": "version = 1\nSPDX-PackageName = \"cap-sflight\"\nSPDX-PackageSupplier = \"<Steffen Weinstock (steffen.weinstock@sap.com)>\"\nSP"
},
{
"path": "_i18n/i18n.properties",
"chars": 1857,
"preview": "# This is the resource bundle for travel_processor\n\nTravel=Travel\nTravels=Travels\nTravelUUID=Travel UUID\nTravelID=Travel"
},
{
"path": "_i18n/i18n_de.properties",
"chars": 1755,
"preview": "# This is the resource bundle for travel_processor\n\nTravel=Reise\nTravels=Reisen\nTravelUUID=Reise-UUID\nTravelID=Reise\nAge"
},
{
"path": "_i18n/i18n_en.properties",
"chars": 1751,
"preview": "# This is the resource bundle for travel_processor\n\nTravel=Travel\nTravels=Travels\nTravelUUID=Travel UUID\nTravelID=Travel"
},
{
"path": "_i18n/i18n_fr.properties",
"chars": 1859,
"preview": "# This is the resource bundle for travel_processor\n\nTravel=Voyage\nTravels=Voyages\nTravelUUID=UUID voyage\nTravelID=Voyage"
},
{
"path": "app/.karma/karma-cap-middleware.js",
"chars": 2166,
"preview": "const spawn = require(\"cross-spawn\"),\n HttpProxy = require(\"http-proxy\");\n\nfunction spawnServer(cmd, args, cwd, fnIsRea"
},
{
"path": "app/.karma/karma.conf.js",
"chars": 1138,
"preview": "const puppeteer = require(\"puppeteer\"),\n cap = require(\"./karma-cap-middleware\");\n\nprocess.env.CHROME_BIN = puppeteer.e"
},
{
"path": "app/common.cds",
"chars": 225,
"preview": "using { Currency } from '../db/common';\n\n\n// Workarounds for overly strict OData libs and clients\nannotate cds.UUID with"
},
{
"path": "app/labels.cds",
"chars": 4453,
"preview": "using { sap.fe.cap.travel as schema } from '../db/schema';\n\n//\n// annotations that control rendering of fields and label"
},
{
"path": "app/services.cds",
"chars": 180,
"preview": "using from './travel_processor/capabilities';\nusing from './travel_processor/field-control';\nusing from './travel_proces"
},
{
"path": "app/travel_analytics/annotations.cds",
"chars": 10761,
"preview": "using AnalyticsService as service from '../../srv/analytics-service';\n\nannotate service.Bookings with @(\n Aggregation.C"
},
{
"path": "app/travel_analytics/karma.conf.js",
"chars": 51,
"preview": "module.exports = require(\"../.karma/karma.conf.js\")"
},
{
"path": "app/travel_analytics/package.json",
"chars": 1231,
"preview": "{\n \"name\": \"travel-analytics\",\n \"version\": \"1.0.0\",\n \"private\": true,\n \"description\": \"SFlight ALP\",\n \"main\": \"weba"
},
{
"path": "app/travel_analytics/tsconfig.json",
"chars": 708,
"preview": "{\n \"compilerOptions\": {\n \"target\": \"es2022\",\n \"module\": \"es2022\",\n \"skipLibCheck\": true,\n "
},
{
"path": "app/travel_analytics/ui5.yaml",
"chars": 1206,
"preview": "# yaml-language-server: $schema=https://sap.github.io/ui5-tooling/schema/ui5.yaml.json\nspecVersion: \"3.0\"\nmetadata:\n na"
},
{
"path": "app/travel_analytics/webapp/Component.ts",
"chars": 430,
"preview": "import BaseComponent from \"sap/fe/core/AppComponent\";\n\n/**\n * @namespace sap.fe.cap.travel_analytics\n */\nexport default "
},
{
"path": "app/travel_analytics/webapp/i18n/i18n.properties",
"chars": 186,
"preview": "# This is the resource bundle for travel_processor\n\n#Texts for manifest.json\n\n#XTIT: Application name\ntitle=Analyze Book"
},
{
"path": "app/travel_analytics/webapp/i18n/i18n_de.properties",
"chars": 196,
"preview": "# This is the resource bundle for travel_processor\n\n#Texts for manifest.json\n\n#XTIT: Application name\ntitle=Buchungen an"
},
{
"path": "app/travel_analytics/webapp/i18n/i18n_en.properties",
"chars": 186,
"preview": "# This is the resource bundle for travel_processor\n\n#Texts for manifest.json\n\n#XTIT: Application name\ntitle=Analyze Book"
},
{
"path": "app/travel_analytics/webapp/i18n/i18n_fr.properties",
"chars": 204,
"preview": "# This is the resource bundle for travel_processor\n\n#Texts for manifest.json\n\n#XTIT: Application name\ntitle=Analyser les"
},
{
"path": "app/travel_analytics/webapp/index.html",
"chars": 1239,
"preview": "<!DOCTYPE html>\n<html lang=\"en\">\n <head>\n <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\" />\n <meta charset="
},
{
"path": "app/travel_analytics/webapp/manifest.json",
"chars": 8564,
"preview": "{\n \"_version\": \"1.42.0\",\n \"sap.app\": {\n \"id\": \"sap.fe.cap.travel_analytics\",\n \"type\": \"application\","
},
{
"path": "app/travel_analytics/webapp/test/flpSandbox.html",
"chars": 2931,
"preview": "<!DOCTYPE html>\n<html lang=\"en\">\n <!-- Copyright (c) 2015 SAP AG, All Rights Reserved -->\n <head>\n <meta http-equiv"
},
{
"path": "app/travel_analytics/webapp/test/integration/Opa.qunit.html",
"chars": 1164,
"preview": "<!DOCTYPE html>\n<html lang=\"en\">\n <head>\n <title>Integration tests</title>\n <meta charset=\"UTF-8\" content=\"IE=edg"
},
{
"path": "app/travel_analytics/webapp/test/integration/Opa.qunit.js",
"chars": 739,
"preview": "sap.ui.require(\n [\n \"sap/fe/test/JourneyRunner\",\n \"sap/fe/cap/travel_analytics/test/integration/OpaJourney\",\n "
},
{
"path": "app/travel_analytics/webapp/test/integration/OpaJourney.js",
"chars": 471,
"preview": "/* global QUnit */\nsap.ui.define([\"sap/ui/test/opaQunit\"], function (opaTest) {\n \"use strict\";\n\n return {\n run: fun"
},
{
"path": "app/travel_analytics/webapp/test/integration/pages/BookingsList.js",
"chars": 341,
"preview": "sap.ui.define([\"sap/fe/test/ListReport\"], function (ListReport) {\n \"use strict\";\n\n const CustomPageDefinitions = {\n "
},
{
"path": "app/travel_analytics/webapp/test/integration/pages/BookingsObjectPage.js",
"chars": 347,
"preview": "sap.ui.define([\"sap/fe/test/ObjectPage\"], function (ObjectPage) {\n \"use strict\";\n\n const CustomPageDefinitions = {\n "
},
{
"path": "app/travel_analytics/webapp/test/testsuite.qunit.html",
"chars": 253,
"preview": "<!DOCTYPE html>\n<html lang=\"en\">\n <head>\n <title>QUnit test suite</title>\n <script src=\"../resources/sap/ui/qunit"
},
{
"path": "app/travel_analytics/webapp/test/testsuite.qunit.js",
"chars": 328,
"preview": "/* global window, parent, location */\nwindow.suite = function () {\n \"use strict\";\n\n const oSuite = new parent.jsUnitTe"
},
{
"path": "app/travel_analytics/xs-app.json",
"chars": 410,
"preview": "{\n \"welcomeFile\": \"/index.html\",\n \"authenticationMethod\": \"route\",\n \"routes\": [\n {\n \"source\": \"^/analytics/(."
},
{
"path": "app/travel_analytics/xs-security.json",
"chars": 824,
"preview": "{\n \"scopes\": [\n {\n \"name\": \"$XSAPPNAME.reviewer\",\n \"description\": \"reviewer\"\n },\n {\n \"name\": \"$"
},
{
"path": "app/travel_processor/capabilities.cds",
"chars": 339,
"preview": "using TravelService from '../../srv/travel-service';\n\nannotate TravelService.Travel with @odata.draft.enabled;\nannotate "
},
{
"path": "app/travel_processor/field-control.cds",
"chars": 3499,
"preview": "using TravelService from '../../srv/travel-service';\n\n//\n// annotations that control the behavior of fields and actions\n"
},
{
"path": "app/travel_processor/karma.conf.js",
"chars": 51,
"preview": "module.exports = require(\"../.karma/karma.conf.js\")"
},
{
"path": "app/travel_processor/layouts.cds",
"chars": 7378,
"preview": "using TravelService from '../../srv/travel-service';\n\n//\n// annotations that control the Fiori layout\n//\n\nannotate Trave"
},
{
"path": "app/travel_processor/package.json",
"chars": 1199,
"preview": "{\n \"name\": \"travel-processor\",\n \"version\": \"1.0.0\",\n \"private\": true,\n \"main\": \"webapp/index.html\",\n \"engines\": {\n "
},
{
"path": "app/travel_processor/tsconfig.json",
"chars": 708,
"preview": "{\n \"compilerOptions\": {\n \"target\": \"es2022\",\n \"module\": \"es2022\",\n \"skipLibCheck\": true,\n "
},
{
"path": "app/travel_processor/ui5.yaml",
"chars": 1196,
"preview": "# yaml-language-server: $schema=https://sap.github.io/ui5-tooling/schema/ui5.yaml.json\nspecVersion: \"3.0\"\nmetadata:\n na"
},
{
"path": "app/travel_processor/webapp/Component.ts",
"chars": 427,
"preview": "import BaseComponent from \"sap/fe/core/AppComponent\";\n\n/**\n * @namespace sap.fe.cap.travel\n */\nexport default class Comp"
},
{
"path": "app/travel_processor/webapp/changes/changes-bundle.json",
"chars": 3,
"preview": "{}\n"
},
{
"path": "app/travel_processor/webapp/changes/flexibility-bundle.json",
"chars": 42,
"preview": "{\n \"compVariants\": [],\n \"changes\": []\n}\n"
},
{
"path": "app/travel_processor/webapp/ext/controller/ControllerExtension.d.ts",
"chars": 331,
"preview": "/**\n * Helper to be able to define how to get the page specific extension API when writing a controller extension.\n */\nd"
},
{
"path": "app/travel_processor/webapp/ext/controller/ObjectPageExtension.controller.ts",
"chars": 2208,
"preview": "import ControllerExtension from \"sap/ui/core/mvc/ControllerExtension\";\nimport ExtensionAPI from \"sap/fe/templates/Object"
},
{
"path": "app/travel_processor/webapp/ext/fragment/CustomSection.fragment.xml",
"chars": 660,
"preview": "<core:FragmentDefinition xmlns:core=\"sap.ui.core\" xmlns=\"sap.m\" xmlns:l=\"sap.ui.layout\" xmlns:macros=\"sap.fe.macros\">\n\t<"
},
{
"path": "app/travel_processor/webapp/ext/fragment/CustomSection.ts",
"chars": 435,
"preview": "import ExtensionAPI from 'sap/fe/core/ExtensionAPI';\nimport UI5Event from 'sap/ui/base/Event';\nimport MessageToast from "
},
{
"path": "app/travel_processor/webapp/ext/fragment/Trees4Tickets.fragment.xml",
"chars": 2074,
"preview": "<core:FragmentDefinition\n\txmlns=\"sap.m\"\n\txmlns:macros=\"sap.fe.macros\"\n\txmlns:l=\"sap.ui.layout\"\n\txmlns:f=\"sap.ui.layout.f"
},
{
"path": "app/travel_processor/webapp/i18n/i18n.properties",
"chars": 182,
"preview": "# This is the resource bundle for travel_processor\n\n#Texts for manifest.json\n\n#XTIT: Application name\ntitle=Manage Trave"
},
{
"path": "app/travel_processor/webapp/i18n/i18n_de.properties",
"chars": 186,
"preview": "# This is the resource bundle for travel_processor\n\n#Texts for manifest.json\n\n#XTIT: Application name\ntitle=Reisen verwa"
},
{
"path": "app/travel_processor/webapp/i18n/i18n_en.properties",
"chars": 182,
"preview": "# This is the resource bundle for travel_processor\n\n#Texts for manifest.json\n\n#XTIT: Application name\ntitle=Manage Trave"
},
{
"path": "app/travel_processor/webapp/i18n/i18n_fr.properties",
"chars": 188,
"preview": "# This is the resource bundle for travel_processor\n\n#Texts for manifest.json\n\n#XTIT: Application name\ntitle=Gérer les vo"
},
{
"path": "app/travel_processor/webapp/index.html",
"chars": 1211,
"preview": "<!DOCTYPE html>\n<html lang=\"en\">\n <head>\n <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\" />\n <meta charset="
},
{
"path": "app/travel_processor/webapp/manifest.json",
"chars": 8452,
"preview": "{\n \"_version\": \"1.32.0\",\n \"sap.app\": {\n \"id\": \"sap.fe.cap.travel\",\n \"type\": \"application\",\n \""
},
{
"path": "app/travel_processor/webapp/test/integration/Opa.qunit.html",
"chars": 1140,
"preview": "<!DOCTYPE html>\n<html lang=\"en\">\n <head>\n <title>Integration tests</title>\n <meta charset=\"UTF-8\" content=\"IE=edg"
},
{
"path": "app/travel_processor/webapp/test/integration/Opa.qunit.js",
"chars": 851,
"preview": "sap.ui.require(\n [\n \"sap/fe/test/JourneyRunner\",\n \"sap/fe/cap/travel/test/integration/pages/MainListReport\",\n "
},
{
"path": "app/travel_processor/webapp/test/integration/OpaJourney.js",
"chars": 13199,
"preview": "/* global QUnit */\nsap.ui.define([\"sap/ui/test/opaQunit\"], function (opaTest) {\n \"use strict\";\n\n const Journey = {\n "
},
{
"path": "app/travel_processor/webapp/test/integration/pages/ItemObjectPage.js",
"chars": 379,
"preview": "sap.ui.define([\"sap/fe/test/ObjectPage\"], function (ObjectPage) {\n \"use strict\";\n\n // OPTIONAL\n const AdditionalCusto"
},
{
"path": "app/travel_processor/webapp/test/integration/pages/MainListReport.js",
"chars": 371,
"preview": "sap.ui.define([\"sap/fe/test/ListReport\"], function (ListReport) {\n \"use strict\";\n\n // OPTIONAL\n const AdditionalCusto"
},
{
"path": "app/travel_processor/webapp/test/integration/pages/MainObjectPage.js",
"chars": 377,
"preview": "sap.ui.define([\"sap/fe/test/ObjectPage\"], function (ObjectPage) {\n \"use strict\";\n\n // OPTIONAL\n const AdditionalCusto"
},
{
"path": "app/travel_processor/webapp/test/testsuite.qunit.html",
"chars": 246,
"preview": "<!DOCTYPE html>\n<html lang=\"en\">\n <head>\n <title>Testsuite</title>\n <script src=\"/resources/sap/ui/qunit/qunit-re"
},
{
"path": "app/travel_processor/webapp/test/testsuite.qunit.js",
"chars": 324,
"preview": "/* global window, parent, location */\nwindow.suite = function () {\n \"use strict\";\n\n const oSuite = new parent.jsUnitTe"
},
{
"path": "app/travel_processor/xs-app.json",
"chars": 410,
"preview": "{\n \"welcomeFile\": \"/index.html\",\n \"authenticationMethod\": \"route\",\n \"routes\": [\n {\n \"source\": \"^/processor/(."
},
{
"path": "app/travel_processor/xs-security.json",
"chars": 824,
"preview": "{\n \"scopes\": [\n {\n \"name\": \"$XSAPPNAME.reviewer\",\n \"description\": \"reviewer\"\n },\n {\n \"name\": \"$"
},
{
"path": "app/value-helps.cds",
"chars": 12468,
"preview": "using { sap.fe.cap.travel as schema } from '../db/schema';\n\n//\n// annotations for value helps\n//\n\nannotate schema.Travel"
},
{
"path": "db/common.cds",
"chars": 795,
"preview": "using { sap, managed } from '@sap/cds/common';\n\nextend sap.common.Currencies with {\n // Currencies.code = ISO 4217 alph"
},
{
"path": "db/data/sap.common-Countries.csv",
"chars": 781,
"preview": "code;name;descr\nAU;Australia;Commonwealth of Australia\nCA;Canada;Canada\nCN;China;People's Republic of China (PRC)\nFR;Fra"
},
{
"path": "db/data/sap.common-Countries.texts.csv",
"chars": 2594,
"preview": "code;locale;name;descr\nAU;de;Australien;Commonwealth Australien\nCA;de;Kanada;Canada\nCN;de;China;Volksrepublik China\nFR;d"
},
{
"path": "db/data/sap.common-Currencies.csv",
"chars": 609,
"preview": "code;symbol;name;descr;numcode;minor;exponent\nEUR;€;Euro;European Euro;978;Cent;2\nUSD;$;US Dollar;United States Dollar;8"
},
{
"path": "db/data/sap.common-Currencies.texts.csv",
"chars": 1258,
"preview": "code;locale;name;descr\nEUR;de;Euro;European Euro\nUSD;de;US-Dollar;United States Dollar\nCAD;de;Kanadischer Dollar;Kanadis"
},
{
"path": "db/data/sap.fe.cap.travel-Airline.csv",
"chars": 737,
"preview": "AirlineID;Name;CurrencyCode_code;AirlinePicURL\nGA;Green Albatros;CAD;https://raw.githubusercontent.com/SAP-samples/fiori"
},
{
"path": "db/data/sap.fe.cap.travel-Airport.csv",
"chars": 2263,
"preview": "AirportID;Name;City;CountryCode_code\nFRA;Frankfurt Airport;Frankfurt/Main;DE\nHAM;Hamburg Airport;Hamburg;DE\nMUC;Munich A"
},
{
"path": "db/data/sap.fe.cap.travel-Booking.csv",
"chars": 1308261,
"preview": "BookingUUID;to_Travel_TravelUUID;BookingID;BookingDate;to_Customer_CustomerID;to_Carrier_AirlineID;ConnectionID;FlightDa"
},
{
"path": "db/data/sap.fe.cap.travel-BookingStatus.csv",
"chars": 35,
"preview": "code;name\nN;New\nX;Canceled\nB;Booked"
},
{
"path": "db/data/sap.fe.cap.travel-BookingStatus.texts.csv",
"chars": 173,
"preview": "code;locale;name\nN;de;Neu\nB;de;Bestätigt\nX;de;Storniert\nN;fr;Nouveau\nB;fr;Réservé\nX;fr;Annulé\nN;it;Nuova\nB;it;Prenotato\n"
},
{
"path": "db/data/sap.fe.cap.travel-BookingSupplement.csv",
"chars": 2274389,
"preview": "BookSupplUUID;to_Travel_TravelUUID;to_Booking_BookingUUID;BookingSupplementID;to_Supplement_SupplementID;Price;CurrencyC"
},
{
"path": "db/data/sap.fe.cap.travel-Flight.csv",
"chars": 2036,
"preview": "AirlineID;ConnectionID;FlightDate;Price;CurrencyCode_code;PlaneType;MaximumSeats;OccupiedSeats\nSW;0001;2025-12-10;10818."
},
{
"path": "db/data/sap.fe.cap.travel-FlightConnection.csv",
"chars": 966,
"preview": "AirlineID;ConnectionID;DepartureAirport_AirportID;DestinationAirport_AirportID;DepartureTime;ArrivalTime;Distance;Distan"
},
{
"path": "db/data/sap.fe.cap.travel-Passenger.csv",
"chars": 80371,
"preview": "CustomerID;FirstName;LastName;Title;Street;PostalCode;City;CountryCode_code;PhoneNumber;EMailAddress\n000001;Theresia;Buc"
},
{
"path": "db/data/sap.fe.cap.travel-Supplement.csv",
"chars": 1956,
"preview": "SupplementID;Price;Type_code;Description;CurrencyCode_code\nBV-0001;2.30;BV;Hot Chocolate;EUR\nBV-0002;7.50;BV;Alcohol fre"
},
{
"path": "db/data/sap.fe.cap.travel-Supplement.texts.csv",
"chars": 5018,
"preview": "SupplementID;locale;Description\nBV-0001;de;Heiße Schokolade\nBV-0002;de;Alkoholfreier Champagner\nBV-0003;de;Cola\nBV-0004;"
},
{
"path": "db/data/sap.fe.cap.travel-SupplementType.csv",
"chars": 49,
"preview": "code;name\nBV;Beverage\nML;Meal\nLU;Luggage\nEX;Extra"
},
{
"path": "db/data/sap.fe.cap.travel-Travel.csv",
"chars": 722959,
"preview": "TravelUUID;TravelID;to_Agency_AgencyID;to_Customer_CustomerID;BeginDate;EndDate;BookingFee;TotalPrice;CurrencyCode_code;"
},
{
"path": "db/data/sap.fe.cap.travel-TravelAgency.csv",
"chars": 5901,
"preview": "AgencyID;Name;Street;PostalCode;City;CountryCode_code;PhoneNumber;EMailAddress;WebAddress\n070001;Sunshine Travel;134 Wes"
},
{
"path": "db/data/sap.fe.cap.travel-TravelStatus.csv",
"chars": 39,
"preview": "code;name\nO;Open\nA;Accepted\nX;Canceled\n"
},
{
"path": "db/data/sap.fe.cap.travel-TravelStatus.texts.csv",
"chars": 175,
"preview": "code;locale;name\nO;en;Open\nA;en;Accepted\nX;en;Canceled\nO;de;Offen\nA;de;Genehmigt\nX;de;Abgelehnt\nO;fr;Ouverte\nA;fr;Accept"
},
{
"path": "db/master-data.cds",
"chars": 3106,
"preview": "using { Currency, Country, custom.managed, sap } from './common';\nnamespace sap.fe.cap.travel;\n\n// ensure all masterdata"
},
{
"path": "db/package.json",
"chars": 154,
"preview": "{\n \"name\": \"deploy\",\n \"dependencies\": {\n \"@sap/hdi-deploy\": \"^4\"\n },\n \"scripts\": {\n \"start\": \"node node_module"
},
{
"path": "db/schema.cds",
"chars": 2911,
"preview": "using { Currency, custom.managed, sap.common.CodeList } from './common';\nusing {\n sap.fe.cap.travel.Airline,\n sap.fe.c"
},
{
"path": "eslint.config.js",
"chars": 943,
"preview": "'use strict'\n\nconst eslint_js = require('@eslint/js')\nconst tseslint = require('typescript-eslint');\nconst globals = req"
},
{
"path": "mta-java.yaml",
"chars": 6690,
"preview": "_schema-version: \"3.2\"\nID: capire.sflight\nversion: 1.0.0\ndescription: \"CAP sflight demo\"\n\nparameters:\n enable-parallel-"
},
{
"path": "mta.yaml",
"chars": 6392,
"preview": "_schema-version: \"3.2\"\nID: capire.sflight\nversion: 1.0.0\ndescription: \"CAP sflight demo\"\n\nparameters:\n enable-parallel-"
},
{
"path": "native-build-env.json",
"chars": 89,
"preview": "{\n \"hana\": [ { \"name\": \"sflight-db\" } ],\n \"xsuaa\": [ { \"name\": \"sflight-uaa\" } ]\n}\n"
},
{
"path": "package.json",
"chars": 3276,
"preview": "{\n \"name\": \"@capire/sflight\",\n \"version\": \"1.0.0\",\n \"private\": true,\n \"description\": \"CAP flight demo scenario\",\n \""
},
{
"path": "pom.xml",
"chars": 3923,
"preview": "<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLoc"
},
{
"path": "srv/analytics-service.cds",
"chars": 2215,
"preview": "using { sap.fe.cap.travel as my } from '../db/schema';\n\nservice AnalyticsService @(path:'/analytics') {\n\n // @(restrict"
},
{
"path": "srv/pom.xml",
"chars": 6091,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"htt"
},
{
"path": "srv/src/main/java/com/sap/cap/sflight/SFlightApplication.java",
"chars": 325,
"preview": "package com.sap.cap.sflight;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autocon"
},
{
"path": "srv/src/main/java/com/sap/cap/sflight/processor/AcceptRejectHandler.java",
"chars": 4818,
"preview": "package com.sap.cap.sflight.processor;\n\nimport static cds.gen.travelservice.TravelService_.TRAVEL;\n\nimport java.util.Opt"
},
{
"path": "srv/src/main/java/com/sap/cap/sflight/processor/CreationHandler.java",
"chars": 6137,
"preview": "package com.sap.cap.sflight.processor;\n\n\nimport java.time.LocalDate;\nimport java.util.Comparator;\nimport java.util.HashM"
},
{
"path": "srv/src/main/java/com/sap/cap/sflight/processor/DeductDiscountHandler.java",
"chars": 2534,
"preview": "package com.sap.cap.sflight.processor;\n\nimport static cds.gen.travelservice.TravelService_.TRAVEL;\nimport static java.la"
},
{
"path": "srv/src/main/java/com/sap/cap/sflight/processor/IllegalTravelDateException.java",
"chars": 346,
"preview": "package com.sap.cap.sflight.processor;\n\nimport com.sap.cds.services.ErrorStatuses;\nimport com.sap.cds.services.ServiceEx"
},
{
"path": "srv/src/main/java/com/sap/cap/sflight/processor/IllegalTravelStatusException.java",
"chars": 350,
"preview": "package com.sap.cap.sflight.processor;\n\nimport com.sap.cds.services.ErrorStatuses;\nimport com.sap.cds.services.ServiceEx"
},
{
"path": "srv/src/main/java/com/sap/cap/sflight/processor/RecalculatePriceHandler.java",
"chars": 6791,
"preview": "package com.sap.cap.sflight.processor;\n\nimport static cds.gen.travelservice.TravelService_.BOOKING;\nimport static cds.ge"
},
{
"path": "srv/src/main/java/com/sap/cap/sflight/processor/UpdateFlightSeatsHandler.java",
"chars": 10305,
"preview": "package com.sap.cap.sflight.processor;\n\nimport cds.gen.travelservice.Booking;\nimport cds.gen.travelservice.Booking_;\nimp"
},
{
"path": "srv/src/main/java/com/sap/cap/sflight/security/WebSecurityConfig.java",
"chars": 1034,
"preview": "package com.sap.cap.sflight.security;\n\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplicati"
},
{
"path": "srv/src/main/java/com/sap/cap/sflight/ui/RedirectFilter.java",
"chars": 1757,
"preview": "package com.sap.cap.sflight.ui;\n\nimport java.io.IOException;\n\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet"
},
{
"path": "srv/src/main/resources/META-INF/native-image/resource-config.json",
"chars": 112,
"preview": "{\n \"resources\": {\n \"includes\": [\n {\n \"pattern\": \"\\\\Qmessages.properties\\\\E\"\n }\n ]\n }\n}\n"
},
{
"path": "srv/src/main/resources/application.yml",
"chars": 588,
"preview": "management:\n endpoints:\n web:\n exposure:\n include: \"*\"\n\ncds:\n sql.hana.search:\n "
},
{
"path": "srv/src/main/resources/messages.properties",
"chars": 441,
"preview": "error.travel.status.unexpected=Travel with Id {0} does not have expected status {1} but already has status {2}.\nerror.tr"
},
{
"path": "srv/src/test/java/com/sap/cap/sflight/SFlightApplicationTest.java",
"chars": 296,
"preview": "package com.sap.cap.sflight;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\nimport org.spri"
},
{
"path": "srv/src/test/java/com/sap/cap/sflight/processor/TravelSmokeTest.java",
"chars": 1615,
"preview": "package com.sap.cap.sflight.processor;\n\nimport static org.hamcrest.Matchers.containsString;\nimport static org.hamcrest.M"
},
{
"path": "srv/src/test/java/com/sap/cap/sflight/processor/UpdateFlightSeatsHandlerServiceIntegrationTest.java",
"chars": 7434,
"preview": "package com.sap.cap.sflight.processor;\n\nimport static cds.gen.travelservice.TravelService_.FLIGHT;\nimport static cds.gen"
},
{
"path": "srv/src/test/resources/META-INF/native-image/reflect-config.json",
"chars": 88,
"preview": "[\n{\n \"name\": \"org.hamcrest.TypeSafeMatcher\",\n \"queryAllDeclaredMethods\": true\n}\n]\n"
},
{
"path": "srv/travel-service.cds",
"chars": 614,
"preview": "using { sap.fe.cap.travel as my } from '../db/schema';\n\nservice TravelService @(path:'/processor') {\n\n @(restrict: [\n "
},
{
"path": "srv/travel-service.ts",
"chars": 5960,
"preview": "import cds from '@sap/cds'\nimport { Booking, BookingSupplement as Supplements, Travel } from '#cds-models/TravelService'"
},
{
"path": "test/odata.test.ts",
"chars": 9212,
"preview": "import cds from '@sap/cds'\nimport { Travel } from '#cds-models/TravelService'\nconst { GET, POST, PATCH, axios, expect } "
},
{
"path": "test/requests.http",
"chars": 433,
"preview": "@server = http://localhost:4004\n@alice = Authorization: Basic alice\n\nGET {{server}}/processor/Travel?$filter=TravelID eq"
},
{
"path": "test/setup.ts",
"chars": 215,
"preview": "// for mocha\nprocess.env.CDS_TYPESCRIPT = \"true\"\n// disables cds-ui5-plugin as otherwise it would not let mocha tests te"
},
{
"path": "tsconfig.json",
"chars": 562,
"preview": "{\n \"compilerOptions\": {\n \"target\": \"ES2022\",\n \"module\": \"nodenext\",\n \"rootDir\": \"./\",\n \"b"
}
]
About this extraction
This page contains the full source code of the SAP-samples/cap-sflight GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 148 files (4.5 MB), approximately 1.2M tokens, and a symbol index with 88 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.