Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 82 additions & 0 deletions .github/actions/deploy/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
name: "Deploy to Cloud Foundry"
description: "Logs into Cloud Foundry and deploys the application"
inputs:
CF_API:
description: "Cloud Foundry API endpoint"
required: true
CF_USERNAME:
description: "Cloud Foundry username"
required: true
CF_PASSWORD:
description: "Cloud Foundry password"
required: true
CF_ORG:
description: "Cloud Foundry organization"
required: true
CF_SPACE:
description: "Cloud Foundry space"
required: true
CF_APP_NAME:
description: "Cloud Foundry application name"
required: true
runs:
using: "composite"
steps:
- name: Install dependencies and Cloud Foundry CLI (v8.9.0)
shell: bash
run: |
sudo apt-get update
sudo apt-get install -y libc6 wget tar
wget "https://packages.cloudfoundry.org/stable?release=linux64-binary&version=8.9.0&source=github-rel" -O cf-cli.tar.gz
tar -xvzf cf-cli.tar.gz
sudo mv cf /usr/local/bin/
sudo mv cf8 /usr/local/bin/
cf --version

- name: Authenticate with Cloud Foundry
shell: bash
run: |
echo "::debug::CF_API=${{ inputs.CF_API }}"
for i in {1..3}; do
cf login -a ${{ inputs.CF_API }} -u ${{ inputs.CF_USERNAME }} -p ${{ inputs.CF_PASSWORD }} -o ${{ inputs.CF_ORG }} -s ${{ inputs.CF_SPACE }} && break
echo "cf login failed, retrying ($i/3)..."
sleep 10
if [ "$i" -eq 3 ]; then
echo "❌ cf login failed after 3 attempts."
exit 1
fi
done

- name: Install Multi-Target Application Build Tool (MBT)
shell: bash
run: npm install -g mbt

- name: Check if mta.yaml Exists
shell: bash
working-directory: incidents-app
run: |
test -f mta.yaml && echo "✅ mta.yaml found!" || echo "⚠️ WARNING: mta.yaml NOT found!"

- name: Build MTA archive
shell: bash
working-directory: incidents-app
run: mbt build -t gen --mtar mta.tar

- name: Install Cloud Foundry MultiApps Plugin
shell: bash
run: |
cf install-plugin -f https://github.com/cloudfoundry-incubator/multiapps-cli-plugin/releases/latest/download/multiapps-plugin.linux64
cf plugins

- name: Undeploy existing apps if exists
shell: bash
run: |
for APP in incidents-testing incidents-testingMTX; do
echo "🔍 Attempting to undeploy $APP"
echo "y" | cf undeploy "$APP" --delete-services --delete-service-keys || echo "⚠️ $APP not found or undeploy failed"
done

- name: Deploy to Cloud Foundry
shell: bash
working-directory: incidents-app
run: cf deploy gen/mta.tar -f
122 changes: 122 additions & 0 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
name: Deploy Incidents App

on:
push:
branches: [main]
workflow_dispatch:
pull_request_target:
branches: [ main ]
types: [ reopened, synchronize, opened ]

jobs:
requires-approval:
runs-on: ubuntu-latest
name: "Waiting for PR approval as this workflow runs on pull_request_target"
if: github.event_name == 'pull_request_target' && github.event.pull_request.base.user.login != 'cap-js'
environment: pr-approval
steps:
- name: Approval Step
run: echo "This job has been approved!"
deploy:
name: Deploy ${{ matrix.tenant }} App
runs-on: ubuntu-latest
needs: requires-approval
if: always() && (needs.requires-approval.result == 'success' || needs.requires-approval.result == 'skipped')
strategy:
matrix:
tenant: [singletenant, multitenant]
permissions:
contents: read
issues: write
pull-requests: write
steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Clone Incidents App Repository
run: |
if [[ "${{ matrix.tenant }}" == "singletenant" ]]; then
git clone --branch attachmentsE2E --single-branch https://github.com/cap-js/incidents-app.git
else
git clone --branch attachmentsE2EMTX --single-branch https://github.com/cap-js/incidents-app.git
fi

- name: Install dependencies
working-directory: incidents-app
run: npm install
- name: Use PR branch version of @cap-js/attachments
if: github.event_name == 'pull_request'
working-directory: incidents-app
run: |
BRANCH_NAME=${{ github.head_ref }}
echo "🔄 Using @cap-js/attachments branch: $BRANCH_NAME"

# Override in app-level package.json
jq --arg url "git+https://github.com/cap-js/attachments.git#$BRANCH_NAME" \
'.dependencies["@cap-js/attachments"] = $url' package.json > tmp.json && mv tmp.json package.json

# Also override in mtx/sidecar if multitenant
if [[ "${{ matrix.tenant }}" == "multitenant" ]]; then
jq --arg url "git+https://github.com/cap-js/attachments.git#$BRANCH_NAME" \
'.dependencies["@cap-js/attachments"] = $url' mtx/sidecar/package.json > tmp.json && mv tmp.json mtx/sidecar/package.json
fi

# Install updated dependencies
npm install
if [[ "${{ matrix.tenant }}" == "multitenant" ]]; then
npm install --prefix mtx/sidecar
fi

- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: "22"

- name: Install CDS CLI
run: npm install -g @sap/cds

- name: Configure application features
working-directory: incidents-app
run: |
if [[ "${{ matrix.tenant }}" == "singletenant" ]]; then
npx cds add hana,xsuaa,workzone --for production
else
npx cds add hana,xsuaa,multitenancy --for production
fi

- name: Fix xs-app.json destination (singletenant only)
if: matrix.tenant == 'singletenant'
working-directory: incidents-app/app/incidents
run: |
jq '(.routes[] | select(.destination == "srv-api")).destination = "incidents-testing-srv-api"' xs-app.json > tmp.json && mv tmp.json xs-app.json

- name: Install additional dependencies
working-directory: incidents-app
run: |
npm install
npm install --prefix app/incidents
if [[ "${{ matrix.tenant }}" == "multitenant" ]]; then
npm install --prefix mtx/sidecar
npm install @sap/xsenv --prefix mtx/sidecar
fi

- name: Freeze npm dependencies (multitenant only)
if: matrix.tenant == 'multitenant'
working-directory: incidents-app
run: |
npm update --package-lock-only
npm update --package-lock-only --prefix mtx/sidecar

- name: Build application
working-directory: incidents-app
run: npx cds build --production

- name: Deploy to SAP BTP Cloud Foundry
uses: ./.github/actions/deploy
with:
CF_API: ${{ secrets.CF_API_AWS }}
CF_USERNAME: ${{ secrets.CF_USERNAME }}
CF_PASSWORD: ${{ secrets.CF_PASSWORD }}
CF_ORG: ${{ secrets.CF_ORG_AWS }}
CF_SPACE: ${{ secrets.CF_SPACE_AWS }}
CF_APP_NAME: ${{ matrix.tenant == 'singletenant' && 'incidents-testing' || 'incidents-testing-mtx' }}
20 changes: 10 additions & 10 deletions tests/non-draft-request.http
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@

### Get list of all incidents
# @name incidents
GET {{host}}/odata/v4/processor/Incidents
GET {{host}}/odata/v4/admin/Incidents
Authorization: {{auth}}

### Creating attachment (metadata request)
@incidentsID = {{incidents.response.body.value[2].ID}}
# @name createAttachment
POST {{host}}/odata/v4/processor/Incidents({{incidentsID}})/attachments
POST {{host}}/odata/v4/admin/Incidents({{incidentsID}})/attachments
Authorization: {{auth}}
Content-Type: application/json

Expand All @@ -21,40 +21,40 @@ Content-Type: application/json

### Put attachment content (content request)
@attachmentsID = {{createAttachment.response.body.ID}}
PUT {{host}}/odata/v4/processor/Incidents({{incidentsID}})/attachments(ID={{attachmentsID}})/content
PUT {{host}}/odata/v4/admin/Incidents({{incidentsID}})/attachments(ID={{attachmentsID}})/content
Authorization: {{auth}}
Content-Type: image/jpeg

< ./integration/content/sample-1.jpg

### Get newly created attachment metadata
GET {{host}}/odata/v4/processor/Incidents({{incidentsID}})/attachments(ID={{attachmentsID}})
GET {{host}}/odata/v4/admin/Incidents({{incidentsID}})/attachments(ID={{attachmentsID}})
Authorization: {{auth}}

### Fetching newly created attachment content
GET {{host}}/odata/v4/processor/Incidents({{incidentsID}})/attachments(ID={{attachmentsID}})/content
GET {{host}}/odata/v4/admin/Incidents({{incidentsID}})/attachments(ID={{attachmentsID}})/content
Authorization: {{auth}}

### Get list of attachments for a particular incident
# @name attachments
GET {{host}}/odata/v4/processor/Incidents(ID={{incidentsID}})/attachments
GET {{host}}/odata/v4/admin/Incidents(ID={{incidentsID}})/attachments
Authorization: {{auth}}

### Get attachments content
GET {{host}}/odata/v4/processor/Incidents({{incidentsID}})/attachments(ID={{attachmentsID}})/content
GET {{host}}/odata/v4/admin/Incidents({{incidentsID}})/attachments(ID={{attachmentsID}})/content
Authorization: {{auth}}

### Get attachments content with up__ID included
GET {{host}}/odata/v4/processor/Incidents({{incidentsID}})/attachments(up__ID={{incidentsID}},ID={{attachmentsID}})/content
GET {{host}}/odata/v4/admin/Incidents({{incidentsID}})/attachments(up__ID={{incidentsID}},ID={{attachmentsID}})/content
Authorization: {{auth}}

### Put attachment content (content request) with up__ID included
PUT {{host}}/odata/v4/processor/Incidents({{incidentsID}})/attachments(up__ID={{incidentsID}},ID={{attachmentsID}})/content
PUT {{host}}/odata/v4/admin/Incidents({{incidentsID}})/attachments(up__ID={{incidentsID}},ID={{attachmentsID}})/content
Authorization: {{auth}}
Content-Type: image/jpeg

< ./integration/content/sample-1.jpg

### Delete attachment
DELETE {{host}}/odata/v4/processor/Incidents({{incidentsID}})/attachments(ID={{attachmentsID}})
DELETE {{host}}/odata/v4/admin/Incidents({{incidentsID}})/attachments(ID={{attachmentsID}})
Authorization: {{auth}}