11 |
12 |
13 | Release Notes
14 |
15 |
16 |
17 |
18 |
19 | ### Definition of Done
20 |
21 | - [ ] Feature scope is implemented
22 | - [ ] Feature scope is tested
23 | - [ ] Feature scope is documented
24 | - [ ] Release notes are created
25 |
--------------------------------------------------------------------------------
/.github/workflows/ci-build.yml:
--------------------------------------------------------------------------------
1 | name: Java CI with Maven
2 |
3 | on:
4 | push:
5 | branches: [ main ]
6 | pull_request:
7 | branches: [ main ]
8 |
9 | env:
10 | # keep the below two variables in sync with the ones in .github/workflows/fosstars-project-report.yml
11 | CVE_CACHE_KEY: cve-db
12 | CVE_CACHE_DIR: ~/.m2/repository/org/owasp/dependency-check-data
13 |
14 | jobs:
15 | build:
16 | runs-on: ubuntu-latest
17 | strategy:
18 | matrix:
19 | java-version: [ 17, 21 ]
20 | name: Build with Java ${{ matrix.java-version }}
21 |
22 | steps:
23 | - uses: actions/checkout@v4
24 | - name: Set up JDK ${{ matrix.java-version }}
25 | uses: actions/setup-java@v4
26 | with:
27 | distribution: 'sapmachine'
28 | java-version: ${{ matrix.java-version }}
29 |
30 | - name: Restore CVE Database
31 | uses: actions/cache/restore@v4
32 | with:
33 | path: ${{ env.CVE_CACHE_DIR }}
34 | key: ${{ env.CVE_CACHE_KEY }}
35 | # fail-on-cache-miss: true
36 |
37 | - name: Build with Maven
38 | run: mvn clean install -Dgpg.skip --no-transfer-progress
39 | - name: Check for source code changes
40 | run: |
41 | if [[ `git status --porcelain` ]]; then
42 | echo -e "Following files need to be formatted: \n$(git diff --name-only)"
43 | exit 1
44 | fi
45 |
--------------------------------------------------------------------------------
/.github/workflows/codeql.yml:
--------------------------------------------------------------------------------
1 | name: "CodeQL Scan"
2 |
3 | on:
4 | push:
5 | branches: [ main ]
6 | pull_request:
7 | branches: [ main ]
8 |
9 | jobs:
10 | CodeQL-Build:
11 | runs-on: ubuntu-latest
12 |
13 | permissions:
14 | security-events: write
15 |
16 | steps:
17 | - name: Checkout repository
18 | uses: actions/checkout@v4
19 |
20 | - name: Set up Java 17
21 | uses: actions/setup-java@v4
22 | with:
23 | distribution: 'sapmachine'
24 | java-version: 17
25 |
26 | - name: Initialize CodeQL
27 | uses: github/codeql-action/init@v3
28 | with:
29 | languages: java
30 |
31 | - name: Autobuild
32 | uses: github/codeql-action/autobuild@v3
33 |
34 | - name: Perform CodeQL Analysis
35 | uses: github/codeql-action/analyze@v3
36 |
--------------------------------------------------------------------------------
/.github/workflows/dependabot-automerge.yml:
--------------------------------------------------------------------------------
1 | name: dependabot merger
2 |
3 | on:
4 | workflow_dispatch:
5 | schedule:
6 | - cron: '17 09 * * *' # trigger daily at 09:17 a.m., as dependabot will create new PRs daily at 6:00 a.m.
7 |
8 | env:
9 | DEPENDABOT_GROUPS: |
10 | production-minor-patch group
11 | plugins group
12 | test group
13 | github-actions group
14 | jobs:
15 | review-prs:
16 | runs-on: ubuntu-latest
17 | permissions:
18 | pull-requests: write
19 | contents: write
20 | steps:
21 | - name: Checkout
22 | uses: actions/checkout@v4
23 |
24 | - name: Approve and Merge PRs
25 | run: |
26 | PRS=$(gh pr list --app "dependabot" --state "open" --json number,title,autoMergeRequest,reviewDecision,mergeable,mergeStateStatus)
27 | PR_NUMBERS=
28 | while IFS= read -r GROUP; do
29 | if [[ -z "$GROUP" ]]; then
30 | continue
31 | fi
32 |
33 | MATCHES=$(jq -r --arg group "$GROUP" '.[] | select(.title | contains($group)) | .number' <<< "$PRS")
34 | echo "[DEBUG] Found PRs for group '$GROUP': '$MATCHES'"
35 |
36 | PR_NUMBERS="$MATCHES"$'\n'"$PR_NUMBERS"
37 | done <<< "${{ env.DEPENDABOT_GROUPS }}"
38 | echo "[DEBUG] Approving and Merging following PRs: '$PR_NUMBERS'"
39 |
40 | while IFS= read -r PR_NUMBER; do
41 | if [[ -z "$PR_NUMBER" ]]; then
42 | continue
43 | fi
44 |
45 | echo "[DEBUG] Approving and Merging PR #$PR_NUMBER"
46 |
47 | # check if PR is already approved
48 | REVIEW_DECISION=$(jq -r --arg pr "$PR_NUMBER" '.[] | select(.number == ($pr | tonumber)) | .reviewDecision' <<< "$PRS")
49 | if [[ "$REVIEW_DECISION" == "APPROVED" ]]; then
50 | echo "[DEBUG] PR #$PR_NUMBER is already approved, skipping"
51 | else
52 | echo "[DEBUG] PR #$PR_NUMBER is not approved yet, approving"
53 | gh pr review "$PR_NUMBER" --approve
54 | fi
55 |
56 | # check if PR is already auto-mergeable
57 | AUTO_MERGE_REQUEST=$(jq -r --arg pr "$PR_NUMBER" '.[] | select(.number == ($pr | tonumber)) | .autoMergeRequest' <<< "$PRS")
58 | if [[ -n "$AUTO_MERGE_REQUEST" ]]; then
59 | echo "[DEBUG] PR #$PR_NUMBER is already auto-mergeable, skipping"
60 | else
61 | echo "[DEBUG] PR #$PR_NUMBER is not auto-mergeable yet, enabling auto-merge"
62 | gh pr merge "$PR_NUMBER" --auto --squash
63 | fi
64 |
65 | # check if PR is behind, so we can instruct dependabot to rebase
66 | MERGE_STATE_STATUS=$(jq -r --arg pr "$PR_NUMBER" '.[] | select(.number == ($pr | tonumber)) | .mergeStateStatus' <<< "$PRS")
67 | if [[ "$MERGE_STATE_STATUS" == "BEHIND" ]]; then
68 | echo "[DEBUG] PR #$PR_NUMBER is behind, instructing dependabot to rebase"
69 | gh pr comment "$PR_NUMBER" --body "@dependabot rebase"
70 | fi
71 | done <<< "$PR_NUMBERS"
72 | env:
73 | GH_TOKEN: ${{ secrets.CLOUD_SDK_AT_SAP_ALL_ACCESS_PAT }}
74 |
--------------------------------------------------------------------------------
/.github/workflows/fosstars-project-report.yml:
--------------------------------------------------------------------------------
1 | name: "Fosstars (Security)"
2 |
3 | on:
4 | workflow_dispatch:
5 | schedule:
6 | - cron: '42 03 * * MON-FRI' # 03:42 on weekdays, a somewhat random time to avoid producing load spikes on the GH actions infrastructure
7 |
8 | env:
9 | MVN_MULTI_THREADED_ARGS: --batch-mode --no-transfer-progress --fail-at-end --show-version --threads 1C
10 | JAVA_VERSION: 17
11 | CVE_CACHE_KEY: cve-db
12 | CVE_CACHE_DIR: ~/.m2/repository/org/owasp/dependency-check-data
13 | CVE_CACHE_REF: refs/heads/main
14 |
15 | jobs:
16 | create_fosstars_report:
17 | runs-on: ubuntu-latest
18 | name: "Security rating"
19 | steps:
20 | - name: "Checkout repository"
21 | uses: actions/checkout@v4
22 |
23 | - name: "Setup java"
24 | uses: actions/setup-java@v4
25 | with:
26 | distribution: "sapmachine"
27 | java-version: ${{ env.JAVA_VERSION }}
28 | cache: 'maven'
29 |
30 | - name: "Restore CVE Database"
31 | uses: actions/cache/restore@v4
32 | with:
33 | path: ${{ env.CVE_CACHE_DIR }}
34 | key: ${{ env.CVE_CACHE_KEY }}
35 | # fail-on-cache-miss: true
36 |
37 | - name: "Build SDK"
38 | run: |
39 | MVN_ARGS="${{ env.MVN_MULTI_THREADED_ARGS }} clean install -DskipTests -DskipFormatting"
40 | mvn $MVN_ARGS
41 |
42 | - name: "CVE Scan"
43 | env:
44 | NVD_API_KEY: ${{ secrets.NVD_API_KEY }}
45 | run: |
46 | mvn -T1 --no-transfer-progress --batch-mode org.owasp:dependency-check-maven:check org.owasp:dependency-check-maven:aggregate
47 |
48 | - name: "Archive CVE Report"
49 | uses: actions/upload-artifact@v4
50 | with:
51 | name: cve-report
52 | path: target/dependency-check-report.html
53 | retention-days: 7
54 |
55 | - name: "Delete Old CVE Cache"
56 | run: |
57 | CACHE_IDS=$(gh cache list --key "${{ env.CVE_CACHE_KEY }}" --ref "${{ env.CVE_CACHE_REF }}" --json id | jq -r '.[] | .id')
58 | for CACHE_ID in $CACHE_IDS; do
59 | echo "Deleting cache with ID: $CACHE_ID"
60 | gh cache delete "${CACHE_ID}"
61 | done
62 | env:
63 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
64 |
65 | - name: "Create Updated CVE Cache"
66 | uses: actions/cache/save@v4
67 | with:
68 | path: ${{ env.CVE_CACHE_DIR }}
69 | key: ${{ env.CVE_CACHE_KEY }}
70 |
71 | # This action changes the active branch!
72 | - name: "Fosstars Rating"
73 | uses: SAP/fosstars-rating-core-action@v1.14.0
74 | with:
75 | report-branch: fosstars-report
76 | token: ${{ secrets.GITHUB_TOKEN }}
77 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: Release to Maven Central
2 |
3 | on:
4 | workflow_dispatch:
5 | inputs:
6 | releaseType:
7 | description: "The release type that should be performed."
8 | type: choice
9 | options:
10 | - major
11 | - minor
12 | - patch
13 | default: minor
14 | customVersion:
15 | description: "The custom version (Semver compatible version string (X.Y.Z)) that should be used for the release. Setting this argument would override the releaseType."
16 | type: string
17 |
18 | env:
19 | # keep the below two variables in sync with the ones in .github/workflows/update-vulnerability-database.yaml
20 | CVE_CACHE_KEY: cve-db
21 | CVE_CACHE_DIR: ~/.m2/repository/org/owasp/dependency-check-data
22 |
23 | jobs:
24 | release:
25 | runs-on: ubuntu-latest
26 | name: Perform Release
27 | steps:
28 | - uses: actions/checkout@v4
29 | name: Checkout Repository
30 | with:
31 | token: ${{ secrets.CLOUD_SDK_AT_SAP_ALL_ACCESS_PAT }}
32 | - uses: crazy-max/ghaction-import-gpg@v6
33 | with:
34 | gpg_private_key: ${{ secrets.CLOUD_SDK_AT_SAP_PRIVATE_GPG_KEY }}
35 | passphrase: ${{ secrets.CLOUD_SDK_AT_SAP_PRIVATE_GPG_PASSPHRASE }}
36 | git_user_signingkey: true
37 | git_commit_gpgsign: true
38 | - uses: actions/setup-java@v4
39 | name: Setup JDK
40 | with:
41 | distribution: 'sapmachine'
42 | java-version: 17
43 |
44 | - name: Restore CVE Database
45 | uses: actions/cache/restore@v4
46 | with:
47 | path: ${{ env.CVE_CACHE_DIR }}
48 | key: ${{ env.CVE_CACHE_KEY }}
49 | # fail-on-cache-miss: true
50 |
51 | - name: Bump Version
52 | id: bump-version
53 | working-directory: .scripts/bump-version
54 | run: |
55 | if [ -n "${{ inputs.customVersion }}" ]; then
56 | python maven_version.py bump --custom-version "${{ inputs.customVersion }}" "../../pom.xml" "../../modules-bom/pom.xml" "../../bom/pom.xml"
57 | else
58 | python maven_version.py bump --bump-type "${{ inputs.releaseType }}" "../../pom.xml" "../../modules-bom/pom.xml" "../../bom/pom.xml"
59 | fi
60 |
61 | - name: Build Project
62 | run: mvn clean install -P release -Dgpg.skip
63 |
64 | - name: Commit Changes
65 | run: |
66 | git config --global user.email "cloudsdk@sap.com"
67 | git config --global user.name "SAP Cloud SDK"
68 |
69 | git commit -S -am "bump version ${{ steps.bump-version.outputs.old_version}} -> ${{ steps.bump-version.outputs.new_version}}"
70 | git tag -s -m "Release version ${{ steps.bump-version.outputs.new_version}}" -a "${{ steps.bump-version.outputs.new_version}}"
71 | git push --follow-tags
72 |
73 | - name: Deploy Release
74 | uses: samuelmeuli/action-maven-publish@v1
75 | with:
76 | gpg_private_key: ${{ secrets.CLOUD_SDK_AT_SAP_PRIVATE_GPG_KEY }}
77 | gpg_passphrase: ${{ secrets.CLOUD_SDK_AT_SAP_PRIVATE_GPG_PASSPHRASE }}
78 | nexus_username: ${{ secrets.CLOUD_SDK_AT_SAP_NEXUS_USER }}
79 | nexus_password: ${{ secrets.CLOUD_SDK_AT_SAP_NEXUS_PASSPHRASE }}
80 | maven_profiles: "release"
81 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | **/target/
2 | **/*.iml
3 | **/*.idea
4 | **/*.vscode
5 | .DS_Store
6 |
7 | **/.venv/**
8 | **/__pycache__/**
9 | .java-version
--------------------------------------------------------------------------------
/.scripts/bump-version/__tests__/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SAP/btp-environment-variable-access/41d0e1f3bdb0307819a38131a1e79c9da45ef756/.scripts/bump-version/__tests__/__init__.py
--------------------------------------------------------------------------------
/.scripts/bump-version/__tests__/maven/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SAP/btp-environment-variable-access/41d0e1f3bdb0307819a38131a1e79c9da45ef756/.scripts/bump-version/__tests__/maven/__init__.py
--------------------------------------------------------------------------------
/.scripts/bump-version/__tests__/maven/resources/test_xml_maven_module_reader/multi_module/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 |
6 |
7 | com.example
8 | parent
9 | 1.1.1
10 |
11 |
12 | com.example
13 | application
14 | 13.3.7
15 |
16 |
17 | value
18 |
19 |
20 |
21 |
22 |
23 | com.example
24 | dpm-with-version
25 | 0.42.0
26 |
27 |
28 | com.example
29 | dpm-without-version
30 |
31 |
32 |
33 |
34 |
35 |
36 | com.example
37 | dp-with-version
38 | 0.1.0
39 |
40 |
41 | com.example
42 | dp-without-version
43 |
44 |
45 |
46 |
47 | sub-module
48 |
49 |
50 |
51 |
--------------------------------------------------------------------------------
/.scripts/bump-version/__tests__/maven/resources/test_xml_maven_module_reader/multi_module/sub-module/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 |
6 |
7 | com.example
8 | application
9 | 1.33.7
10 |
11 |
12 | com.example
13 | sub-module
14 |
15 |
16 | 42.0.0
17 |
18 |
19 |
20 |
21 |
22 | com.example.dependency
23 | dependency-with-property-version
24 | ${dependency.version}
25 |
26 |
27 |
28 |
29 |
30 |
31 | com.example.dependency
32 | dependency-with-property-version
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/.scripts/bump-version/__tests__/maven/resources/test_xml_maven_module_reader/single_module/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 |
6 |
7 | com.example
8 | parent
9 | 1.1.1
10 |
11 |
12 | com.example
13 | application
14 | 13.3.7
15 |
16 |
17 | value
18 |
19 |
20 |
21 |
22 |
23 | com.example
24 | dpm-with-version
25 | 0.42.0
26 |
27 |
28 | com.example
29 | dpm-without-version
30 |
31 |
32 |
33 |
34 |
35 |
36 | com.example
37 | dp-with-version
38 | 0.1.0
39 |
40 |
41 | com.example
42 | dp-without-version
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 | com.example
51 | plm-with-version
52 | 1.33.7
53 |
54 |
55 | com.example
56 | plm-without-version
57 |
58 |
59 |
60 |
61 |
62 | com.example
63 | plm-with-version
64 |
65 |
66 | com.example
67 | pl-with-version
68 | 7.33.1
69 |
70 |
71 | com.example
72 | pl-without-version
73 |
74 |
75 |
76 |
77 |
78 |
--------------------------------------------------------------------------------
/.scripts/bump-version/__tests__/maven/resources/test_xml_maven_project/multi_module/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 |
6 |
7 | com.example
8 | parent
9 | 1.1.1
10 |
11 |
12 | com.example
13 | application
14 | 13.3.7
15 |
16 |
17 | value
18 |
19 |
20 |
21 |
22 |
23 | com.example
24 | dpm-with-version
25 | 0.42.0
26 |
27 |
28 | com.example
29 | dpm-without-version
30 |
31 |
32 |
33 |
34 |
35 |
36 | com.example
37 | dp-with-version
38 | 0.1.0
39 |
40 |
41 | com.example
42 | dp-without-version
43 |
44 |
45 |
46 |
47 | sub-module
48 |
49 |
50 |
51 |
--------------------------------------------------------------------------------
/.scripts/bump-version/__tests__/maven/resources/test_xml_maven_project/multi_module/sub-module/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 |
6 |
7 | com.example
8 | application
9 | 13.3.7
10 |
11 |
12 | sub-module
13 |
14 |
15 | 42.0.0
16 |
17 |
18 |
19 |
20 |
21 | com.example.dependency
22 | dependency-with-property-version
23 | ${dependency.version}
24 |
25 |
26 |
27 |
28 |
29 |
30 | com.example.dependency
31 | dependency-with-property-version
32 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/.scripts/bump-version/__tests__/maven/resources/test_xml_maven_project/single_module/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 4.0.0
4 |
5 |
6 | com.example
7 | parent
8 | 1.1.1
9 |
10 |
11 | com.example
12 | application
13 | ${this.version}
14 |
15 |
16 | 13.3.7
17 | 42.9.9
18 |
19 |
20 |
21 |
22 |
23 | com.example
24 | dpm-with-version
25 | 0.42.0
26 |
27 |
28 | com.example
29 | dpm-with-property-version
30 | ${dependency.version}
31 |
32 |
33 | com.example
34 | dpm-without-version
35 |
36 |
37 |
38 |
39 |
40 |
41 | com.example
42 | dp-with-version
43 | 0.1.0
44 |
45 |
46 | com.example
47 | dp-without-version
48 |
49 |
50 |
51 |
--------------------------------------------------------------------------------
/.scripts/bump-version/__tests__/maven/test_maven_module_identifier.py:
--------------------------------------------------------------------------------
1 | from unittest import TestCase
2 |
3 | from maven.maven_module_identifier import MavenModuleIdentifier
4 |
5 |
6 | class TestMavenModuleIdentifier(TestCase):
7 | def test_implement_abstract_properties(self) -> None:
8 | class MockMavenModuleIdentifier(MavenModuleIdentifier):
9 | def __init__(self, group_id: str, artifact_id: str, version: str):
10 | self._group_id: str = group_id
11 | self._artifact_id: str = artifact_id
12 | self._version: str = version
13 |
14 | @property
15 | def group_id(self) -> str:
16 | return self._group_id
17 |
18 | @property
19 | def artifact_id(self) -> str:
20 | return self._artifact_id
21 |
22 | def _get_version(self) -> str:
23 | return self._version
24 |
25 | def _set_version(self, version: str) -> None:
26 | self._version = version
27 |
28 | sut: MavenModuleIdentifier = MockMavenModuleIdentifier(
29 | "com.example", "application", "0.1.0"
30 | )
31 |
32 | self.assertEqual(sut.group_id, "com.example")
33 | self.assertEqual(sut.artifact_id, "application")
34 | self.assertEqual(sut.version, "0.1.0")
35 |
36 | sut.version = "1.0.0"
37 |
38 | self.assertEqual(sut.group_id, "com.example")
39 | self.assertEqual(sut.artifact_id, "application")
40 | self.assertEqual(sut.version, "1.0.0")
41 |
--------------------------------------------------------------------------------
/.scripts/bump-version/__tests__/maven/test_maven_property.py:
--------------------------------------------------------------------------------
1 | from unittest import TestCase
2 |
3 | from maven.maven_property import MavenProperty
4 |
5 |
6 | class TestMavenProperty(TestCase):
7 | def test_implement_abstract_properties(self) -> None:
8 | class MockMavenProperty(MavenProperty):
9 | def __init__(self, name: str, value: str):
10 | self._name: str = name
11 | self._value: str = value
12 |
13 | def _get_name(self) -> str:
14 | return self._name
15 |
16 | def _get_value(self) -> str:
17 | return self._value
18 |
19 | def _set_value(self, value: str) -> None:
20 | self._value = value
21 |
22 | sut: MavenProperty = MockMavenProperty("foo", "bar")
23 |
24 | self.assertEqual(sut.name, "foo")
25 | self.assertEqual(sut.value, "bar")
26 |
27 | sut.value = "baz"
28 |
29 | self.assertEqual(sut.name, "foo")
30 | self.assertEqual(sut.value, "baz")
31 |
--------------------------------------------------------------------------------
/.scripts/bump-version/__tests__/maven/test_xml_maven_module_reader.py:
--------------------------------------------------------------------------------
1 | from pathlib import Path
2 | from typing import cast
3 | from unittest import TestCase
4 |
5 | from maven.xml_maven_module import XmlMavenModule
6 | from maven.xml_maven_module_identifier import XmlMavenModuleIdentifier
7 | from maven.xml_maven_module_reader import XmlMavenModuleReader
8 |
9 |
10 | class TestXmlMavenModuleReader(TestCase):
11 | RESOURCES: Path = Path(Path(__file__).parent, "resources", Path(__file__).stem)
12 |
13 | def test_read_single_module(self) -> None:
14 | sut: XmlMavenModuleReader = XmlMavenModuleReader()
15 |
16 | module: XmlMavenModule = sut.read(
17 | Path(self.RESOURCES, "single_module", "pom.xml")
18 | )
19 |
20 | self.assertEqual(module.identifier.group_id, "com.example")
21 | self.assertEqual(module.identifier.artifact_id, "application")
22 | self.assertEqual(module.identifier.version, "13.3.7")
23 | self.assertEqual(len(module.properties), 1)
24 | self.assertEqual(len(module.dependencies), 2)
25 | self.assertEqual(len(module.plugins), 2)
26 | self.assertIsNotNone(module.parent_identifier)
27 | self.assertEqual(module.parent_identifier.group_id, "com.example")
28 | self.assertEqual(module.parent_identifier.artifact_id, "parent")
29 | self.assertEqual(module.parent_identifier.version, "1.1.1")
30 |
31 | def test_read_multi_module_recursively(self) -> None:
32 | sut: XmlMavenModuleReader = XmlMavenModuleReader()
33 |
34 | modules: list[XmlMavenModule] = sut.read_recursive(
35 | Path(self.RESOURCES, "multi_module", "pom.xml")
36 | )
37 |
38 | self.assertEqual(len(modules), 2)
39 |
40 | parent: XmlMavenModule = modules[0]
41 | self.assertEqual(parent.identifier.group_id, "com.example")
42 | self.assertEqual(parent.identifier.artifact_id, "application")
43 | self.assertEqual(parent.identifier.version, "13.3.7")
44 | self.assertEqual(len(parent.properties), 1)
45 | self.assertEqual(len(parent.dependencies), 2)
46 | self.assertIsNotNone(parent.parent_identifier)
47 | self.assertEqual(parent.parent_identifier.group_id, "com.example")
48 | self.assertEqual(parent.parent_identifier.artifact_id, "parent")
49 | self.assertEqual(parent.parent_identifier.version, "1.1.1")
50 |
51 | child: XmlMavenModule = modules[1]
52 | self.assertEqual(child.identifier.group_id, "com.example")
53 | self.assertEqual(child.identifier.artifact_id, "sub-module")
54 | self.assertEqual(child.identifier.version, "1.33.7")
55 | self.assertEqual(len(child.properties), 1)
56 | self.assertEqual(len(child.dependencies), 1)
57 | self.assertIsNotNone(child.parent_identifier)
58 | self.assertEqual(child.parent_identifier.group_id, "com.example")
59 | self.assertEqual(child.parent_identifier.artifact_id, "application")
60 | self.assertEqual(child.parent_identifier.version, "1.33.7")
61 |
62 | self.assertIsInstance(child.identifier, XmlMavenModuleIdentifier)
63 | self.assertIsInstance(child.parent_identifier, XmlMavenModuleIdentifier)
64 | self.assertEqual(
65 | cast(XmlMavenModuleIdentifier, child.identifier).version_node,
66 | cast(XmlMavenModuleIdentifier, child.parent_identifier).version_node,
67 | )
68 |
--------------------------------------------------------------------------------
/.scripts/bump-version/__tests__/utility/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SAP/btp-environment-variable-access/41d0e1f3bdb0307819a38131a1e79c9da45ef756/.scripts/bump-version/__tests__/utility/__init__.py
--------------------------------------------------------------------------------
/.scripts/bump-version/__tests__/utility/test_type_utility.py:
--------------------------------------------------------------------------------
1 | from typing import Optional
2 | from unittest import TestCase
3 |
4 | from utility.type_utility import get_or_else, get_or_raise, if_not_none, without_nones
5 |
6 |
7 | class TestTypeUtility(TestCase):
8 | NONE: Optional[str] = None
9 |
10 | def test_get_or_else(self) -> None:
11 | self.assertEqual(get_or_else("Foo", "Bar"), "Foo")
12 | self.assertEqual(get_or_else(self.NONE, "Bar"), "Bar")
13 | self.assertEqual(get_or_else(self.NONE, lambda: "Bar"), "Bar")
14 |
15 | def test_get_or_raise(self) -> None:
16 | self.assertEqual(get_or_raise("Foo", Exception("Assertion Error")), "Foo")
17 |
18 | with self.assertRaises(AssertionError):
19 | get_or_raise(None)
20 |
21 | with self.assertRaises(Exception):
22 | get_or_raise(None, Exception())
23 |
24 | with self.assertRaises(Exception):
25 | get_or_raise(None, lambda: Exception())
26 |
27 | def test_if_not_none(self) -> None:
28 | sut: list[str] = []
29 |
30 | if_not_none("Hello, World!", sut.append)
31 | self.assertListEqual(sut, ["Hello, World!"])
32 |
33 | if_not_none(self.NONE, sut.append)
34 | self.assertListEqual(sut, ["Hello, World!"])
35 |
36 | def test_without_nones(self) -> None:
37 | sut: list[str] = ["foo", None, "bar", None, None, "baz"]
38 |
39 | self.assertListEqual(without_nones(sut), ["foo", "bar", "baz"])
40 |
--------------------------------------------------------------------------------
/.scripts/bump-version/__tests__/utility/xml/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SAP/btp-environment-variable-access/41d0e1f3bdb0307819a38131a1e79c9da45ef756/.scripts/bump-version/__tests__/utility/xml/__init__.py
--------------------------------------------------------------------------------
/.scripts/bump-version/__tests__/utility/xml/test_e_tree_xml_document.py:
--------------------------------------------------------------------------------
1 | from typing import Optional
2 | from unittest import TestCase
3 | from xml.etree.ElementTree import Element, ElementTree
4 |
5 | from utility.xml.e_tree_xml_document import ETreeXmlDocument
6 | from utility.xml.xml_node import XmlNode
7 |
8 |
9 | class TestETreeXmlDocument(TestCase):
10 | def test_find_root_node(self) -> None:
11 | root_element: Element = Element("{root-namespace}root-node")
12 | tree: ElementTree = ElementTree(root_element)
13 |
14 | sut: ETreeXmlDocument = ETreeXmlDocument(tree)
15 |
16 | root_node: Optional[XmlNode] = sut.find_first_node("root-node")
17 |
18 | self.assertIsNotNone(root_node)
19 | self.assertEqual(root_node.name, "root-node")
20 | self.assertEqual(root_node.namespace, "root-namespace")
21 | self.assertIsNone(root_node.text)
22 |
23 | def test_find_nested_node(self) -> None:
24 | root_element: Element = Element("{root-namespace}root-node")
25 | root_element.text = "root-text"
26 | sub_element: Element = Element("{sub-namespace}sub-node")
27 | sub_element.text = "sub-text"
28 | root_element.append(sub_element)
29 |
30 | tree: ElementTree = ElementTree(root_element)
31 |
32 | sut: ETreeXmlDocument = ETreeXmlDocument(tree)
33 |
34 | sub_node: Optional[XmlNode] = sut.find_first_node("root-node", "sub-node")
35 |
36 | self.assertIsNotNone(sub_node)
37 | self.assertEqual(sub_node.name, "sub-node")
38 | self.assertEqual(sub_node.namespace, "sub-namespace")
39 | self.assertEqual(sub_node.text, "sub-text")
40 |
--------------------------------------------------------------------------------
/.scripts/bump-version/__tests__/utility/xml/test_e_tree_xml_node.py:
--------------------------------------------------------------------------------
1 | from typing import Optional
2 | from unittest import TestCase
3 | from xml.etree.ElementTree import Element
4 |
5 | from utility.xml.e_tree_xml_node import ETreeXmlNode
6 | from utility.xml.xml_node import XmlNode
7 |
8 |
9 | class TestETreeXmlNode(TestCase):
10 | def test_get_name(self) -> None:
11 | element: Element = Element("{namespace}name")
12 | sut: ETreeXmlNode = ETreeXmlNode(element)
13 |
14 | self.assertEqual(sut.name, "name")
15 |
16 | def test_get_namespace(self) -> None:
17 | element: Element = Element("{namespace}name")
18 | sut: ETreeXmlNode = ETreeXmlNode(element)
19 |
20 | self.assertEqual(sut.namespace, "namespace")
21 |
22 | def test_get_text(self) -> None:
23 | element: Element = Element("{namespace}name")
24 | element.text = "Hello, World!"
25 | sut: ETreeXmlNode = ETreeXmlNode(element)
26 |
27 | self.assertEqual(sut.text, "Hello, World!")
28 |
29 | def test_set_text(self) -> None:
30 | element: Element = Element("{namespace}name")
31 | element.text = "Hello, World!"
32 | sut: ETreeXmlNode = ETreeXmlNode(element)
33 |
34 | sut.text = "foo"
35 |
36 | self.assertEqual(element.text, "foo")
37 | self.assertEqual(sut.text, "foo")
38 |
39 | def test_find_first_node_without_path(self) -> None:
40 | element: Element = Element("{namespace}name")
41 | sut: ETreeXmlNode = ETreeXmlNode(element)
42 |
43 | self.assertEqual(sut, sut.find_first_node())
44 |
45 | def test_find_first_nested_node(self) -> None:
46 | element: Element = Element("{namespace}name")
47 | sub_element: Element = Element("{sub-namespace}sub-name")
48 | sub_element.text = "sub-text"
49 |
50 | element.append(sub_element)
51 |
52 | sut: ETreeXmlNode = ETreeXmlNode(element)
53 |
54 | sub_node: Optional[ETreeXmlNode] = sut.find_first_node("sub-name")
55 |
56 | self.assertIsNotNone(sub_node)
57 | self.assertEqual(sub_node.name, "sub-name")
58 | self.assertEqual(sub_node.namespace, "sub-namespace")
59 | self.assertEqual(sub_node.text, "sub-text")
60 |
61 | def test_find_first_nested_node_with_changed_element(self) -> None:
62 | element: Element = Element("{namespace}name")
63 | sub_element: Element = Element("{sub-namespace}sub-name")
64 | sub_element.text = "sub-text"
65 |
66 | element.append(sub_element)
67 |
68 | sut: ETreeXmlNode = ETreeXmlNode(element)
69 |
70 | sub_node: Optional[ETreeXmlNode] = sut.find_first_node("sub-name")
71 |
72 | # sanity check: sub node is found
73 | self.assertIsNotNone(sub_node)
74 |
75 | element.remove(sub_element)
76 |
77 | sub_node = sut.find_first_node("sub-name")
78 |
79 | self.assertIsNone(sub_node)
80 |
81 | def test_find_all_nodes_without_path(self) -> None:
82 | element: Element = Element("{namespace}name")
83 | sut: ETreeXmlNode = ETreeXmlNode(element)
84 |
85 | self.assertListEqual(sut.find_all_nodes(), [sut])
86 |
87 | def test_find_all_nested_nodes(self) -> None:
88 | element: Element = Element("{namespace}name")
89 | first_sub_element: Element = Element("{sub-namespace-1}sub-name")
90 | first_sub_element.text = "sub-text-1"
91 | second_sub_element: Element = Element("{sub-namespace-2}sub-name")
92 | second_sub_element.text = "sub-text-2"
93 |
94 | element.append(first_sub_element)
95 | element.append(second_sub_element)
96 |
97 | sut: ETreeXmlNode = ETreeXmlNode(element)
98 |
99 | sub_nodes: list[XmlNode] = sut.find_all_nodes("sub-name")
100 |
101 | self.assertEqual(len(sub_nodes), 2)
102 |
103 | first_sub_node: XmlNode = sub_nodes[0]
104 | self.assertEqual(first_sub_node.name, "sub-name")
105 | self.assertEqual(first_sub_node.namespace, "sub-namespace-1")
106 | self.assertEqual(first_sub_node.text, "sub-text-1")
107 |
108 | second_sub_node: XmlNode = sub_nodes[1]
109 | self.assertEqual(second_sub_node.name, "sub-name")
110 | self.assertEqual(second_sub_node.namespace, "sub-namespace-2")
111 | self.assertEqual(second_sub_node.text, "sub-text-2")
112 |
113 | def test_find_all_nested_nodes_with_changed_element(self) -> None:
114 | element: Element = Element("{namespace}name")
115 | first_sub_element: Element = Element("{sub-namespace-1}sub-name")
116 | first_sub_element.text = "sub-text-1"
117 | second_sub_element: Element = Element("{sub-namespace-2}sub-name")
118 | second_sub_element.text = "sub-text-2"
119 |
120 | element.append(first_sub_element)
121 | element.append(second_sub_element)
122 |
123 | sut: ETreeXmlNode = ETreeXmlNode(element)
124 |
125 | sub_nodes: list[XmlNode] = sut.find_all_nodes("sub-name")
126 |
127 | # sanity check: make sure all sub nodes are found
128 | self.assertEqual(len(sub_nodes), 2)
129 |
130 | element.remove(first_sub_element)
131 |
132 | sub_nodes = sut.find_all_nodes("sub-name")
133 |
134 | self.assertEqual(len(sub_nodes), 1)
135 |
--------------------------------------------------------------------------------
/.scripts/bump-version/__tests__/utility/xml/test_xml_node.py:
--------------------------------------------------------------------------------
1 | from typing import Optional
2 | from unittest import TestCase
3 |
4 | from utility.xml.xml_node import XmlNode
5 |
6 |
7 | class TestXmlNode(TestCase):
8 | def test_abstract_property(self) -> None:
9 | class MockXmlNode(XmlNode):
10 | def __init__(self, name: str, text: str):
11 | self._name: str = name
12 | self._text: str = text
13 |
14 | @property
15 | def name(self) -> str:
16 | return self._name
17 |
18 | def _get_text(self) -> str:
19 | return self._text
20 |
21 | def _set_text(self, text: str) -> None:
22 | self._text = text
23 |
24 | def _get_nodes(self) -> list["XmlNode"]:
25 | return []
26 |
27 | def find_first_node(self, *path_segments: str) -> Optional["XmlNode"]:
28 | return None
29 |
30 | def find_all_nodes(self, *path_segments: str) -> list["XmlNode"]:
31 | return []
32 |
33 | sut: XmlNode = MockXmlNode("foo", "bar")
34 |
35 | self.assertEqual(sut.name, "foo")
36 | self.assertEqual(sut.text, "bar")
37 |
38 | sut.text = "baz"
39 |
40 | self.assertEqual(sut.name, "foo")
41 | self.assertEqual(sut.text, "baz")
42 |
--------------------------------------------------------------------------------
/.scripts/bump-version/maven/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SAP/btp-environment-variable-access/41d0e1f3bdb0307819a38131a1e79c9da45ef756/.scripts/bump-version/maven/__init__.py
--------------------------------------------------------------------------------
/.scripts/bump-version/maven/maven_module.py:
--------------------------------------------------------------------------------
1 | from abc import ABC, abstractmethod
2 | from typing import Optional
3 |
4 | from maven.maven_module_identifier import MavenModuleIdentifier
5 | from maven.maven_property import MavenProperty
6 |
7 |
8 | class MavenModule(ABC):
9 | @property
10 | def identifier(self) -> MavenModuleIdentifier:
11 | return self._get_identifier()
12 |
13 | @abstractmethod
14 | def _get_identifier(self) -> MavenModuleIdentifier:
15 | raise NotImplementedError
16 |
17 | @property
18 | def parent_identifier(self) -> Optional[MavenModuleIdentifier]:
19 | return self._get_parent_identifier()
20 |
21 | @abstractmethod
22 | def _get_parent_identifier(self) -> Optional[MavenModuleIdentifier]:
23 | raise NotImplementedError
24 |
25 | @property
26 | def properties(self) -> list[MavenProperty]:
27 | return self._get_properties()
28 |
29 | @abstractmethod
30 | def _get_properties(self) -> list[MavenProperty]:
31 | raise NotImplementedError
32 |
33 | @property
34 | def dependencies(self) -> list[MavenModuleIdentifier]:
35 | return self._get_dependencies()
36 |
37 | @abstractmethod
38 | def _get_dependencies(self) -> list[MavenModuleIdentifier]:
39 | raise NotImplementedError
40 |
41 | @property
42 | def plugins(self) -> list[MavenModuleIdentifier]:
43 | return self._get_plugins()
44 |
45 | @abstractmethod
46 | def _get_plugins(self) -> list[MavenModuleIdentifier]:
47 | raise NotImplementedError
48 |
49 | def __str__(self) -> str:
50 | return str(self.identifier)
51 |
--------------------------------------------------------------------------------
/.scripts/bump-version/maven/maven_module_identifier.py:
--------------------------------------------------------------------------------
1 | from abc import ABC, abstractmethod
2 |
3 |
4 | class MavenModuleIdentifier(ABC):
5 | @property
6 | def group_id(self) -> str:
7 | raise NotImplementedError
8 |
9 | @property
10 | def artifact_id(self) -> str:
11 | raise NotImplementedError
12 |
13 | @property
14 | def version(self) -> str:
15 | return self._get_version()
16 |
17 | @abstractmethod
18 | def _get_version(self) -> str:
19 | raise NotImplementedError
20 |
21 | @version.setter
22 | def version(self, version: str) -> None:
23 | self._set_version(version)
24 |
25 | @abstractmethod
26 | def _set_version(self, version: str) -> None:
27 | raise NotImplementedError
28 |
29 | def __str__(self) -> str:
30 | return f"{self.group_id}:{self.artifact_id}:{self.version}"
31 |
--------------------------------------------------------------------------------
/.scripts/bump-version/maven/maven_module_reader.py:
--------------------------------------------------------------------------------
1 | from abc import ABC, abstractmethod
2 | from pathlib import Path
3 |
4 | from maven.maven_module import MavenModule
5 |
6 |
7 | class MavenModuleReader(ABC):
8 | @abstractmethod
9 | def read(self, pom: Path) -> MavenModule:
10 | raise NotImplementedError
11 |
12 | @abstractmethod
13 | def read_recursive(self, pom: Path) -> list[MavenModule]:
14 | raise NotImplementedError
15 |
--------------------------------------------------------------------------------
/.scripts/bump-version/maven/maven_project.py:
--------------------------------------------------------------------------------
1 | from abc import ABC, abstractmethod
2 |
3 | from maven.maven_module import MavenModule
4 |
5 |
6 | class MavenProject(ABC):
7 | @property
8 | def modules(self) -> list[MavenModule]:
9 | return self._get_modules()
10 |
11 | @abstractmethod
12 | def _get_modules(self) -> list[MavenModule]:
13 | raise NotImplementedError
14 |
15 | @abstractmethod
16 | def add_module(self, module: MavenModule) -> None:
17 | raise NotImplementedError
18 |
--------------------------------------------------------------------------------
/.scripts/bump-version/maven/maven_property.py:
--------------------------------------------------------------------------------
1 | from abc import ABC, abstractmethod
2 |
3 |
4 | class MavenProperty(ABC):
5 | @property
6 | def name(self) -> str:
7 | return self._get_name()
8 |
9 | @abstractmethod
10 | def _get_name(self) -> str:
11 | raise NotImplementedError
12 |
13 | @property
14 | def value(self) -> str:
15 | return self._get_value()
16 |
17 | @abstractmethod
18 | def _get_value(self) -> str:
19 | raise NotImplementedError
20 |
21 | @value.setter
22 | def value(self, value: str) -> None:
23 | self._set_value(value)
24 |
25 | @abstractmethod
26 | def _set_value(self, value: str) -> None:
27 | raise NotImplementedError
28 |
29 | def __str__(self) -> str:
30 | return f"{self.name} = '{self.value}'"
31 |
--------------------------------------------------------------------------------
/.scripts/bump-version/maven/xml_maven_module.py:
--------------------------------------------------------------------------------
1 | from pathlib import Path
2 | from typing import Optional
3 |
4 | from maven.maven_module import MavenModule
5 | from maven.maven_module_identifier import MavenModuleIdentifier
6 | from maven.maven_property import MavenProperty
7 | from maven.xml_maven_module_identifier import XmlMavenModuleIdentifier
8 | from maven.xml_maven_property import XmlMavenProperty
9 | from utility.type_utility import get_or_else
10 | from utility.xml.xml_document import XmlDocument
11 |
12 |
13 | class XmlMavenModule(MavenModule):
14 | def __init__(
15 | self,
16 | xml_document: XmlDocument,
17 | pom_file: Path,
18 | identifier: XmlMavenModuleIdentifier,
19 | parent_identifier: Optional[XmlMavenModuleIdentifier] = None,
20 | properties: Optional[list[XmlMavenProperty]] = None,
21 | dependencies: Optional[list[XmlMavenModuleIdentifier]] = None,
22 | plugins: Optional[list[XmlMavenModuleIdentifier]] = None,
23 | ):
24 | self._xml_document: XmlDocument = xml_document
25 | self._pom_file: Path = pom_file
26 | self._identifier: XmlMavenModuleIdentifier = identifier
27 | self._parent_identifier: Optional[XmlMavenModuleIdentifier] = parent_identifier
28 | self._properties: list[XmlMavenProperty] = get_or_else(properties, list)
29 | self._dependencies: list[XmlMavenModuleIdentifier] = get_or_else(
30 | dependencies, list
31 | )
32 | self._plugins: list[XmlMavenModuleIdentifier] = get_or_else(plugins, list)
33 |
34 | @property
35 | def xml_document(self) -> XmlDocument:
36 | return self._xml_document
37 |
38 | @property
39 | def pom_file(self) -> Path:
40 | return self._pom_file
41 |
42 | def _get_identifier(self) -> MavenModuleIdentifier:
43 | return self._identifier
44 |
45 | def _get_parent_identifier(self) -> Optional[MavenModuleIdentifier]:
46 | return self._parent_identifier
47 |
48 | def _get_properties(self) -> list[MavenProperty]:
49 | return self._properties
50 |
51 | def _get_dependencies(self) -> list[MavenModuleIdentifier]:
52 | return self._dependencies
53 |
54 | def _get_plugins(self) -> list[MavenModuleIdentifier]:
55 | return self._plugins
56 |
--------------------------------------------------------------------------------
/.scripts/bump-version/maven/xml_maven_module_identifier.py:
--------------------------------------------------------------------------------
1 | from maven.maven_module_identifier import MavenModuleIdentifier
2 | from utility.xml.xml_node import XmlNode
3 |
4 |
5 | class XmlMavenModuleIdentifier(MavenModuleIdentifier):
6 | def __init__(
7 | self, group_id_node: XmlNode, artifact_id_node: XmlNode, version_node: XmlNode
8 | ):
9 | self._group_id_node: XmlNode = group_id_node
10 | self._artifact_id_node: XmlNode = artifact_id_node
11 | self._version_node: XmlNode = version_node
12 |
13 | @property
14 | def group_id(self) -> str:
15 | return self._group_id_node.text
16 |
17 | @property
18 | def group_id_node(self) -> XmlNode:
19 | return self._group_id_node
20 |
21 | @group_id_node.setter
22 | def group_id_node(self, node: XmlNode) -> None:
23 | self._group_id_node = node
24 |
25 | @property
26 | def artifact_id(self) -> str:
27 | return self._artifact_id_node.text
28 |
29 | @property
30 | def artifact_id_node(self) -> XmlNode:
31 | return self._artifact_id_node
32 |
33 | def _get_version(self) -> str:
34 | return self._version_node.text
35 |
36 | def _set_version(self, version: str) -> None:
37 | self._version_node.text = version
38 |
39 | @property
40 | def version_node(self) -> XmlNode:
41 | return self._version_node
42 |
43 | @version_node.setter
44 | def version_node(self, node: XmlNode) -> None:
45 | self._version_node = node
46 |
--------------------------------------------------------------------------------
/.scripts/bump-version/maven/xml_maven_property.py:
--------------------------------------------------------------------------------
1 | from maven.maven_property import MavenProperty
2 | from utility.xml.xml_node import XmlNode
3 |
4 |
5 | class XmlMavenProperty(MavenProperty):
6 | def __init__(self, node: XmlNode):
7 | self._delegate: XmlNode = node
8 |
9 | @property
10 | def node(self) -> XmlNode:
11 | return self._delegate
12 |
13 | def _get_name(self) -> str:
14 | return self._delegate.name
15 |
16 | def _get_value(self) -> str:
17 | return self._delegate.text
18 |
19 | def _set_value(self, value: str) -> None:
20 | self._delegate.text = value
21 |
--------------------------------------------------------------------------------
/.scripts/bump-version/maven_version.py:
--------------------------------------------------------------------------------
1 | import os
2 | from argparse import ArgumentParser
3 | from pathlib import Path
4 | from typing import Any, Dict
5 |
6 | from maven.xml_maven_module import XmlMavenModule
7 | from maven.xml_maven_module_reader import XmlMavenModuleReader
8 | from maven.xml_maven_project import XmlMavenProject
9 |
10 |
11 | def bump(
12 | project_root_poms: list[Path],
13 | bump_type: XmlMavenProject.VersionBumpType,
14 | custom_version: str = "",
15 | assert_uniform_version: bool = True,
16 | github_actions_output: bool = True,
17 | ) -> None:
18 | if any(filter(lambda x: x.name != "pom.xml", project_root_poms)):
19 | raise AssertionError(
20 | f"Currently, only Maven projects ('pom.xml') are supported by this operation. Sorry."
21 | )
22 |
23 | if not bump_type and not custom_version:
24 | raise AssertionError("Either bump-type or custom-version must be provided.")
25 |
26 | if custom_version and not XmlMavenProject.SEMANTIC_VERSION.match(custom_version):
27 | raise AssertionError(f"Invalid custom version '{custom_version}'.")
28 |
29 | project: XmlMavenProject = XmlMavenProject()
30 | module_reader: XmlMavenModuleReader = XmlMavenModuleReader()
31 |
32 | for project_root_pom in project_root_poms:
33 | project.add_modules(*module_reader.read_recursive(project_root_pom))
34 |
35 | if github_actions_output:
36 | versions: Dict[XmlMavenModule, str] = project.get_module_versions()
37 | unique_versions: list[str] = list(set(versions.values()))
38 |
39 | if len(unique_versions) == 1:
40 | __write_to_github_actions_output("old_version", unique_versions[0])
41 |
42 | else:
43 | __write_to_github_actions_output("old_version", "undefined")
44 |
45 | project.bump_version(
46 | bump_type, custom_version, assert_uniform_version=assert_uniform_version, write_modules=True
47 | )
48 |
49 | if github_actions_output:
50 | versions: Dict[XmlMavenModule, str] = project.get_module_versions()
51 | unique_versions: list[str] = list(set(versions.values()))
52 |
53 | if len(unique_versions) == 1:
54 | __write_to_github_actions_output("new_version", unique_versions[0])
55 |
56 | else:
57 | __write_to_github_actions_output("new_version", "undefined")
58 |
59 |
60 | def __write_to_github_actions_output(key: str, value: str) -> None:
61 | if "GITHUB_OUTPUT" not in os.environ:
62 | print("UNABLE TO WRITE TO GITHUB_OUTPUT. '$GITHUB_OUTPUT' IS NOT DEFINED.")
63 | return
64 |
65 | output_path: str = os.environ["GITHUB_OUTPUT"]
66 | if not output_path:
67 | print("UNABLE TO WRITE TO GITHUB_OUTPUT. '$GITHUB_OUTPUT' IS NOT DEFINED.")
68 | return
69 |
70 | with open(output_path, "a+") as output_file:
71 | print(f"{key}={value}", file=output_file)
72 |
73 |
74 | def main() -> None:
75 | argument_parser: ArgumentParser = ArgumentParser(
76 | "Script to manage Maven project versions"
77 | )
78 |
79 | sub_parsers: Any = argument_parser.add_subparsers(dest="subparser")
80 |
81 | # region bump command
82 |
83 | bump_parser: ArgumentParser = sub_parsers.add_parser(
84 | "bump", help="Bumps a Maven project version."
85 | )
86 | bump_parser.add_argument(
87 | "--bump-type",
88 | type=str,
89 | required=False,
90 | help=f"Available values: '{XmlMavenProject.VersionBumpType.MAJOR}', "
91 | f"'{XmlMavenProject.VersionBumpType.MINOR}', "
92 | f"and '{XmlMavenProject.VersionBumpType.PATCH}'"
93 | "Note: Either bump-type or custom-version must be provided.",
94 | )
95 | bump_parser.add_argument(
96 | "--custom-version",
97 | type=str,
98 | required=False,
99 | help="Allows to update maven modules with a custom version. "
100 | "The input value should be semver compatible version string (X.Y.Z). "
101 | "Note: Either bump-type or custom-version must be provided."
102 | "This option will override the bump-type option if both are provided.",
103 | )
104 | bump_parser.add_argument(
105 | "--accept-non-uniform-versions",
106 | action="store_false",
107 | required=False,
108 | help="Indicates whether the version bump should even be performed if the project contains "
109 | "different versions. "
110 | "Note: ALL module versions will be increased if this option is enabled.",
111 | )
112 | bump_parser.add_argument(
113 | "--no-github-action-outputs",
114 | action="store_true",
115 | required=False,
116 | help="Indicates whether the script should NOT set the GitHub action outputs.",
117 | )
118 | bump_parser.add_argument("pom", type=Path, nargs="+")
119 |
120 | # endregion
121 |
122 | parsed_args: Any = argument_parser.parse_args()
123 | if parsed_args.subparser == "bump":
124 | bump_type = XmlMavenProject.VersionBumpType[parsed_args.bump_type.upper()] if parsed_args.bump_type else ""
125 | bump(
126 | parsed_args.pom,
127 | bump_type,
128 | parsed_args.custom_version,
129 | not parsed_args.accept_non_uniform_versions,
130 | not parsed_args.no_github_action_outputs,
131 | )
132 |
133 |
134 | if __name__ == "__main__":
135 | main()
136 |
--------------------------------------------------------------------------------
/.scripts/bump-version/utility/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SAP/btp-environment-variable-access/41d0e1f3bdb0307819a38131a1e79c9da45ef756/.scripts/bump-version/utility/__init__.py
--------------------------------------------------------------------------------
/.scripts/bump-version/utility/type_utility.py:
--------------------------------------------------------------------------------
1 | from typing import Any, Callable, Optional, TypeVar, Union
2 |
3 | T = TypeVar("T")
4 |
5 |
6 | def get_or_else(maybe_value: Optional[T], fallback: Union[T, Callable[[], T]]) -> T:
7 | if maybe_value is not None:
8 | return maybe_value
9 |
10 | if isinstance(fallback, Callable):
11 | return fallback()
12 |
13 | return fallback
14 |
15 |
16 | def get_or_raise(
17 | maybe_value: Optional[T],
18 | error: Optional[Union[Exception, Callable[[], Exception]]] = None,
19 | ) -> T:
20 | if maybe_value is not None:
21 | return maybe_value
22 |
23 | if error is None:
24 | raise AssertionError("Value must not be None")
25 |
26 | if isinstance(error, Exception):
27 | raise error
28 |
29 | raise error()
30 |
31 |
32 | def if_not_none(maybe_value: Optional[T], action: Callable[[T], None]) -> None:
33 | if maybe_value is None:
34 | return
35 |
36 | action(maybe_value)
37 |
38 |
39 | def without_nones(values: list[Optional[T]]) -> list[T]:
40 | return [value for value in values if value is not None]
41 |
42 |
43 | def all_defined(*values: Any) -> bool:
44 | for v in values:
45 | if v is None:
46 | return False
47 |
48 | return True
49 |
--------------------------------------------------------------------------------
/.scripts/bump-version/utility/xml/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SAP/btp-environment-variable-access/41d0e1f3bdb0307819a38131a1e79c9da45ef756/.scripts/bump-version/utility/xml/__init__.py
--------------------------------------------------------------------------------
/.scripts/bump-version/utility/xml/e_tree_xml_document.py:
--------------------------------------------------------------------------------
1 | from pathlib import Path
2 | from typing import Optional
3 | from xml.etree.ElementTree import ElementTree
4 |
5 | from utility.xml.e_tree_xml_node import ETreeXmlNode
6 | from utility.xml.xml_document import XmlDocument
7 | from utility.xml.xml_node import XmlNode
8 |
9 |
10 | class ETreeXmlDocument(XmlDocument):
11 | def __init__(self, element_tree: ElementTree):
12 | self._delegate: ElementTree = element_tree
13 |
14 | def find_first_node(self, *path_segments: str) -> Optional[XmlNode]:
15 | maybe_root: Optional[XmlNode] = self._try_get_root_node()
16 | if maybe_root is None:
17 | return None
18 |
19 | if len(path_segments) < 1:
20 | return None
21 |
22 | if maybe_root.name != path_segments[0]:
23 | return None
24 |
25 | return maybe_root.find_first_node(*path_segments[1:])
26 |
27 | def find_all_nodes(self, *path_segments: str) -> list[XmlNode]:
28 | maybe_root: Optional[XmlNode] = self._try_get_root_node()
29 | if maybe_root is None:
30 | return []
31 |
32 | if len(path_segments) < 1:
33 | return []
34 |
35 | if maybe_root.name != path_segments[0]:
36 | return []
37 |
38 | return maybe_root.find_all_nodes(*path_segments)
39 |
40 | def save(self, file: Path) -> None:
41 | namespace: str = self._get_default_namespace()
42 | self._delegate.write(
43 | file, encoding="UTF-8", xml_declaration=True, default_namespace=namespace
44 | )
45 |
46 | self._replace_quotes_with_double_quotes(file)
47 |
48 | def _get_default_namespace(self) -> str:
49 | maybe_root: Optional[XmlNode] = self._try_get_root_node()
50 | if maybe_root is None:
51 | return ""
52 |
53 | return maybe_root.namespace
54 |
55 | def _try_get_root_node(self) -> Optional[XmlNode]:
56 | return ETreeXmlNode.try_create(self._delegate.getroot())
57 |
58 | def _replace_quotes_with_double_quotes(self, file: Path) -> None:
59 | content: str = ""
60 | with file.open("r") as r:
61 | for line in r:
62 | if line.strip().startswith("
18 |
19 | com.sap.cloud.environment.servicebinding.api
20 | java-core-api
21 |
22 |
23 | org.slf4j
24 | slf4j-api
25 |
26 |
27 |
28 |
29 | org.junit.jupiter
30 | junit-jupiter-api
31 | test
32 |
33 |
34 | org.junit.jupiter
35 | junit-jupiter
36 | test
37 |
38 |
39 | org.mockito
40 | mockito-core
41 | test
42 |
43 |
44 | org.assertj
45 | assertj-core
46 | test
47 |
48 |
49 | org.slf4j
50 | slf4j-simple
51 | test
52 |
53 |
54 |
55 |
--------------------------------------------------------------------------------
/api-parent/access-api/src/main/java/com/sap/cloud/environment/servicebinding/api/DefaultServiceBindingAccessor.java:
--------------------------------------------------------------------------------
1 | package com.sap.cloud.environment.servicebinding.api;
2 |
3 | import java.util.List;
4 | import java.util.stream.Collectors;
5 |
6 | import javax.annotation.Nonnull;
7 | import javax.annotation.Nullable;
8 |
9 | import org.slf4j.Logger;
10 | import org.slf4j.LoggerFactory;
11 |
12 | /**
13 | * A static access point for the default {@link ServiceBindingAccessor}. The statically stored instance inside this
14 | * class can both be retrieved ({@link DefaultServiceBindingAccessor#getInstance()}) and manipulated
15 | * ({@link DefaultServiceBindingAccessor#setInstance(ServiceBindingAccessor)}). Applications might want to overwrite the
16 | * default instance during startup to tweak the default behavior of libraries that are relying on it.
17 | * Please note: It is considered best practice to offer APIs that accept a dedicated
18 | * {@link ServiceBindingAccessor} instance instead of using the globally available instance stored inside this class.
19 | * For example, libraries that are using {@link ServiceBindingAccessor}s should offer APIs such as the following:
20 | *
21 | *
22 | * public ReturnType doSomethingWithServiceBindings( @Nonnull final ServiceBindingAccessor accessor );
23 | *
24 | *
25 | * If that is, for some reason, not feasible, only then should this static default instance be used.
26 | */
27 | public final class DefaultServiceBindingAccessor
28 | {
29 | @Nonnull
30 | private static final Logger logger = LoggerFactory.getLogger(DefaultServiceBindingAccessor.class);
31 |
32 | @Nonnull
33 | private static ServiceBindingAccessor instance = newDefaultInstance();
34 |
35 | private DefaultServiceBindingAccessor()
36 | {
37 | throw new IllegalStateException("This utility class must not be instantiated.");
38 | }
39 |
40 | /**
41 | * Returns the statically stored {@link ServiceBindingAccessor} instance. This instance can be changed at any time
42 | * by using {@link DefaultServiceBindingAccessor#setInstance(ServiceBindingAccessor)}.
43 | * By default, the returned {@link ServiceBindingAccessor} will be assembled in the following way:
44 | *
45 | * Use {@link ServiceBindingAccessor#getInstancesViaServiceLoader()} to retrieve a list of
46 | * {@link ServiceBindingAccessor} instances.
47 | * Combine instances of the found implementations using the {@link ServiceBindingMerger} (the merging strategy
48 | * used is {@link ServiceBindingMerger#KEEP_EVERYTHING}).
49 | * Wrap the resulting instance of {@link ServiceBindingMerger} into a {@link SimpleServiceBindingCache}.
50 | *
51 | *
52 | * @return The statically stored {@link ServiceBindingAccessor} instance.
53 | */
54 | @Nonnull
55 | public static ServiceBindingAccessor getInstance()
56 | {
57 | return instance;
58 | }
59 |
60 | /**
61 | * Overwrites the statically stored {@link ServiceBindingAccessor} instance.
62 | *
63 | * @param accessor
64 | * The {@link ServiceBindingAccessor} instance that should be returned by
65 | * {@link DefaultServiceBindingAccessor#getInstance()}. If {@code accessor} is {@code null}, the default
66 | * instance will be used (see {@link DefaultServiceBindingAccessor#getInstance()} for more details).
67 | */
68 | public static void setInstance( @Nullable final ServiceBindingAccessor accessor )
69 | {
70 | if( accessor != null ) {
71 | logger.debug("Setting instance to {}.", accessor.getClass().getName());
72 | instance = accessor;
73 | } else {
74 | logger.debug("Resetting instance.");
75 | instance = newDefaultInstance();
76 | }
77 | }
78 |
79 | @Nonnull
80 | private static ServiceBindingAccessor newDefaultInstance()
81 | {
82 | final List defaultAccessors = ServiceBindingAccessor.getInstancesViaServiceLoader();
83 |
84 | if( logger.isDebugEnabled() ) {
85 | final String classNames =
86 | defaultAccessors.stream().map(Object::getClass).map(Class::getName).collect(Collectors.joining(", "));
87 | logger
88 | .debug(
89 | "Following implementations of {} will be used for the {}: {}.",
90 | ServiceBindingAccessor.class.getSimpleName(),
91 | DefaultServiceBindingAccessor.class.getSimpleName(),
92 | classNames);
93 | }
94 |
95 | final ServiceBindingMerger bindingMerger =
96 | new ServiceBindingMerger(defaultAccessors, ServiceBindingMerger.KEEP_EVERYTHING);
97 |
98 | return new SimpleServiceBindingCache(bindingMerger);
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/api-parent/access-api/src/main/java/com/sap/cloud/environment/servicebinding/api/ServiceBindingAccessor.java:
--------------------------------------------------------------------------------
1 | package com.sap.cloud.environment.servicebinding.api;
2 |
3 | import java.util.List;
4 | import java.util.ServiceLoader;
5 | import java.util.stream.Collectors;
6 | import java.util.stream.StreamSupport;
7 |
8 | import javax.annotation.Nonnull;
9 |
10 | import com.sap.cloud.environment.servicebinding.api.exception.ServiceBindingAccessException;
11 |
12 | /**
13 | * Represents a source for {@link ServiceBinding}s.
14 | */
15 | @FunctionalInterface
16 | public interface ServiceBindingAccessor
17 | {
18 | /**
19 | * Returns {@link ServiceBindingAccessor} instances for implementations that are exposed via the
20 | * Service Loader Pattern .
21 | *
22 | * These instances are useful when the behavior of one (or more) specific {@link ServiceBindingAccessor}s should be
23 | * overwritten while leaving others in their default state.
24 | * Example:
25 | *
26 | *
27 | * final List<ServiceBindingAccessor> defaultInstances = ServiceBindingAccessor.getInstancesViaServiceLoader();
28 | * if( defaultInstances.removeIf(SapVcapServicesServiceBindingAccessor.class::isInstance) ) {
29 | * defaultInstances.add(new SapVcapServicesServiceBindingAccessor(customEnvironmentVariableReader));
30 | * }
31 | *
32 | * final ServiceBindingMerger merger = new ServiceBindingMerger(defaultInstances, ServiceBindingMerger.KEEP_UNIQUE);
33 | * final SimpleServiceBindingCache cache = new SimpleServiceBindingCache(merger);
34 | *
35 | * DefaultServiceBindingAccessor.setInstance(cache);
36 | *
37 | *
38 | * @return A {@link List} of {@link ServiceBindingAccessor} instances created from implementations that are exposed
39 | * via the Service Locator Pattern (see above).
40 | */
41 | static List getInstancesViaServiceLoader()
42 | {
43 | final ServiceLoader serviceLoader =
44 | ServiceLoader.load(ServiceBindingAccessor.class, ServiceBindingAccessor.class.getClassLoader());
45 | return StreamSupport.stream(serviceLoader.spliterator(), false).collect(Collectors.toList());
46 | }
47 |
48 | /**
49 | * Retrieves all {@link ServiceBinding}s that are accessible for this {@link ServiceBindingAccessor}.
50 | *
51 | * @return All accessible {@link ServiceBinding}s.
52 | * @throws ServiceBindingAccessException
53 | * Thrown if anything went wrong while loading the {@link ServiceBinding}s.
54 | */
55 | @Nonnull
56 | List getServiceBindings()
57 | throws ServiceBindingAccessException;
58 | }
59 |
--------------------------------------------------------------------------------
/api-parent/access-api/src/main/java/com/sap/cloud/environment/servicebinding/api/ServiceBindingMerger.java:
--------------------------------------------------------------------------------
1 | package com.sap.cloud.environment.servicebinding.api;
2 |
3 | import java.util.ArrayList;
4 | import java.util.Collection;
5 | import java.util.List;
6 |
7 | import javax.annotation.Nonnull;
8 |
9 | /**
10 | * A {@link ServiceBindingAccessor} that merges the result of multiple other {@link ServiceBindingAccessor}s. This is
11 | * done in the following manner:
12 | * 1. Create an empty {@link java.util.Set} of {@link ServiceBinding}s - this is the final result that should be
13 | * returned by {@link #getServiceBindings()}.
14 | * 2. For each delegate {@link ServiceBindingAccessor}:
15 | * 1. Call {@link ServiceBindingAccessor#getServiceBindings()}.
16 | * 2. For each {@link ServiceBinding}:
17 | * 1. Check whether the given {@link ServiceBinding} already exists* in the result
18 | * {@link java.util.Set}.
19 | * 2. If the {@link ServiceBinding} does not yet exist, add it to the result.
20 | * *: This class uses the {@link EqualityComparer} to determine whether a given {@link ServiceBinding} already
21 | * exists in the result {@link java.util.Set}.
22 | */
23 | public class ServiceBindingMerger implements ServiceBindingAccessor
24 | {
25 | /**
26 | * A {@link EqualityComparer} that always evaluates to {@code false}. Therefore, all {@link ServiceBinding}s will be
27 | * included in the combined result set.
28 | */
29 | @Nonnull
30 | public static final EqualityComparer KEEP_EVERYTHING = ( a, b ) -> false;
31 |
32 | /**
33 | * A {@link EqualityComparer} that compares {@link ServiceBinding} instance by using the
34 | * {@link Object#equals(Object)} implementation. Therefore, duplicated (i.e. non-unique) {@link ServiceBinding}s
35 | * will not be included in the combined result set.
36 | */
37 | @Nonnull
38 | public static final EqualityComparer KEEP_UNIQUE = Object::equals;
39 |
40 | @Nonnull
41 | private final Collection accessors;
42 |
43 | @Nonnull
44 | private final EqualityComparer equalityComparer;
45 |
46 | /**
47 | * Initializes a new {@link ServiceBindingMerger} instance. It will use the given {@code equalityComparer} to
48 | * combine the individual {@link ServiceBinding}s returned from the given {@code accessors}.
49 | *
50 | * @param accessors
51 | * The {@link ServiceBindingAccessor}s that should be used to get the actual {@link ServiceBinding}s
52 | * from.
53 | * @param equalityComparer
54 | * The {@link EqualityComparer} to check whether a given {@link ServiceBinding} is already part of the
55 | * combined result set.
56 | */
57 | public ServiceBindingMerger(
58 | @Nonnull final Collection accessors,
59 | @Nonnull final EqualityComparer equalityComparer )
60 | {
61 | this.accessors = new ArrayList<>(accessors);
62 | this.equalityComparer = equalityComparer;
63 | }
64 |
65 | @Nonnull
66 | @Override
67 | public List getServiceBindings()
68 | {
69 | final List mergedServiceBindings = new ArrayList<>();
70 | accessors
71 | .stream()
72 | .map(ServiceBindingAccessor::getServiceBindings)
73 | .flatMap(List::stream)
74 | .forEachOrdered(binding -> {
75 | if( contains(mergedServiceBindings, binding) ) {
76 | return;
77 | }
78 |
79 | mergedServiceBindings.add(binding);
80 | });
81 |
82 | return mergedServiceBindings;
83 | }
84 |
85 | private
86 | boolean
87 | contains( @Nonnull final List existingBindings, @Nonnull final ServiceBinding newBinding )
88 | {
89 | return existingBindings.stream().anyMatch(contained -> equalityComparer.areEqual(contained, newBinding));
90 | }
91 |
92 | /**
93 | * Represents a method object that is capable of comparing two instances of {@link ServiceBinding}. An instance of
94 | * this interface will be used when checking whether a given {@link ServiceBinding} is already contained in the
95 | * combined set of {@link ServiceBinding}s.
96 | */
97 | @FunctionalInterface
98 | public interface EqualityComparer
99 | {
100 | /**
101 | * Determines whether the given {@link ServiceBinding} instances ({@code a} and {@code b}) are equal.
102 | *
103 | * @param a
104 | * The first {@link ServiceBinding} instance.
105 | * @param b
106 | * The second {@link ServiceBinding} instance.
107 | * @return {@code true} if {@code a} and {@code b} are equal, {@code false} otherwise.
108 | */
109 | boolean areEqual( @Nonnull final ServiceBinding a, @Nonnull final ServiceBinding b );
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/api-parent/access-api/src/main/java/com/sap/cloud/environment/servicebinding/api/exception/ServiceBindingAccessException.java:
--------------------------------------------------------------------------------
1 | package com.sap.cloud.environment.servicebinding.api.exception;
2 |
3 | import javax.annotation.Nonnull;
4 |
5 | /**
6 | * A {@link RuntimeException} that is thrown if anything goes wrong while accessing
7 | * {@link com.sap.cloud.environment.servicebinding.api.ServiceBinding}s.
8 | */
9 | public class ServiceBindingAccessException extends RuntimeException
10 | {
11 | private static final long serialVersionUID = 8589108462580396260L;
12 |
13 | /**
14 | * Initializes a new {@link ServiceBindingAccessException} instance with a dedicated {@code message}.
15 | *
16 | * @param message
17 | * The exception message.
18 | */
19 | public ServiceBindingAccessException( @Nonnull final String message )
20 | {
21 | super(message);
22 | }
23 |
24 | /**
25 | * Initializes a new {@link ServiceBindingAccessException} instance with a dedicated {@code cause}.
26 | *
27 | * @param cause
28 | * The exception cause.
29 | */
30 | public ServiceBindingAccessException( @Nonnull final Throwable cause )
31 | {
32 | super(cause);
33 | }
34 |
35 | /**
36 | * Initializes a new {@link ServiceBindingAccessException} instance with a dedicated {@code message} and
37 | * {@code cause}.
38 | *
39 | * @param message
40 | * The exception message.
41 | * @param cause
42 | * The exception cause.
43 | */
44 | public ServiceBindingAccessException( @Nonnull final String message, @Nonnull final Throwable cause )
45 | {
46 | super(message, cause);
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/api-parent/consumption-api/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 4.0.0
4 |
5 |
6 | com.sap.cloud.environment.servicebinding.api
7 | java-api-parent
8 | 0.21.0
9 |
10 | java-consumption-api
11 |
12 |
13 | ${project.basedir}/../../
14 |
15 |
16 |
17 |
18 |
19 | com.sap.cloud.environment.servicebinding.api
20 | java-core-api
21 |
22 |
23 |
24 |
25 | org.junit.jupiter
26 | junit-jupiter-api
27 | test
28 |
29 |
30 | org.junit.jupiter
31 | junit-jupiter
32 | test
33 |
34 |
35 | org.assertj
36 | assertj-core
37 | test
38 |
39 |
40 | org.mockito
41 | mockito-core
42 | test
43 |
44 |
45 | org.slf4j
46 | slf4j-simple
47 | test
48 |
49 |
50 |
51 |
--------------------------------------------------------------------------------
/api-parent/consumption-api/src/main/java/com/sap/cloud/environment/servicebinding/api/exception/KeyNotFoundException.java:
--------------------------------------------------------------------------------
1 | package com.sap.cloud.environment.servicebinding.api.exception;
2 |
3 | import javax.annotation.Nonnull;
4 |
5 | import com.sap.cloud.environment.servicebinding.api.TypedMapView;
6 |
7 | /**
8 | * A {@link RuntimeException} that is thrown if a specific key was not found in a {@link TypedMapView}.
9 | */
10 | public class KeyNotFoundException extends RuntimeException
11 | {
12 | private static final long serialVersionUID = 1457209610404439200L;
13 |
14 | @Nonnull
15 | private final TypedMapView typedMapView;
16 |
17 | @Nonnull
18 | private final String requestedKey;
19 |
20 | /**
21 | * Initializes a new {@link KeyNotFoundException} instance with a given {@code typedMapView} and
22 | * {@code requestedKey}.
23 | *
24 | * @param typedMapView
25 | * The {@link TypedMapView} that was queries.
26 | * @param requestedKey
27 | * The key that wasn't found.
28 | */
29 | public KeyNotFoundException( @Nonnull final TypedMapView typedMapView, @Nonnull final String requestedKey )
30 | {
31 | this.typedMapView = typedMapView;
32 | this.requestedKey = requestedKey;
33 | }
34 |
35 | /**
36 | * Returns the queried {@link TypedMapView}.
37 | *
38 | * @return The queried {@link TypedMapView}.
39 | */
40 | @Nonnull
41 | public TypedMapView getTypedMapView()
42 | {
43 | return typedMapView;
44 | }
45 |
46 | /**
47 | * Returns the requested key that wasn't found.
48 | *
49 | * @return The requestde key that wasn't found.
50 | */
51 | @Nonnull
52 | public String getRequestedKey()
53 | {
54 | return requestedKey;
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/api-parent/consumption-api/src/main/java/com/sap/cloud/environment/servicebinding/api/exception/ValueCastException.java:
--------------------------------------------------------------------------------
1 | package com.sap.cloud.environment.servicebinding.api.exception;
2 |
3 | import java.util.Optional;
4 |
5 | import javax.annotation.Nonnull;
6 | import javax.annotation.Nullable;
7 |
8 | /**
9 | * A {@link RuntimeException} that is thrown if a value cannot be cast to an expected target type.
10 | */
11 | public class ValueCastException extends RuntimeException
12 | {
13 | private static final long serialVersionUID = -2894546268341628598L;
14 |
15 | @Nonnull
16 | private final Class> requestedType;
17 |
18 | @Nullable
19 | private final Object actualObject;
20 |
21 | /**
22 | * Initializes a new {@link ValueCastException} instance with the given {@code requestedType} and the
23 | * {@code actualObject}.
24 | *
25 | * @param requestedType
26 | * The {@link Class}, which the {@code actualObject} should be cast to.
27 | * @param actualObject
28 | * The actual object found, that couldn't be cast to the {@code requestedType}.
29 | */
30 | public ValueCastException( @Nonnull final Class> requestedType, @Nullable final Object actualObject )
31 | {
32 | this.requestedType = requestedType;
33 | this.actualObject = actualObject;
34 | }
35 |
36 | /**
37 | * Returns the {@link Class} that was requested as the target type.
38 | *
39 | * @return The {@link Class} that was requested as the target type.
40 | */
41 | @Nonnull
42 | public Class> getRequestedType()
43 | {
44 | return requestedType;
45 | }
46 |
47 | /**
48 | * Returns an {@link Optional} that might contain the object (if present) that should have been cast.
49 | *
50 | * @return An {@link Optional} that might contain the object (if present) that should have been cast.
51 | */
52 | @Nonnull
53 | public Optional getActualObject()
54 | {
55 | return Optional.ofNullable(actualObject);
56 | }
57 |
58 | /**
59 | * Returns an {@link Optional} that might contain the actual {@link Class} of the object (if present) that should
60 | * have been cast.
61 | *
62 | * @return An {@link Optional} that might contain the actual {@link Class} of the object (if present) that should
63 | * have been cast.
64 | */
65 | @Nonnull
66 | public Optional> getActualType()
67 | {
68 | return getActualObject().map(Object::getClass);
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/api-parent/consumption-api/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker:
--------------------------------------------------------------------------------
1 | mock-maker-inline
--------------------------------------------------------------------------------
/api-parent/core-api/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 4.0.0
4 |
5 |
6 | com.sap.cloud.environment.servicebinding.api
7 | java-api-parent
8 | 0.21.0
9 |
10 | java-core-api
11 |
12 |
13 | ${project.basedir}/../../
14 |
15 |
16 |
17 |
18 |
19 |
20 | org.junit.jupiter
21 | junit-jupiter-api
22 | test
23 |
24 |
25 | org.junit.jupiter
26 | junit-jupiter
27 | test
28 |
29 |
30 | org.assertj
31 | assertj-core
32 | test
33 |
34 |
35 | org.slf4j
36 | slf4j-simple
37 | test
38 |
39 |
40 | org.mockito
41 | mockito-core
42 | test
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/api-parent/core-api/src/main/java/com/sap/cloud/environment/servicebinding/api/ServiceBinding.java:
--------------------------------------------------------------------------------
1 | package com.sap.cloud.environment.servicebinding.api;
2 |
3 | import javax.annotation.Nonnull;
4 | import java.util.List;
5 | import java.util.Map;
6 | import java.util.Optional;
7 | import java.util.Set;
8 |
9 | /**
10 | * Represents an immutable collection of (secret) key-value properties. These properties can be used to establish
11 | * a connection to the bound service.
12 | */
13 | public interface ServiceBinding
14 | {
15 | /**
16 | * Returns the {@link Set} of contained keys.
17 | *
18 | * @return The {@link Set} of contained keys.
19 | */
20 | @Nonnull
21 | Set getKeys();
22 |
23 | /**
24 | * Checks whether the given {@code key} is contained in this {@link ServiceBinding}.
25 | *
26 | * @param key
27 | * The key to check.
28 | * @return {@code true} if the given {@code key} is contained, {@code false} otherwise.
29 | */
30 | boolean containsKey( @Nonnull final String key );
31 |
32 | /**
33 | * Returns an {@link Optional} that might contain a value (if it exists) for the given {@code key}.
34 | *
35 | * @param key
36 | * The key of the value to get.
37 | * @return An {@link Optional} that might contain a value (if it exists) for the given {@code key}.
38 | */
39 | @Nonnull
40 | Optional get( @Nonnull final String key );
41 |
42 | /**
43 | * Returns an {@link Optional} that might contain the name (if it exists) of this {@link ServiceBinding}.
44 | *
45 | * @return An {@link Optional} that might contain the name (if it exists) of this {@link ServiceBinding}.
46 | */
47 | @Nonnull
48 | Optional getName();
49 |
50 | /**
51 | * Returns an {@link Optional} that might contain the name of the bound service (if it exists) of this
52 | * {@link ServiceBinding}.
53 | *
54 | * @return An {@link Optional} that might contain the name of the bound service (if it exists) of this
55 | * {@link ServiceBinding}.
56 | */
57 | @Nonnull
58 | Optional getServiceName();
59 |
60 | /**
61 | * Returns an {@link Optional} that might contain the {@link ServiceIdentifier} of the bound service (if it exists)
62 | * of this {@link ServiceBinding}.
63 | *
64 | * @return An {@link Optional} that might contain the {@link ServiceIdentifier} of the bound service (if it exists)
65 | * of this {@link ServiceBinding}.
66 | */
67 | @Nonnull
68 | default Optional getServiceIdentifier()
69 | {
70 | return getServiceName().map(ServiceIdentifier::of);
71 | }
72 |
73 | /**
74 | * Returns an {@link Optional} that might contain the plan of the bound service (if it exists) of this
75 | * {@link ServiceBinding}.
76 | *
77 | * @return An {@link Optional} that might contain the plan of the bound service (if it exists) of this
78 | * {@link ServiceBinding}.
79 | */
80 | @Nonnull
81 | Optional getServicePlan();
82 |
83 | /**
84 | * Returns a {@link List} of all tags of this {@link ServiceBinding}.
85 | *
86 | * @return A {@link List} of all tags of this {@link ServiceBinding}.
87 | */
88 | @Nonnull
89 | List getTags();
90 |
91 | /**
92 | * Returna a {@link Map} of credentials that are required to connect to the bound service.
93 | *
94 | * @return A {@link Map} of credentials that are required to connect to the bound service.
95 | */
96 | @Nonnull
97 | Map getCredentials();
98 | }
99 |
--------------------------------------------------------------------------------
/api-parent/core-api/src/main/java/com/sap/cloud/environment/servicebinding/api/ServiceIdentifier.java:
--------------------------------------------------------------------------------
1 | package com.sap.cloud.environment.servicebinding.api;
2 |
3 | import javax.annotation.Nonnull;
4 | import java.util.Locale;
5 | import java.util.Map;
6 | import java.util.Optional;
7 | import java.util.concurrent.ConcurrentHashMap;
8 |
9 | /**
10 | * Represents an equatable service identifier that can be used across applications.
11 | */
12 | public final class ServiceIdentifier
13 | {
14 | @Nonnull
15 | private static final Map INSTANCES = new ConcurrentHashMap<>();
16 |
17 | /**
18 | * Represents the SAP
20 | * Extended Service for User and Account Authentication (XSUAA) .
21 | */
22 | @Nonnull
23 | public static final ServiceIdentifier XSUAA = of("xsuaa");
24 |
25 | /**
26 | * Represents the SAP Destination
27 | * Service (Cloud Foundry) .
28 | */
29 | @Nonnull
30 | public static final ServiceIdentifier DESTINATION = of("destination");
31 |
32 | /**
33 | * Represents the SAP
35 | * Connectivity Service (Cloud Foundry) . This is the proxy service that enables applications to access
36 | * On-Premise systems from the SAP Business Technology Platform (Cloud Foundry).
37 | */
38 | @Nonnull
39 | public static final ServiceIdentifier CONNECTIVITY = of("connectivity");
40 |
41 | /**
42 | * Represents the SAP Audit Log Retrieval (Cloud
43 | * Foundry environment) service.
44 | */
45 | @Nonnull
46 | public static final ServiceIdentifier AUDIT_LOG_RETRIEVAL = of("auditlog-management");
47 |
48 | /**
49 | * Represents the SAP Workflow Service for Cloud
50 | * Foundry .
51 | */
52 | @Nonnull
53 | public static final ServiceIdentifier WORKFLOW = of("workflow");
54 |
55 | /**
56 | * Represents the SAP Business Rules Service for
57 | * Cloud Foundry .
58 | */
59 | @Nonnull
60 | public static final ServiceIdentifier BUSINESS_RULES = of("business-rules");
61 |
62 | /**
63 | * Represents the SAP Identity Authentication
64 | * Service (also known as IAS ).
65 | */
66 | @Nonnull
67 | public static final ServiceIdentifier IDENTITY_AUTHENTICATION = of("identity");
68 |
69 | /**
70 | * Represents the SAP AI Core Service .
71 | */
72 | @Nonnull
73 | public static final ServiceIdentifier AI_CORE = of("aicore");
74 |
75 | /**
76 | * Returns an {@link ServiceIdentifier} based on the provided {@code id}.
77 | *
78 | * The {@code id} will be modified using {@link String#trim()} and {@link String#toLowerCase()}.
79 | *
80 | *
81 | * @param id
82 | * The identifier to use.
83 | * @return An instance of {@link ServiceIdentifier}.
84 | * @throws IllegalArgumentException
85 | * If the provided {@code id} is empty or blank after it has been modified using
86 | * {@link String#trim()}.
87 | */
88 | @Nonnull
89 | public static ServiceIdentifier of( @Nonnull final String id )
90 | {
91 | final String trimmedId = id.trim().toLowerCase(Locale.ROOT);
92 | if( trimmedId.isEmpty() ) {
93 | throw new IllegalArgumentException(String.format("The provided id ('%s') must not be empty or blank.", id));
94 | }
95 |
96 | return INSTANCES.computeIfAbsent(trimmedId, ServiceIdentifier::new);
97 | }
98 |
99 | @Nonnull
100 | private final String id;
101 |
102 | private ServiceIdentifier( @Nonnull final String id )
103 | {
104 | this.id = id;
105 | }
106 |
107 | @Override
108 | public String toString()
109 | {
110 | return id;
111 | }
112 |
113 | @Override
114 | public int hashCode()
115 | {
116 | return id.hashCode();
117 | }
118 |
119 | @Override
120 | public boolean equals( Object obj )
121 | {
122 | return obj instanceof ServiceIdentifier && id.equals(((ServiceIdentifier) obj).id);
123 | }
124 | }
125 |
--------------------------------------------------------------------------------
/api-parent/core-api/src/main/java/com/sap/cloud/environment/servicebinding/api/exception/UnsupportedPropertyTypeException.java:
--------------------------------------------------------------------------------
1 | package com.sap.cloud.environment.servicebinding.api.exception;
2 |
3 | import javax.annotation.Nonnull;
4 |
5 | /**
6 | * A {@link RuntimeException} that is thrown if an unsupported property type is encountered while initializing a
7 | * {@link com.sap.cloud.environment.servicebinding.api.ServiceBinding}.
8 | */
9 | public class UnsupportedPropertyTypeException extends RuntimeException
10 | {
11 | private static final long serialVersionUID = -880511175727343442L;
12 |
13 | /**
14 | * Initializes a new {@link UnsupportedPropertyTypeException} with the given {@code unsupportedType}.
15 | *
16 | * @param unsupportedType
17 | * The {@link Class} of the unsupported property.
18 | */
19 | public UnsupportedPropertyTypeException( @Nonnull final Class> unsupportedType )
20 | {
21 | super(String.format("The type '%s' is not supported.", unsupportedType.getName()));
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/api-parent/core-api/src/test/java/com/sap/cloud/environment/servicebinding/api/ServiceIdentifierTest.java:
--------------------------------------------------------------------------------
1 | package com.sap.cloud.environment.servicebinding.api;
2 |
3 | import org.junit.jupiter.api.Test;
4 |
5 | import static org.assertj.core.api.Assertions.assertThat;
6 | import static org.assertj.core.api.Assertions.assertThatThrownBy;
7 |
8 | class ServiceIdentifierTest
9 | {
10 | @Test
11 | void testInstancesAreCachedCaseInsensitively()
12 | {
13 | final ServiceIdentifier a = ServiceIdentifier.of("foo");
14 | final ServiceIdentifier b = ServiceIdentifier.of("FOO");
15 | final ServiceIdentifier c = ServiceIdentifier.of("bar");
16 |
17 | assertThat(a).isSameAs(b).isNotSameAs(c);
18 | }
19 |
20 | @Test
21 | void testOfTrimsIdentifier()
22 | {
23 | assertThat(ServiceIdentifier.of(" foo ")).isSameAs(ServiceIdentifier.of("foo"));
24 | assertThat(ServiceIdentifier.of(" \nbar \t")).isSameAs(ServiceIdentifier.of("bar"));
25 | assertThat(ServiceIdentifier.of(" \nb a\tz \t")).isSameAs(ServiceIdentifier.of("b a\tz"));
26 | }
27 |
28 | @Test
29 | void testOfThrowsExceptionForEmptyOrBlankId()
30 | {
31 | assertThatThrownBy(() -> ServiceIdentifier.of("")).isExactlyInstanceOf(IllegalArgumentException.class);
32 | assertThatThrownBy(() -> ServiceIdentifier.of(" ")).isExactlyInstanceOf(IllegalArgumentException.class);
33 | assertThatThrownBy(() -> ServiceIdentifier.of("\t")).isExactlyInstanceOf(IllegalArgumentException.class);
34 | assertThatThrownBy(() -> ServiceIdentifier.of("\n")).isExactlyInstanceOf(IllegalArgumentException.class);
35 | }
36 |
37 | @Test
38 | void testEquality()
39 | {
40 | assertThat(ServiceIdentifier.of("foo")).isEqualTo(ServiceIdentifier.of("foo"));
41 | assertThat(ServiceIdentifier.of("foo")).isNotEqualTo(ServiceIdentifier.of("bar"));
42 | }
43 |
44 | @Test
45 | void testHashCode()
46 | {
47 | assertThat(ServiceIdentifier.of("foo").hashCode()).isEqualTo(ServiceIdentifier.of("foo").hashCode());
48 | assertThat(ServiceIdentifier.of("foo").hashCode()).isNotEqualTo(ServiceIdentifier.of("bar").hashCode());
49 | }
50 |
51 | @Test
52 | void testToString()
53 | {
54 | assertThat(ServiceIdentifier.of("foo")).hasToString("foo");
55 | assertThat(ServiceIdentifier.of("BAR")).hasToString("bar");
56 | assertThat(ServiceIdentifier.of(" BaZ \t")).hasToString("baz");
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/api-parent/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 4.0.0
4 |
5 |
6 | com.sap.cloud.environment.servicebinding
7 | java-parent
8 | 0.21.0
9 |
10 | com.sap.cloud.environment.servicebinding.api
11 | java-api-parent
12 | pom
13 |
14 |
15 | core-api
16 | consumption-api
17 | access-api
18 |
19 |
20 |
21 | ${project.basedir}/../
22 |
23 |
24 |
--------------------------------------------------------------------------------
/sap-service-operator/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 4.0.0
4 |
5 | com.sap.cloud.environment.servicebinding
6 | java-parent
7 | 0.21.0
8 |
9 | java-sap-service-operator
10 |
11 |
12 | ${project.basedir}/../
13 |
14 |
15 |
16 |
17 |
18 | com.sap.cloud.environment.servicebinding.api
19 | java-core-api
20 |
21 |
22 | com.sap.cloud.environment.servicebinding.api
23 | java-access-api
24 |
25 |
26 | org.json
27 | json
28 |
29 |
30 | org.slf4j
31 | slf4j-api
32 |
33 |
34 |
35 |
36 | org.junit.jupiter
37 | junit-jupiter-api
38 | test
39 |
40 |
41 | org.junit.jupiter
42 | junit-jupiter
43 | test
44 |
45 |
46 | org.assertj
47 | assertj-core
48 | test
49 |
50 |
51 | org.mockito
52 | mockito-core
53 | test
54 |
55 |
56 | org.slf4j
57 | slf4j-simple
58 | test
59 |
60 |
61 |
62 |
--------------------------------------------------------------------------------
/sap-service-operator/src/main/java/com/sap/cloud/environment/servicebinding/BindingMetadata.java:
--------------------------------------------------------------------------------
1 | package com.sap.cloud.environment.servicebinding;
2 |
3 | import java.util.ArrayList;
4 | import java.util.Collection;
5 | import java.util.Collections;
6 | import java.util.List;
7 |
8 | import javax.annotation.Nonnull;
9 |
10 | class BindingMetadata
11 | {
12 | @Nonnull
13 | private final List metadataProperties;
14 |
15 | @Nonnull
16 | private final List credentialProperties;
17 |
18 | BindingMetadata(
19 | @Nonnull final Collection metadataProperties,
20 | @Nonnull final Collection credentialProperties )
21 | {
22 | this.metadataProperties = Collections.unmodifiableList(new ArrayList<>(metadataProperties));
23 | this.credentialProperties = Collections.unmodifiableList(new ArrayList<>(credentialProperties));
24 | }
25 |
26 | @Nonnull
27 | public List getMetadataProperties()
28 | {
29 | return metadataProperties;
30 | }
31 |
32 | @Nonnull
33 | public List getCredentialProperties()
34 | {
35 | return credentialProperties;
36 | }
37 |
38 | }
39 |
--------------------------------------------------------------------------------
/sap-service-operator/src/main/java/com/sap/cloud/environment/servicebinding/BindingProperty.java:
--------------------------------------------------------------------------------
1 | package com.sap.cloud.environment.servicebinding;
2 |
3 | import java.util.Objects;
4 |
5 | import javax.annotation.Nonnull;
6 |
7 | class BindingProperty
8 | {
9 | @Nonnull
10 | private final String name;
11 |
12 | @Nonnull
13 | private final String sourceName;
14 |
15 | @Nonnull
16 | private final BindingPropertyFormat format;
17 |
18 | private final boolean isContainer;
19 |
20 | @Nonnull
21 | public static BindingProperty container( @Nonnull final String name )
22 | {
23 | return container(name, name);
24 | }
25 |
26 | @Nonnull
27 | public static BindingProperty container( @Nonnull final String name, @Nonnull final String sourceName )
28 | {
29 | return new BindingProperty(name, sourceName, BindingPropertyFormat.JSON, true);
30 | }
31 |
32 | @Nonnull
33 | public static BindingProperty text( @Nonnull final String name )
34 | {
35 | return text(name, name);
36 | }
37 |
38 | @Nonnull
39 | public static BindingProperty text( @Nonnull final String name, @Nonnull final String sourceName )
40 | {
41 | return new BindingProperty(name, sourceName, BindingPropertyFormat.TEXT, false);
42 | }
43 |
44 | @Nonnull
45 | public static BindingProperty json( @Nonnull final String name )
46 | {
47 | return json(name, name);
48 | }
49 |
50 | @Nonnull
51 | public static BindingProperty json( @Nonnull final String name, @Nonnull final String sourceName )
52 | {
53 | return new BindingProperty(name, sourceName, BindingPropertyFormat.JSON, false);
54 | }
55 |
56 | BindingProperty(
57 | @Nonnull final String name,
58 | @Nonnull final String sourceName,
59 | @Nonnull final BindingPropertyFormat format,
60 | final boolean isContainer )
61 | {
62 | this.name = name;
63 | this.sourceName = sourceName;
64 | this.format = format;
65 | this.isContainer = isContainer;
66 | }
67 |
68 | @Nonnull
69 | public String getName()
70 | {
71 | return name;
72 | }
73 |
74 | @Nonnull
75 | public String getSourceName()
76 | {
77 | return sourceName;
78 | }
79 |
80 | @Nonnull
81 | public BindingPropertyFormat getFormat()
82 | {
83 | return format;
84 | }
85 |
86 | public boolean isContainer()
87 | {
88 | return isContainer;
89 | }
90 |
91 | @Override
92 | public boolean equals( final Object obj )
93 | {
94 | if( !(obj instanceof BindingProperty) ) {
95 | return false;
96 | }
97 |
98 | final BindingProperty other = (BindingProperty) obj;
99 |
100 | return getName().equals(other.getName())
101 | && getSourceName().equals(other.getSourceName())
102 | && getFormat() == other.getFormat()
103 | && isContainer() == other.isContainer();
104 | }
105 |
106 | @Override
107 | public int hashCode()
108 | {
109 | return Objects.hash(name, sourceName, format, isContainer);
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/sap-service-operator/src/main/java/com/sap/cloud/environment/servicebinding/BindingPropertyFormat.java:
--------------------------------------------------------------------------------
1 | package com.sap.cloud.environment.servicebinding;
2 |
3 | import javax.annotation.Nonnull;
4 |
5 | enum BindingPropertyFormat
6 | {
7 | TEXT("text"),
8 | JSON("json");
9 |
10 | @Nonnull
11 | private final String value;
12 |
13 | BindingPropertyFormat( @Nonnull final String value )
14 | {
15 | this.value = value;
16 | }
17 |
18 | @Nonnull
19 | public String getValue()
20 | {
21 | return value;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/sap-service-operator/src/main/java/com/sap/cloud/environment/servicebinding/LayeredParsingStrategy.java:
--------------------------------------------------------------------------------
1 | package com.sap.cloud.environment.servicebinding;
2 |
3 | import java.io.IOException;
4 | import java.nio.file.Path;
5 | import java.util.Optional;
6 |
7 | import javax.annotation.Nonnull;
8 |
9 | import com.sap.cloud.environment.servicebinding.api.ServiceBinding;
10 |
11 | /**
12 | * Represents a strategy to read a {@link ServiceBinding} from a specific layered file structure (see
13 | * {@link SapServiceOperatorLayeredServiceBindingAccessor}).
14 | */
15 | @FunctionalInterface
16 | public interface LayeredParsingStrategy
17 | {
18 | /**
19 | * Tries to initialize a new {@link ServiceBinding} instance from the files contained in the given
20 | * {@code bindingPath}.
21 | *
22 | * @param serviceName
23 | * The name of the {@link ServiceBinding} (see {@link ServiceBinding#getName()}).
24 | * @param bindingName
25 | * The name of the bound service of the {@link ServiceBinding} (see
26 | * {@link ServiceBinding#getServiceName()}).
27 | * @param bindingPath
28 | * The {@link Path} that contains the property file(s).
29 | * @return An {@link Optional} that might contain a new {@link ServiceBinding} instance if the files contained in
30 | * the given {@code bindingPath} match the expected structure.
31 | * @throws IOException
32 | * Thrown if reading property files failed.
33 | */
34 | @Nonnull
35 | Optional
36 | parse( @Nonnull final String serviceName, @Nonnull final String bindingName, @Nonnull final Path bindingPath )
37 | throws IOException;
38 | }
39 |
--------------------------------------------------------------------------------
/sap-service-operator/src/main/java/com/sap/cloud/environment/servicebinding/LayeredPropertySetter.java:
--------------------------------------------------------------------------------
1 | package com.sap.cloud.environment.servicebinding;
2 |
3 | import java.util.HashMap;
4 | import java.util.List;
5 | import java.util.Map;
6 |
7 | import javax.annotation.Nonnull;
8 |
9 | import org.json.JSONArray;
10 |
11 | @FunctionalInterface
12 | interface LayeredPropertySetter
13 | {
14 | @Nonnull
15 | String CREDENTIALS_KEY = "credentials";
16 |
17 | @Nonnull
18 | @SuppressWarnings( "unchecked" )
19 | LayeredPropertySetter TO_CREDENTIALS = ( binding, name, value ) -> {
20 | Map credentials = null;
21 | if( binding.containsKey(CREDENTIALS_KEY) ) {
22 | final Object maybeCredentials = binding.get(CREDENTIALS_KEY);
23 | if( maybeCredentials instanceof Map ) {
24 | credentials = (Map) maybeCredentials;
25 | } else {
26 | throw new IllegalStateException(
27 | String.format("The '%s' property must be of type %s.", CREDENTIALS_KEY, Map.class.getSimpleName()));
28 | }
29 | }
30 |
31 | if( credentials == null ) {
32 | credentials = new HashMap<>();
33 | binding.put(CREDENTIALS_KEY, credentials);
34 | }
35 |
36 | credentials.put(name, value);
37 | };
38 |
39 | @Nonnull
40 | LayeredPropertySetter TO_ROOT = Map::put;
41 |
42 | @Nonnull
43 | @SuppressWarnings( "unchecked" )
44 | static LayeredPropertySetter asList( @Nonnull final LayeredPropertySetter actualSetter )
45 | {
46 | return ( binding, name, value ) -> {
47 | final List list;
48 | if( value instanceof List ) {
49 | list = (List) value;
50 | } else if( value instanceof String ) {
51 | list = new JSONArray((String) value).toList();
52 | } else {
53 | throw new IllegalStateException(
54 | String
55 | .format(
56 | "The provided value '%s' cannot be converted to a %s.",
57 | value,
58 | List.class.getSimpleName()));
59 | }
60 |
61 | actualSetter.setProperty(binding, name, list);
62 | };
63 | }
64 |
65 | void setProperty(
66 | @Nonnull final Map rawServiceBinding,
67 | @Nonnull final String propertyName,
68 | @Nonnull final Object propertyValue );
69 | }
70 |
--------------------------------------------------------------------------------
/sap-service-operator/src/main/java/com/sap/cloud/environment/servicebinding/LayeredSecretKeyParsingStrategy.java:
--------------------------------------------------------------------------------
1 | package com.sap.cloud.environment.servicebinding;
2 |
3 | import java.io.IOException;
4 | import java.nio.charset.Charset;
5 | import java.nio.charset.StandardCharsets;
6 | import java.nio.file.Files;
7 | import java.nio.file.Path;
8 | import java.util.HashMap;
9 | import java.util.List;
10 | import java.util.Map;
11 | import java.util.Optional;
12 | import java.util.stream.Collectors;
13 |
14 | import javax.annotation.Nonnull;
15 |
16 | import org.json.JSONException;
17 | import org.json.JSONObject;
18 | import org.slf4j.Logger;
19 | import org.slf4j.LoggerFactory;
20 |
21 | import com.sap.cloud.environment.servicebinding.api.DefaultServiceBinding;
22 | import com.sap.cloud.environment.servicebinding.api.ServiceBinding;
23 |
24 | /**
25 | * A {@link LayeredParsingStrategy} that expects exactly one JSON file that contains the credentials. All other
26 | * properties (i.e. metadata) are expected to be contained in their own files.
27 | */
28 | public final class LayeredSecretKeyParsingStrategy implements LayeredParsingStrategy
29 | {
30 | @Nonnull
31 | private static final Logger logger = LoggerFactory.getLogger(LayeredSecretKeyParsingStrategy.class);
32 |
33 | @Nonnull
34 | private static final String PLAN_KEY = "plan";
35 |
36 | @Nonnull
37 | private final Charset charset;
38 |
39 | private LayeredSecretKeyParsingStrategy( @Nonnull final Charset charset )
40 | {
41 | this.charset = charset;
42 | }
43 |
44 | /**
45 | * Initializes a new {@link LayeredSecretKeyParsingStrategy} instance using the default configuration.
46 | *
47 | * @return A new {@link LayeredSecretKeyParsingStrategy} instance with default configuration.
48 | */
49 | @Nonnull
50 | public static LayeredSecretKeyParsingStrategy newDefault()
51 | {
52 | return new LayeredSecretKeyParsingStrategy(StandardCharsets.UTF_8);
53 | }
54 |
55 | @Nonnull
56 | @Override
57 | public
58 | Optional
59 | parse( @Nonnull final String serviceName, @Nonnull final String bindingName, @Nonnull final Path bindingPath )
60 | throws IOException
61 | {
62 | logger.debug("Trying to read service binding from '{}'.", bindingPath);
63 |
64 | final List propertyFiles =
65 | Files.list(bindingPath).filter(Files::isRegularFile).collect(Collectors.toList());
66 |
67 | if( propertyFiles.isEmpty() ) {
68 | // service binding directory must contain at least one json file
69 | logger.debug("Skipping '{}': The directory is empty.", bindingPath);
70 | return Optional.empty();
71 | }
72 |
73 | final Map rawServiceBinding = new HashMap<>();
74 | boolean credentialsFound = false;
75 | for( final Path propertyFile : propertyFiles ) {
76 | final String propertyName = propertyFile.getFileName().toString();
77 | final String fileContent = String.join("\n", Files.readAllLines(propertyFile, charset));
78 | if( fileContent.isEmpty() ) {
79 | logger.debug("Ignoring empty property file '{}'.", propertyFile);
80 | continue;
81 | }
82 |
83 | try {
84 | final Map parsedCredentials = new JSONObject(fileContent).toMap();
85 |
86 | if( credentialsFound ) {
87 | // we expect exactly one valid json object in this service binding
88 | logger.debug("Skipping '{}': More than one JSON file found.", bindingPath);
89 | return Optional.empty();
90 | }
91 |
92 | credentialsFound = true;
93 | rawServiceBinding.put(LayeredPropertySetter.CREDENTIALS_KEY, parsedCredentials);
94 | }
95 | catch( final JSONException e ) {
96 | // property is not a valid json object --> it cannot be the credentials object
97 | rawServiceBinding.put(propertyName, fileContent);
98 | }
99 | }
100 |
101 | if( !credentialsFound ) {
102 | // the service binding is expected to have credentials attached to it
103 | logger.debug("Skipping '{}': No credentials property found.", bindingPath);
104 | return Optional.empty();
105 | }
106 |
107 | final DefaultServiceBinding serviceBinding =
108 | DefaultServiceBinding
109 | .builder()
110 | .copy(rawServiceBinding)
111 | .withName(bindingName)
112 | .withServiceName(serviceName)
113 | .withServicePlanKey(PLAN_KEY)
114 | .withCredentialsKey(LayeredPropertySetter.CREDENTIALS_KEY)
115 | .build();
116 | logger.debug("Successfully read service binding from '{}'.", bindingPath);
117 | return Optional.of(serviceBinding);
118 | }
119 | }
120 |
--------------------------------------------------------------------------------
/sap-service-operator/src/main/resources/META-INF/services/com.sap.cloud.environment.servicebinding.api.ServiceBindingAccessor:
--------------------------------------------------------------------------------
1 | com.sap.cloud.environment.servicebinding.SapServiceOperatorLayeredServiceBindingAccessor
2 | com.sap.cloud.environment.servicebinding.SapServiceOperatorServiceBindingIoAccessor
--------------------------------------------------------------------------------
/sap-service-operator/src/test/java/com/sap/cloud/environment/servicebinding/BindingMetadataFactoryTest.java:
--------------------------------------------------------------------------------
1 | package com.sap.cloud.environment.servicebinding;
2 |
3 | import javax.annotation.Nonnull;
4 |
5 | import org.json.JSONException;
6 | import org.junit.jupiter.api.Test;
7 |
8 | import static org.assertj.core.api.Assertions.assertThat;
9 | import static org.assertj.core.api.Assertions.assertThatThrownBy;
10 |
11 | class BindingMetadataFactoryTest
12 | {
13 | @Nonnull
14 | private static final BindingProperty TYPE_PROPERTY = BindingProperty.text("type", "type.txt");
15 |
16 | @Nonnull
17 | private static final BindingProperty TAGS_PROPERTY = BindingProperty.json("tags", "tags.json");
18 |
19 | @Nonnull
20 | private static final BindingProperty USER_PROPERTY = BindingProperty.text("user", "user.secret");
21 |
22 | @Nonnull
23 | private static final BindingProperty PASSWORD_PROPERTY = BindingProperty.text("password", "password.secret");
24 |
25 | @Nonnull
26 | private static final BindingProperty UAA_PROPERTY = BindingProperty.container("uaa", "uaa.json");
27 |
28 | @Test
29 | void fromJsonWithFlatMetadata()
30 | {
31 | final String rawMetadata = TestResource.read(BindingMetadataFactoryTest.class, "flat_metadata.json");
32 |
33 | final BindingMetadata sut = BindingMetadataFactory.getFromJson(rawMetadata);
34 |
35 | assertThat(sut).isNotNull();
36 | assertThat(sut.getMetadataProperties().size()).isEqualTo(2);
37 | assertThat(sut.getCredentialProperties().size()).isEqualTo(2);
38 |
39 | assertThat(sut.getMetadataProperties()).containsExactlyInAnyOrder(TYPE_PROPERTY, TAGS_PROPERTY);
40 | assertThat(sut.getCredentialProperties()).containsExactlyInAnyOrder(USER_PROPERTY, PASSWORD_PROPERTY);
41 | }
42 |
43 | @Test
44 | void fromJsonWithContainerMetadata()
45 | {
46 | final String rawMetadata = TestResource.read(BindingMetadataFactoryTest.class, "container_metadata.json");
47 |
48 | final BindingMetadata sut = BindingMetadataFactory.getFromJson(rawMetadata);
49 |
50 | assertThat(sut).isNotNull();
51 | assertThat(sut.getMetadataProperties().size()).isEqualTo(2);
52 | assertThat(sut.getCredentialProperties().size()).isEqualTo(1);
53 |
54 | assertThat(sut.getMetadataProperties()).containsExactlyInAnyOrder(TYPE_PROPERTY, TAGS_PROPERTY);
55 | assertThat(sut.getCredentialProperties()).containsExactlyInAnyOrder(UAA_PROPERTY);
56 | }
57 |
58 | @Test
59 | void fromJsonWithMultipleContainerMetadata()
60 | {
61 | final String rawMetadata = TestResource.read(BindingMetadataFactoryTest.class, "multi_container_metadata.json");
62 |
63 | final BindingMetadata sut = BindingMetadataFactory.getFromJson(rawMetadata);
64 |
65 | assertThat(sut).isNotNull();
66 | assertThat(sut.getMetadataProperties().size()).isEqualTo(2);
67 | assertThat(sut.getCredentialProperties().size()).isEqualTo(2);
68 |
69 | assertThat(sut.getMetadataProperties()).containsExactlyInAnyOrder(TYPE_PROPERTY, TAGS_PROPERTY);
70 | assertThat(sut.getCredentialProperties())
71 | .containsExactlyInAnyOrder(
72 | BindingProperty.container("container1"),
73 | BindingProperty.container("container2"));
74 | }
75 |
76 | @Test
77 | void fromJsonWithInvalidInputThrowsIllegalArgumentException()
78 | {
79 | assertThatThrownBy(() -> BindingMetadataFactory.getFromJson("this is not a valid JSON object"))
80 | .isExactlyInstanceOf(IllegalArgumentException.class)
81 | .hasCauseExactlyInstanceOf(JSONException.class);
82 | }
83 |
84 | @Test
85 | void fromJsonWithEmptyInputLeadsToEmptyMetadata()
86 | {
87 | final BindingMetadata sut = BindingMetadataFactory.getFromJson("{}");
88 |
89 | assertThat(sut).isNotNull();
90 | assertThat(sut.getMetadataProperties()).isEmpty();
91 | assertThat(sut.getCredentialProperties()).isEmpty();
92 | }
93 |
94 | @Test
95 | void fromJsonIgnoresPropertiesWithMissingFields()
96 | {
97 | final String rawMetadata = TestResource.read(BindingMetadataFactoryTest.class, "malformed_metadata.json");
98 |
99 | final BindingMetadata sut = BindingMetadataFactory.getFromJson(rawMetadata);
100 |
101 | assertThat(sut).isNotNull();
102 | assertThat(sut.getMetadataProperties().size()).isEqualTo(1);
103 | assertThat(sut.getCredentialProperties().size()).isEqualTo(1);
104 |
105 | assertThat(sut.getMetadataProperties()).containsExactlyInAnyOrder(TYPE_PROPERTY);
106 | assertThat(sut.getCredentialProperties()).containsExactlyInAnyOrder(USER_PROPERTY);
107 | }
108 | }
109 |
--------------------------------------------------------------------------------
/sap-service-operator/src/test/java/com/sap/cloud/environment/servicebinding/LayeredDataParsingStrategyTest.java:
--------------------------------------------------------------------------------
1 | package com.sap.cloud.environment.servicebinding;
2 |
3 | import java.io.IOException;
4 | import java.nio.file.Path;
5 | import java.util.List;
6 | import java.util.Optional;
7 |
8 | import org.junit.jupiter.api.Test;
9 |
10 | import com.sap.cloud.environment.servicebinding.api.ServiceBinding;
11 |
12 | import static org.assertj.core.api.Assertions.assertThat;
13 |
14 | class LayeredDataParsingStrategyTest
15 | {
16 | @Test
17 | @SuppressWarnings( "unchecked" )
18 | void parseValidBinding()
19 | throws IOException
20 | {
21 | final Path path = TestResource.get(LayeredDataParsingStrategyTest.class, "ValidBinding");
22 |
23 | final LayeredDataParsingStrategy sut = LayeredDataParsingStrategy.newDefault();
24 |
25 | final ServiceBinding serviceBinding = sut.parse("XSUAA", "my-xsuaa-binding", path).orElse(null);
26 |
27 | assertThat(serviceBinding).isNotNull();
28 |
29 | assertThat(serviceBinding.getKeys())
30 | .containsExactlyInAnyOrder("instance_guid", "instance_name", "label", "plan", "tags", "credentials");
31 |
32 | assertThat(serviceBinding.getName()).hasValue("my-xsuaa-binding");
33 | assertThat(serviceBinding.getServiceName()).hasValue("XSUAA");
34 | assertThat(serviceBinding.getServicePlan()).hasValue("my-plan");
35 | assertThat(serviceBinding.getTags()).containsExactly("my-tag-1", "my-tag-2");
36 |
37 | assertThat(serviceBinding.getCredentials()).containsOnlyKeys("domains", "clientid", "clientsecret");
38 | assertThat((List) serviceBinding.getCredentials().get("domains"))
39 | .containsExactly("my-domain-1", "my-domain-2");
40 | assertThat(serviceBinding.getCredentials()).containsEntry("clientid", "my-clientid");
41 | assertThat(serviceBinding.getCredentials()).containsEntry("clientsecret", "my-clientsecret");
42 | }
43 |
44 | @Test
45 | void parseWithoutCredentialsLeadsToEmptyResult()
46 | throws IOException
47 | {
48 | final Path path = TestResource.get(LayeredDataParsingStrategyTest.class, "NoCredentials");
49 |
50 | final LayeredDataParsingStrategy sut = LayeredDataParsingStrategy.newDefault();
51 |
52 | final Optional serviceBinding;
53 | serviceBinding = sut.parse("XSUAA", "my-xsuaa-binding", path);
54 |
55 | assertThat(serviceBinding.isPresent()).isFalse();
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/sap-service-operator/src/test/java/com/sap/cloud/environment/servicebinding/LayeredSecretKeyParsingStrategyTest.java:
--------------------------------------------------------------------------------
1 | package com.sap.cloud.environment.servicebinding;
2 |
3 | import java.io.IOException;
4 | import java.nio.file.Path;
5 | import java.util.List;
6 | import java.util.Optional;
7 |
8 | import org.junit.jupiter.api.Test;
9 |
10 | import com.sap.cloud.environment.servicebinding.api.ServiceBinding;
11 |
12 | import static org.assertj.core.api.Assertions.assertThat;
13 |
14 | class LayeredSecretKeyParsingStrategyTest
15 | {
16 | @Test
17 | @SuppressWarnings( "unchecked" )
18 | void parseValidBinding()
19 | throws IOException
20 | {
21 | final Path path = TestResource.get(LayeredSecretKeyParsingStrategyTest.class, "ValidBinding");
22 |
23 | final LayeredSecretKeyParsingStrategy sut = LayeredSecretKeyParsingStrategy.newDefault();
24 |
25 | final ServiceBinding serviceBinding = sut.parse("XSUAA", "my-xsuaa-binding", path).orElse(null);
26 |
27 | assertThat(serviceBinding).isNotNull();
28 | assertThat(serviceBinding.getName()).hasValue("my-xsuaa-binding");
29 | assertThat(serviceBinding.getServiceName()).hasValue("XSUAA");
30 | assertThat(serviceBinding.getServicePlan()).hasValue("my-plan");
31 | assertThat(serviceBinding.getTags()).isEmpty();
32 |
33 | assertThat(serviceBinding.getCredentials())
34 | .containsOnlyKeys("domain", "domains", "clientid", "clientsecret", "url", "zone_uuid");
35 | assertThat(serviceBinding.getCredentials()).containsEntry("domain", "my-trusted-domain");
36 | assertThat(serviceBinding.getCredentials().get("domains")).isInstanceOf(List.class);
37 | assertThat((List) serviceBinding.getCredentials().get("domains")).containsExactly("my-trusted-domain");
38 | assertThat(serviceBinding.getCredentials()).containsEntry("clientid", "my-client-id");
39 | assertThat(serviceBinding.getCredentials()).containsEntry("clientsecret", "my-client-secret");
40 | assertThat(serviceBinding.getCredentials()).containsEntry("url", "https://my-trusted-domain.com");
41 | assertThat(serviceBinding.getCredentials()).containsEntry("zone_uuid", "my-zone-uuid");
42 | }
43 |
44 | @Test
45 | void parsingTwoJsonFilesLeadsEmptyResult()
46 | throws IOException
47 | {
48 | final Path path = TestResource.get(LayeredSecretKeyParsingStrategyTest.class, "TwoJsonFiles");
49 |
50 | final LayeredSecretKeyParsingStrategy sut = LayeredSecretKeyParsingStrategy.newDefault();
51 |
52 | final Optional serviceBinding = sut.parse("XSUAA", "my-xsuaa-binding", path);
53 |
54 | assertThat(serviceBinding.isPresent()).isFalse();
55 | }
56 |
57 | @Test
58 | void parsingNoJsonFileLeadsEmptyResult()
59 | throws IOException
60 | {
61 | final Path path = TestResource.get(LayeredSecretKeyParsingStrategyTest.class, "NoJsonFile");
62 |
63 | final LayeredSecretKeyParsingStrategy sut = LayeredSecretKeyParsingStrategy.newDefault();
64 |
65 | final Optional serviceBinding = sut.parse("XSUAA", "my-xsuaa-binding", path);
66 |
67 | assertThat(serviceBinding.isPresent()).isFalse();
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/sap-service-operator/src/test/java/com/sap/cloud/environment/servicebinding/LayeredSecretRootKeyParsingStrategyTest.java:
--------------------------------------------------------------------------------
1 | package com.sap.cloud.environment.servicebinding;
2 |
3 | import java.io.IOException;
4 | import java.nio.file.Path;
5 | import java.util.Optional;
6 |
7 | import org.junit.jupiter.api.Test;
8 |
9 | import com.sap.cloud.environment.servicebinding.api.ServiceBinding;
10 |
11 | import static org.assertj.core.api.Assertions.assertThat;
12 |
13 | class LayeredSecretRootKeyParsingStrategyTest
14 | {
15 | @Test
16 | void multipleFilesLeadToEmptyResult()
17 | throws IOException
18 | {
19 | final Path path = TestResource.get(LayeredSecretRootKeyParsingStrategyTest.class, "MultipleFiles");
20 |
21 | final LayeredSecretRootKeyParsingStrategy sut = LayeredSecretRootKeyParsingStrategy.newDefault();
22 |
23 | final Optional serviceBinding = sut.parse("service", "binding", path);
24 |
25 | assertThat(serviceBinding.isPresent()).isFalse();
26 | }
27 |
28 | @Test
29 | void fileWithoutJsonLeadsToEmptyResult()
30 | throws IOException
31 | {
32 | final Path path = TestResource.get(LayeredSecretRootKeyParsingStrategyTest.class, "NotAJsonFile");
33 |
34 | final LayeredSecretRootKeyParsingStrategy sut = LayeredSecretRootKeyParsingStrategy.newDefault();
35 |
36 | final Optional serviceBinding = sut.parse("service", "binding", path);
37 |
38 | assertThat(serviceBinding.isPresent()).isFalse();
39 | }
40 |
41 | @Test
42 | void parseValidBinding()
43 | throws IOException
44 | {
45 | final Path path = TestResource.get(LayeredSecretRootKeyParsingStrategyTest.class, "ValidBinding");
46 |
47 | final LayeredSecretRootKeyParsingStrategy sut = LayeredSecretRootKeyParsingStrategy.newDefault();
48 |
49 | final ServiceBinding serviceBinding = sut.parse("XSUAA", "my-xsuaa-binding", path).orElse(null);
50 |
51 | assertThat(serviceBinding).isNotNull();
52 | assertThat(serviceBinding.getName()).hasValue("my-xsuaa-binding");
53 | assertThat(serviceBinding.getServiceName()).hasValue("XSUAA");
54 | assertThat(serviceBinding.getServicePlan()).hasValue("lite");
55 | assertThat(serviceBinding.getTags()).containsExactly("tag1", "tag2");
56 | assertThat(serviceBinding.getCredentials()).containsKeys("clientid", "clientsecret");
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/sap-service-operator/src/test/java/com/sap/cloud/environment/servicebinding/TestResource.java:
--------------------------------------------------------------------------------
1 | package com.sap.cloud.environment.servicebinding;
2 |
3 | import java.io.IOException;
4 | import java.net.URISyntaxException;
5 | import java.net.URL;
6 | import java.nio.charset.StandardCharsets;
7 | import java.nio.file.Files;
8 | import java.nio.file.Path;
9 | import java.nio.file.Paths;
10 |
11 | import javax.annotation.Nonnull;
12 |
13 | public final class TestResource
14 | {
15 | private TestResource()
16 | {
17 | throw new IllegalStateException("This utility class must not be initialized.");
18 | }
19 |
20 | @Nonnull
21 | public static String read( @Nonnull final Class> testClass, @Nonnull final String fileName )
22 | {
23 | final Path path = get(testClass, fileName);
24 | try {
25 | return String.join("\n", Files.readAllLines(path, StandardCharsets.UTF_8));
26 | }
27 | catch( final IOException e ) {
28 | throw new AssertionError(
29 | String.format("Unable to read test resource from '%s/%s'", testClass.getSimpleName(), fileName),
30 | e);
31 | }
32 | }
33 |
34 | @Nonnull
35 | public static Path get( @Nonnull final Class> testClass, @Nonnull final String fileName )
36 | {
37 | return Paths.get(get(testClass).toString(), fileName);
38 | }
39 |
40 | @Nonnull
41 | public static Path get( @Nonnull final Class> testClass )
42 | {
43 | final URL url = testClass.getClassLoader().getResource(testClass.getSimpleName());
44 | if( url == null ) {
45 | throw new AssertionError(
46 | String.format("Unable to load test resource directory '%s'", testClass.getSimpleName()));
47 | }
48 |
49 | try {
50 | return Paths.get(url.toURI());
51 | }
52 | catch( final URISyntaxException e ) {
53 | throw new AssertionError(
54 | String.format("Unable to load test resource directory '%s'", testClass.getSimpleName()),
55 | e);
56 | }
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/sap-service-operator/src/test/resources/BindingMetadataFactoryTest/container_metadata.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://sap.com/schemas/service-bindings",
3 | "metaDataProperties": [
4 | {
5 | "name": "type",
6 | "sourceName": "type.txt",
7 | "format": "text"
8 | },
9 | {
10 | "name": "tags",
11 | "sourceName": "tags.json",
12 | "format": "json"
13 | }
14 | ],
15 | "credentialProperties": [
16 | {
17 | "name": "uaa",
18 | "sourceName": "uaa.json",
19 | "format": "json",
20 | "container": true
21 | }
22 | ]
23 | }
24 |
--------------------------------------------------------------------------------
/sap-service-operator/src/test/resources/BindingMetadataFactoryTest/flat_metadata.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://sap.com/schemas/service-bindings",
3 | "metaDataProperties": [
4 | {
5 | "name": "type",
6 | "sourceName": "type.txt",
7 | "format": "text"
8 | },
9 | {
10 | "name": "tags",
11 | "sourceName": "tags.json",
12 | "format": "json"
13 | }
14 | ],
15 | "credentialProperties": [
16 | {
17 | "name": "user",
18 | "sourceName": "user.secret",
19 | "format": "text"
20 | },
21 | {
22 | "name": "password",
23 | "sourceName": "password.secret",
24 | "format": "text"
25 | }
26 | ]
27 | }
28 |
--------------------------------------------------------------------------------
/sap-service-operator/src/test/resources/BindingMetadataFactoryTest/malformed_metadata.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://sap.com/schemas/service-bindings",
3 | "metaDataProperties": [
4 | {
5 | "name": "this property has no 'format' field"
6 | },
7 | {
8 | "format": "this property has no 'name' field"
9 | },
10 | {
11 | "name": "type",
12 | "sourceName": "type.txt",
13 | "format": "text"
14 | }
15 | ],
16 | "credentialProperties": [
17 | {
18 | "name": "this property has no 'format' field"
19 | },
20 | {
21 | "format": "this property has no 'name' field"
22 | },
23 | {
24 | "name": "user",
25 | "sourceName": "user.secret",
26 | "format": "text"
27 | }
28 | ]
29 | }
--------------------------------------------------------------------------------
/sap-service-operator/src/test/resources/BindingMetadataFactoryTest/multi_container_metadata.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://sap.com/schemas/service-bindings",
3 | "metaDataProperties": [
4 | {
5 | "name": "type",
6 | "sourceName": "type.txt",
7 | "format": "text"
8 | },
9 | {
10 | "name": "tags",
11 | "sourceName": "tags.json",
12 | "format": "json"
13 | }
14 | ],
15 | "credentialProperties": [
16 | {
17 | "name": "container1",
18 | "format": "json",
19 | "container": true
20 | },
21 | {
22 | "name": "container2",
23 | "format": "json",
24 | "container": true
25 | }
26 | ]
27 | }
28 |
--------------------------------------------------------------------------------
/sap-service-operator/src/test/resources/LayeredDataParsingStrategyTest/NoCredentials/instance_guid:
--------------------------------------------------------------------------------
1 | my-instance-guid
--------------------------------------------------------------------------------
/sap-service-operator/src/test/resources/LayeredDataParsingStrategyTest/NoCredentials/instance_name:
--------------------------------------------------------------------------------
1 | my-instance-name
--------------------------------------------------------------------------------
/sap-service-operator/src/test/resources/LayeredDataParsingStrategyTest/NoCredentials/label:
--------------------------------------------------------------------------------
1 | my-label
--------------------------------------------------------------------------------
/sap-service-operator/src/test/resources/LayeredDataParsingStrategyTest/NoCredentials/plan:
--------------------------------------------------------------------------------
1 | my-plan
--------------------------------------------------------------------------------
/sap-service-operator/src/test/resources/LayeredDataParsingStrategyTest/NoCredentials/tags:
--------------------------------------------------------------------------------
1 | ["my-tag-1", "my-tag-2"]
--------------------------------------------------------------------------------
/sap-service-operator/src/test/resources/LayeredDataParsingStrategyTest/ValidBinding/clientid:
--------------------------------------------------------------------------------
1 | my-clientid
--------------------------------------------------------------------------------
/sap-service-operator/src/test/resources/LayeredDataParsingStrategyTest/ValidBinding/clientsecret:
--------------------------------------------------------------------------------
1 | my-clientsecret
--------------------------------------------------------------------------------
/sap-service-operator/src/test/resources/LayeredDataParsingStrategyTest/ValidBinding/domains:
--------------------------------------------------------------------------------
1 | ["my-domain-1", "my-domain-2"]
--------------------------------------------------------------------------------
/sap-service-operator/src/test/resources/LayeredDataParsingStrategyTest/ValidBinding/instance_guid:
--------------------------------------------------------------------------------
1 | my-instance-guid
--------------------------------------------------------------------------------
/sap-service-operator/src/test/resources/LayeredDataParsingStrategyTest/ValidBinding/instance_name:
--------------------------------------------------------------------------------
1 | my-instance-name
--------------------------------------------------------------------------------
/sap-service-operator/src/test/resources/LayeredDataParsingStrategyTest/ValidBinding/label:
--------------------------------------------------------------------------------
1 | my-label
--------------------------------------------------------------------------------
/sap-service-operator/src/test/resources/LayeredDataParsingStrategyTest/ValidBinding/plan:
--------------------------------------------------------------------------------
1 | my-plan
--------------------------------------------------------------------------------
/sap-service-operator/src/test/resources/LayeredDataParsingStrategyTest/ValidBinding/tags:
--------------------------------------------------------------------------------
1 | ["my-tag-1", "my-tag-2"]
--------------------------------------------------------------------------------
/sap-service-operator/src/test/resources/LayeredSecretKeyParsingStrategyTest/NoJsonFile/file1:
--------------------------------------------------------------------------------
1 | this is just plain text
--------------------------------------------------------------------------------
/sap-service-operator/src/test/resources/LayeredSecretKeyParsingStrategyTest/TwoJsonFiles/file1.json:
--------------------------------------------------------------------------------
1 | {
2 | "key1": "value1"
3 | }
--------------------------------------------------------------------------------
/sap-service-operator/src/test/resources/LayeredSecretKeyParsingStrategyTest/TwoJsonFiles/file2.json:
--------------------------------------------------------------------------------
1 | {
2 | "key2": "value2"
3 | }
--------------------------------------------------------------------------------
/sap-service-operator/src/test/resources/LayeredSecretKeyParsingStrategyTest/ValidBinding/credentials.json:
--------------------------------------------------------------------------------
1 | {
2 | "domain": "my-trusted-domain",
3 | "domains": [
4 | "my-trusted-domain"
5 | ],
6 | "clientid": "my-client-id",
7 | "clientsecret": "my-client-secret",
8 | "url": "https://my-trusted-domain.com",
9 | "zone_uuid": "my-zone-uuid"
10 | }
--------------------------------------------------------------------------------
/sap-service-operator/src/test/resources/LayeredSecretKeyParsingStrategyTest/ValidBinding/instance_guid:
--------------------------------------------------------------------------------
1 | my-instance-guid
--------------------------------------------------------------------------------
/sap-service-operator/src/test/resources/LayeredSecretKeyParsingStrategyTest/ValidBinding/instance_name:
--------------------------------------------------------------------------------
1 | my-instance-name
--------------------------------------------------------------------------------
/sap-service-operator/src/test/resources/LayeredSecretKeyParsingStrategyTest/ValidBinding/label:
--------------------------------------------------------------------------------
1 | my-label
--------------------------------------------------------------------------------
/sap-service-operator/src/test/resources/LayeredSecretKeyParsingStrategyTest/ValidBinding/plan:
--------------------------------------------------------------------------------
1 | my-plan
--------------------------------------------------------------------------------
/sap-service-operator/src/test/resources/LayeredSecretRootKeyParsingStrategyTest/MultipleFiles/file1:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SAP/btp-environment-variable-access/41d0e1f3bdb0307819a38131a1e79c9da45ef756/sap-service-operator/src/test/resources/LayeredSecretRootKeyParsingStrategyTest/MultipleFiles/file1
--------------------------------------------------------------------------------
/sap-service-operator/src/test/resources/LayeredSecretRootKeyParsingStrategyTest/MultipleFiles/file2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SAP/btp-environment-variable-access/41d0e1f3bdb0307819a38131a1e79c9da45ef756/sap-service-operator/src/test/resources/LayeredSecretRootKeyParsingStrategyTest/MultipleFiles/file2
--------------------------------------------------------------------------------
/sap-service-operator/src/test/resources/LayeredSecretRootKeyParsingStrategyTest/NotAJsonFile/file:
--------------------------------------------------------------------------------
1 | this is plain text
--------------------------------------------------------------------------------
/sap-service-operator/src/test/resources/LayeredSecretRootKeyParsingStrategyTest/ValidBinding/binding.json:
--------------------------------------------------------------------------------
1 | {
2 | "plan": "lite",
3 | "tags": [
4 | "tag1",
5 | "tag2"
6 | ],
7 | "clientid": "my-client-id",
8 | "clientsecret": "my-client-secret"
9 | }
--------------------------------------------------------------------------------
/sap-service-operator/src/test/resources/SapServiceOperatorLayeredServiceBindingAccessorTest/InvalidBinding/xsuaa/data-binding/instance_guid:
--------------------------------------------------------------------------------
1 | data-xsuaa-instance-guid
--------------------------------------------------------------------------------
/sap-service-operator/src/test/resources/SapServiceOperatorLayeredServiceBindingAccessorTest/InvalidBinding/xsuaa/data-binding/instance_name:
--------------------------------------------------------------------------------
1 | data-xsuaa-instance-name
--------------------------------------------------------------------------------
/sap-service-operator/src/test/resources/SapServiceOperatorLayeredServiceBindingAccessorTest/InvalidBinding/xsuaa/data-binding/label:
--------------------------------------------------------------------------------
1 | data-xsuaa-label
--------------------------------------------------------------------------------
/sap-service-operator/src/test/resources/SapServiceOperatorLayeredServiceBindingAccessorTest/InvalidBinding/xsuaa/data-binding/plan:
--------------------------------------------------------------------------------
1 | data-xsuaa-plan
--------------------------------------------------------------------------------
/sap-service-operator/src/test/resources/SapServiceOperatorLayeredServiceBindingAccessorTest/InvalidBinding/xsuaa/data-binding/tags:
--------------------------------------------------------------------------------
1 | ["data-xsuaa-tag-1", "data-xsuaa-tag-2"]
--------------------------------------------------------------------------------
/sap-service-operator/src/test/resources/SapServiceOperatorLayeredServiceBindingAccessorTest/InvalidBinding/xsuaa/secret-key-binding/credentials.json:
--------------------------------------------------------------------------------
1 | {
2 | "domain": "secret-key-xsuaa-domain",
3 | "domains": [
4 | "secret-key-xsuaa-domain-1",
5 | "secret-key-xsuaa-domain-2"
6 | ],
7 | "clientid": "secret-key-xsuaa-clientid",
8 | "clientsecret": "secret-key-xsuaa-clientsecret",
9 | "url": "https://secret-key-xsuaa-domain.com",
10 | "zone_uuid": "secret-key-xsuaa-zone-uuid"
11 | }
--------------------------------------------------------------------------------
/sap-service-operator/src/test/resources/SapServiceOperatorLayeredServiceBindingAccessorTest/InvalidBinding/xsuaa/secret-key-binding/instance_guid:
--------------------------------------------------------------------------------
1 | secret-key-xsuaa-instance-guid
--------------------------------------------------------------------------------
/sap-service-operator/src/test/resources/SapServiceOperatorLayeredServiceBindingAccessorTest/InvalidBinding/xsuaa/secret-key-binding/instance_name:
--------------------------------------------------------------------------------
1 | secret-key-xsuaa-instance-name
--------------------------------------------------------------------------------
/sap-service-operator/src/test/resources/SapServiceOperatorLayeredServiceBindingAccessorTest/InvalidBinding/xsuaa/secret-key-binding/label:
--------------------------------------------------------------------------------
1 | secret-key-xsuaa-label
--------------------------------------------------------------------------------
/sap-service-operator/src/test/resources/SapServiceOperatorLayeredServiceBindingAccessorTest/InvalidBinding/xsuaa/secret-key-binding/plan:
--------------------------------------------------------------------------------
1 | secret-key-xsuaa-plan
--------------------------------------------------------------------------------
/sap-service-operator/src/test/resources/SapServiceOperatorLayeredServiceBindingAccessorTest/InvalidBinding/xsuaa/secret-root-key-binding/binding.json:
--------------------------------------------------------------------------------
1 | {
2 | "plan": "secret-root-key-xsuaa-plan",
3 | "tags": [
4 | "secret-root-key-xsuaa-tag-1",
5 | "secret-root-key-xsuaa-tag-2"
6 | ],
7 | "clientid": "secret-root-key-xsuaa-clientid",
8 | "clientsecret": "secret-root-key-xsuaa-clientsecret"
9 | }
--------------------------------------------------------------------------------
/sap-service-operator/src/test/resources/SapServiceOperatorLayeredServiceBindingAccessorTest/MixedBindings/xsuaa/data-binding/clientid:
--------------------------------------------------------------------------------
1 | data-xsuaa-clientid
--------------------------------------------------------------------------------
/sap-service-operator/src/test/resources/SapServiceOperatorLayeredServiceBindingAccessorTest/MixedBindings/xsuaa/data-binding/clientsecret:
--------------------------------------------------------------------------------
1 | data-xsuaa-clientsecret
--------------------------------------------------------------------------------
/sap-service-operator/src/test/resources/SapServiceOperatorLayeredServiceBindingAccessorTest/MixedBindings/xsuaa/data-binding/domains:
--------------------------------------------------------------------------------
1 | ["data-xsuaa-domain-1", "data-xsuaa-domain-2"]
--------------------------------------------------------------------------------
/sap-service-operator/src/test/resources/SapServiceOperatorLayeredServiceBindingAccessorTest/MixedBindings/xsuaa/data-binding/instance_guid:
--------------------------------------------------------------------------------
1 | data-xsuaa-instance-guid
--------------------------------------------------------------------------------
/sap-service-operator/src/test/resources/SapServiceOperatorLayeredServiceBindingAccessorTest/MixedBindings/xsuaa/data-binding/instance_name:
--------------------------------------------------------------------------------
1 | data-xsuaa-instance-name
--------------------------------------------------------------------------------
/sap-service-operator/src/test/resources/SapServiceOperatorLayeredServiceBindingAccessorTest/MixedBindings/xsuaa/data-binding/label:
--------------------------------------------------------------------------------
1 | data-xsuaa-label
--------------------------------------------------------------------------------
/sap-service-operator/src/test/resources/SapServiceOperatorLayeredServiceBindingAccessorTest/MixedBindings/xsuaa/data-binding/plan:
--------------------------------------------------------------------------------
1 | data-xsuaa-plan
--------------------------------------------------------------------------------
/sap-service-operator/src/test/resources/SapServiceOperatorLayeredServiceBindingAccessorTest/MixedBindings/xsuaa/data-binding/tags:
--------------------------------------------------------------------------------
1 | ["data-xsuaa-tag-1", "data-xsuaa-tag-2"]
--------------------------------------------------------------------------------
/sap-service-operator/src/test/resources/SapServiceOperatorLayeredServiceBindingAccessorTest/MixedBindings/xsuaa/secret-key-binding/credentials.json:
--------------------------------------------------------------------------------
1 | {
2 | "domain": "secret-key-xsuaa-domain",
3 | "domains": [
4 | "secret-key-xsuaa-domain-1",
5 | "secret-key-xsuaa-domain-2"
6 | ],
7 | "clientid": "secret-key-xsuaa-clientid",
8 | "clientsecret": "secret-key-xsuaa-clientsecret",
9 | "url": "https://secret-key-xsuaa-domain.com",
10 | "zone_uuid": "secret-key-xsuaa-zone-uuid"
11 | }
--------------------------------------------------------------------------------
/sap-service-operator/src/test/resources/SapServiceOperatorLayeredServiceBindingAccessorTest/MixedBindings/xsuaa/secret-key-binding/instance_guid:
--------------------------------------------------------------------------------
1 | secret-key-xsuaa-instance-guid
--------------------------------------------------------------------------------
/sap-service-operator/src/test/resources/SapServiceOperatorLayeredServiceBindingAccessorTest/MixedBindings/xsuaa/secret-key-binding/instance_name:
--------------------------------------------------------------------------------
1 | secret-key-xsuaa-instance-name
--------------------------------------------------------------------------------
/sap-service-operator/src/test/resources/SapServiceOperatorLayeredServiceBindingAccessorTest/MixedBindings/xsuaa/secret-key-binding/label:
--------------------------------------------------------------------------------
1 | secret-key-xsuaa-label
--------------------------------------------------------------------------------
/sap-service-operator/src/test/resources/SapServiceOperatorLayeredServiceBindingAccessorTest/MixedBindings/xsuaa/secret-key-binding/plan:
--------------------------------------------------------------------------------
1 | secret-key-xsuaa-plan
--------------------------------------------------------------------------------
/sap-service-operator/src/test/resources/SapServiceOperatorLayeredServiceBindingAccessorTest/MixedBindings/xsuaa/secret-root-key-binding/binding.json:
--------------------------------------------------------------------------------
1 | {
2 | "plan": "secret-root-key-xsuaa-plan",
3 | "tags": [
4 | "secret-root-key-xsuaa-tag-1",
5 | "secret-root-key-xsuaa-tag-2"
6 | ],
7 | "clientid": "secret-root-key-xsuaa-clientid",
8 | "clientsecret": "secret-root-key-xsuaa-clientsecret"
9 | }
--------------------------------------------------------------------------------
/sap-service-operator/src/test/resources/SapServiceOperatorLayeredServiceBindingAccessorTest/WithMetadataFileStructure/data-xsuaa-binding/.metadata:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://sap.com/schemas/service-bindings",
3 | "metaDataProperties": [
4 | {
5 | "name": "type",
6 | "sourceName": "label",
7 | "format": "text"
8 | },
9 | {
10 | "name": "tags",
11 | "format": "json"
12 | },
13 | {
14 | "name": "plan",
15 | "format": "text"
16 | },
17 | {
18 | "name": "instance_guid",
19 | "format": "text"
20 | },
21 | {
22 | "name": "instance_name",
23 | "format": "text"
24 | }
25 | ],
26 | "credentialProperties": [
27 | {
28 | "name": "clientid",
29 | "format": "text"
30 | },
31 | {
32 | "name": "clientsecret",
33 | "format": "text"
34 | },
35 | {
36 | "name": "domains",
37 | "format": "json"
38 | },
39 | {
40 | "name": "uaa",
41 | "format": "json"
42 | }
43 | ]
44 | }
45 |
--------------------------------------------------------------------------------
/sap-service-operator/src/test/resources/SapServiceOperatorLayeredServiceBindingAccessorTest/WithMetadataFileStructure/data-xsuaa-binding/clientid:
--------------------------------------------------------------------------------
1 | data-xsuaa-clientid
--------------------------------------------------------------------------------
/sap-service-operator/src/test/resources/SapServiceOperatorLayeredServiceBindingAccessorTest/WithMetadataFileStructure/data-xsuaa-binding/clientsecret:
--------------------------------------------------------------------------------
1 | data-xsuaa-clientsecret
--------------------------------------------------------------------------------
/sap-service-operator/src/test/resources/SapServiceOperatorLayeredServiceBindingAccessorTest/WithMetadataFileStructure/data-xsuaa-binding/domains:
--------------------------------------------------------------------------------
1 | ["data-xsuaa-domain-1", "data-xsuaa-domain-2"]
--------------------------------------------------------------------------------
/sap-service-operator/src/test/resources/SapServiceOperatorLayeredServiceBindingAccessorTest/WithMetadataFileStructure/data-xsuaa-binding/instance_guid:
--------------------------------------------------------------------------------
1 | data-xsuaa-instance-guid
--------------------------------------------------------------------------------
/sap-service-operator/src/test/resources/SapServiceOperatorLayeredServiceBindingAccessorTest/WithMetadataFileStructure/data-xsuaa-binding/instance_name:
--------------------------------------------------------------------------------
1 | data-xsuaa-instance-name
--------------------------------------------------------------------------------
/sap-service-operator/src/test/resources/SapServiceOperatorLayeredServiceBindingAccessorTest/WithMetadataFileStructure/data-xsuaa-binding/label:
--------------------------------------------------------------------------------
1 | xsuaa
--------------------------------------------------------------------------------
/sap-service-operator/src/test/resources/SapServiceOperatorLayeredServiceBindingAccessorTest/WithMetadataFileStructure/data-xsuaa-binding/plan:
--------------------------------------------------------------------------------
1 | application
--------------------------------------------------------------------------------
/sap-service-operator/src/test/resources/SapServiceOperatorLayeredServiceBindingAccessorTest/WithMetadataFileStructure/data-xsuaa-binding/tags:
--------------------------------------------------------------------------------
1 | ["data-xsuaa-tag-1", "data-xsuaa-tag-2"]
--------------------------------------------------------------------------------
/sap-service-operator/src/test/resources/SapServiceOperatorLayeredServiceBindingAccessorTest/WithMetadataFileStructure/data-xsuaa-binding/uaa:
--------------------------------------------------------------------------------
1 | {
2 | "text": "text",
3 | "number": 13.37,
4 | "bool": true,
5 | "list": [
6 | "some item",
7 | 4.2,
8 | false,
9 | {
10 | "text-in-object-in-list": "text-in-object-in-list",
11 | "number-in-object-in-list": 99.9,
12 | "bool-in-object-in-list": false
13 | },
14 | [
15 | "text in list in list",
16 | 0.01,
17 | true
18 | ]
19 | ],
20 | "object": {
21 | "text-in-object": "some entry",
22 | "number-in-object": -73.31,
23 | "bool-in-object": true,
24 | "object-in-object": {
25 | "text-in-object-in-object": "text-in-object-in-object",
26 | "number-in-object-in-object": -12.34,
27 | "bool-in-object-in-object": false
28 | },
29 | "list-in-object": [
30 | "text in list in object",
31 | -33.33,
32 | true
33 | ]
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/sap-service-operator/src/test/resources/SapServiceOperatorLayeredServiceBindingAccessorTest/WithMetadataFileStructure/secret-key-xsuaa-binding/.metadata:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://sap.com/schemas/service-bindings",
3 | "metaDataProperties": [
4 | {
5 | "name": "type",
6 | "sourceName": "label",
7 | "format": "text"
8 | },
9 | {
10 | "name": "tags",
11 | "format": "json"
12 | },
13 | {
14 | "name": "plan",
15 | "format": "text"
16 | },
17 | {
18 | "name": "instance_guid",
19 | "format": "text"
20 | },
21 | {
22 | "name": "instance_name",
23 | "format": "text"
24 | }
25 | ],
26 | "credentialProperties": [
27 | {
28 | "name": "credentials",
29 | "sourceName": "credentials.json",
30 | "format": "json",
31 | "container": true
32 | }
33 | ]
34 | }
35 |
--------------------------------------------------------------------------------
/sap-service-operator/src/test/resources/SapServiceOperatorLayeredServiceBindingAccessorTest/WithMetadataFileStructure/secret-key-xsuaa-binding/credentials.json:
--------------------------------------------------------------------------------
1 | {
2 | "domain": "secret-key-xsuaa-domain-1",
3 | "domains": [
4 | "secret-key-xsuaa-domain-1"
5 | ],
6 | "clientid": "secret-key-xsuaa-clientid",
7 | "clientsecret": "secret-key-xsuaa-clientsecret",
8 | "url": "https://secret-key-xsuaa-domain-1.com",
9 | "zone_uuid": "secret-key-xsuaa-zone-uuid"
10 | }
--------------------------------------------------------------------------------
/sap-service-operator/src/test/resources/SapServiceOperatorLayeredServiceBindingAccessorTest/WithMetadataFileStructure/secret-key-xsuaa-binding/instance_guid:
--------------------------------------------------------------------------------
1 | secret-key-xsuaa-instance-guid
--------------------------------------------------------------------------------
/sap-service-operator/src/test/resources/SapServiceOperatorLayeredServiceBindingAccessorTest/WithMetadataFileStructure/secret-key-xsuaa-binding/instance_name:
--------------------------------------------------------------------------------
1 | secret-key-xsuaa-instance-name
--------------------------------------------------------------------------------
/sap-service-operator/src/test/resources/SapServiceOperatorLayeredServiceBindingAccessorTest/WithMetadataFileStructure/secret-key-xsuaa-binding/label:
--------------------------------------------------------------------------------
1 | xsuaa
--------------------------------------------------------------------------------
/sap-service-operator/src/test/resources/SapServiceOperatorLayeredServiceBindingAccessorTest/WithMetadataFileStructure/secret-key-xsuaa-binding/plan:
--------------------------------------------------------------------------------
1 | lite
--------------------------------------------------------------------------------
/sap-service-operator/src/test/resources/SapServiceOperatorLayeredServiceBindingAccessorTest/WithMetadataFileStructure/secret-key-xsuaa-binding/tags:
--------------------------------------------------------------------------------
1 | ["secret-key-xsuaa-tag-1", "secret-key-xsuaa-tag-2"]
--------------------------------------------------------------------------------
/sap-service-operator/src/test/resources/SapServiceOperatorServiceBindingIoAccessorTest/PartiallyValidMixedBindings/data-xsuaa-binding/.metadata:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://sap.com/schemas/service-bindings",
3 | "metaDataProperties": [
4 | {
5 | "name": "type",
6 | "sourceName": "label",
7 | "format": "text"
8 | },
9 | {
10 | "name": "tags",
11 | "format": "json"
12 | },
13 | {
14 | "name": "plan",
15 | "format": "text"
16 | },
17 | {
18 | "name": "instance_guid",
19 | "format": "text"
20 | },
21 | {
22 | "name": "instance_name",
23 | "format": "text"
24 | }
25 | ],
26 | "credentialProperties": [
27 | {
28 | "name": "clientid",
29 | "format": "text"
30 | },
31 | {
32 | "name": "clientsecret",
33 | "format": "text"
34 | },
35 | {
36 | "name": "domains",
37 | "format": "json"
38 | },
39 | {
40 | "name": "uaa",
41 | "format": "json"
42 | }
43 | ]
44 | }
45 |
--------------------------------------------------------------------------------
/sap-service-operator/src/test/resources/SapServiceOperatorServiceBindingIoAccessorTest/PartiallyValidMixedBindings/data-xsuaa-binding/clientid:
--------------------------------------------------------------------------------
1 | data-xsuaa-clientid
--------------------------------------------------------------------------------
/sap-service-operator/src/test/resources/SapServiceOperatorServiceBindingIoAccessorTest/PartiallyValidMixedBindings/data-xsuaa-binding/clientsecret:
--------------------------------------------------------------------------------
1 | data-xsuaa-clientsecret
--------------------------------------------------------------------------------
/sap-service-operator/src/test/resources/SapServiceOperatorServiceBindingIoAccessorTest/PartiallyValidMixedBindings/data-xsuaa-binding/domains:
--------------------------------------------------------------------------------
1 | ["data-xsuaa-domain-1", "data-xsuaa-domain-2"]
--------------------------------------------------------------------------------
/sap-service-operator/src/test/resources/SapServiceOperatorServiceBindingIoAccessorTest/PartiallyValidMixedBindings/data-xsuaa-binding/instance_guid:
--------------------------------------------------------------------------------
1 | data-xsuaa-instance-guid
--------------------------------------------------------------------------------
/sap-service-operator/src/test/resources/SapServiceOperatorServiceBindingIoAccessorTest/PartiallyValidMixedBindings/data-xsuaa-binding/instance_name:
--------------------------------------------------------------------------------
1 | data-xsuaa-instance-name
--------------------------------------------------------------------------------
/sap-service-operator/src/test/resources/SapServiceOperatorServiceBindingIoAccessorTest/PartiallyValidMixedBindings/data-xsuaa-binding/label:
--------------------------------------------------------------------------------
1 | xsuaa
--------------------------------------------------------------------------------
/sap-service-operator/src/test/resources/SapServiceOperatorServiceBindingIoAccessorTest/PartiallyValidMixedBindings/data-xsuaa-binding/plan:
--------------------------------------------------------------------------------
1 | application
--------------------------------------------------------------------------------
/sap-service-operator/src/test/resources/SapServiceOperatorServiceBindingIoAccessorTest/PartiallyValidMixedBindings/data-xsuaa-binding/tags:
--------------------------------------------------------------------------------
1 | ["data-xsuaa-tag-1", "data-xsuaa-tag-2"]
--------------------------------------------------------------------------------
/sap-service-operator/src/test/resources/SapServiceOperatorServiceBindingIoAccessorTest/PartiallyValidMixedBindings/data-xsuaa-binding/uaa:
--------------------------------------------------------------------------------
1 | {
2 | "text": "text",
3 | "number": 13.37,
4 | "bool": true,
5 | "list": [
6 | "some item",
7 | 4.2,
8 | false,
9 | {
10 | "text-in-object-in-list": "text-in-object-in-list",
11 | "number-in-object-in-list": 99.9,
12 | "bool-in-object-in-list": false
13 | },
14 | [
15 | "text in list in list",
16 | 0.01,
17 | true
18 | ]
19 | ],
20 | "object": {
21 | "text-in-object": "some entry",
22 | "number-in-object": -73.31,
23 | "bool-in-object": true,
24 | "object-in-object": {
25 | "text-in-object-in-object": "text-in-object-in-object",
26 | "number-in-object-in-object": -12.34,
27 | "bool-in-object-in-object": false
28 | },
29 | "list-in-object": [
30 | "text in list in object",
31 | -33.33,
32 | true
33 | ]
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/sap-service-operator/src/test/resources/SapServiceOperatorServiceBindingIoAccessorTest/PartiallyValidMixedBindings/no-credentials/.metadata:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://sap.com/schemas/service-bindings",
3 | "metaDataProperties": [
4 | {
5 | "name": "type",
6 | "sourceName": "label",
7 | "format": "text"
8 | },
9 | {
10 | "name": "tags",
11 | "format": "json"
12 | },
13 | {
14 | "name": "plan",
15 | "format": "text"
16 | },
17 | {
18 | "name": "instance_guid",
19 | "format": "text"
20 | },
21 | {
22 | "name": "instance_name",
23 | "format": "text"
24 | }
25 | ],
26 | "credentialProperties": [
27 | ]
28 | }
29 |
--------------------------------------------------------------------------------
/sap-service-operator/src/test/resources/SapServiceOperatorServiceBindingIoAccessorTest/PartiallyValidMixedBindings/no-credentials/instance_guid:
--------------------------------------------------------------------------------
1 | instance-guid
--------------------------------------------------------------------------------
/sap-service-operator/src/test/resources/SapServiceOperatorServiceBindingIoAccessorTest/PartiallyValidMixedBindings/no-credentials/instance_name:
--------------------------------------------------------------------------------
1 | instance-name
--------------------------------------------------------------------------------
/sap-service-operator/src/test/resources/SapServiceOperatorServiceBindingIoAccessorTest/PartiallyValidMixedBindings/no-credentials/label:
--------------------------------------------------------------------------------
1 | xsuaa
--------------------------------------------------------------------------------
/sap-service-operator/src/test/resources/SapServiceOperatorServiceBindingIoAccessorTest/PartiallyValidMixedBindings/no-credentials/plan:
--------------------------------------------------------------------------------
1 | lite
--------------------------------------------------------------------------------
/sap-service-operator/src/test/resources/SapServiceOperatorServiceBindingIoAccessorTest/PartiallyValidMixedBindings/no-credentials/tags:
--------------------------------------------------------------------------------
1 | ["tag-1", "tag-2"]
--------------------------------------------------------------------------------
/sap-service-operator/src/test/resources/SapServiceOperatorServiceBindingIoAccessorTest/PartiallyValidMixedBindings/no-metadata-file/binding.json:
--------------------------------------------------------------------------------
1 | {
2 | "plan": "lite",
3 | "tags": [
4 | "tag-1",
5 | "tag-2"
6 | ],
7 | "clientid": "client-id",
8 | "clientsecret": "client-secret"
9 | }
--------------------------------------------------------------------------------
/sap-service-operator/src/test/resources/SapServiceOperatorServiceBindingIoAccessorTest/PartiallyValidMixedBindings/no-type-property/.metadata:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://sap.com/schemas/service-bindings",
3 | "metaDataProperties": [
4 | {
5 | "name": "tags",
6 | "format": "json"
7 | },
8 | {
9 | "name": "plan",
10 | "format": "text"
11 | },
12 | {
13 | "name": "instance_guid",
14 | "format": "text"
15 | },
16 | {
17 | "name": "instance_name",
18 | "format": "text"
19 | }
20 | ],
21 | "credentialProperties": [
22 | {
23 | "name": "clientid",
24 | "format": "text"
25 | },
26 | {
27 | "name": "clientsecret",
28 | "format": "text"
29 | },
30 | {
31 | "name": "domains",
32 | "format": "json"
33 | }
34 | ]
35 | }
36 |
--------------------------------------------------------------------------------
/sap-service-operator/src/test/resources/SapServiceOperatorServiceBindingIoAccessorTest/PartiallyValidMixedBindings/no-type-property/clientid:
--------------------------------------------------------------------------------
1 | clientid
--------------------------------------------------------------------------------
/sap-service-operator/src/test/resources/SapServiceOperatorServiceBindingIoAccessorTest/PartiallyValidMixedBindings/no-type-property/clientsecret:
--------------------------------------------------------------------------------
1 | clientsecret
--------------------------------------------------------------------------------
/sap-service-operator/src/test/resources/SapServiceOperatorServiceBindingIoAccessorTest/PartiallyValidMixedBindings/no-type-property/domains:
--------------------------------------------------------------------------------
1 | ["domain-1", "domain-2"]
--------------------------------------------------------------------------------
/sap-service-operator/src/test/resources/SapServiceOperatorServiceBindingIoAccessorTest/PartiallyValidMixedBindings/no-type-property/instance_guid:
--------------------------------------------------------------------------------
1 | instance-guid
--------------------------------------------------------------------------------
/sap-service-operator/src/test/resources/SapServiceOperatorServiceBindingIoAccessorTest/PartiallyValidMixedBindings/no-type-property/instance_name:
--------------------------------------------------------------------------------
1 | instance-name
--------------------------------------------------------------------------------
/sap-service-operator/src/test/resources/SapServiceOperatorServiceBindingIoAccessorTest/PartiallyValidMixedBindings/no-type-property/plan:
--------------------------------------------------------------------------------
1 | application
--------------------------------------------------------------------------------
/sap-service-operator/src/test/resources/SapServiceOperatorServiceBindingIoAccessorTest/PartiallyValidMixedBindings/no-type-property/tags:
--------------------------------------------------------------------------------
1 | ["tag-1", "tag-2"]
--------------------------------------------------------------------------------
/sap-service-operator/src/test/resources/SapServiceOperatorServiceBindingIoAccessorTest/PartiallyValidMixedBindings/secret-key-xsuaa-binding/.metadata:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://sap.com/schemas/service-bindings",
3 | "metaDataProperties": [
4 | {
5 | "name": "type",
6 | "sourceName": "label",
7 | "format": "text"
8 | },
9 | {
10 | "name": "tags",
11 | "format": "json"
12 | },
13 | {
14 | "name": "plan",
15 | "format": "text"
16 | },
17 | {
18 | "name": "instance_guid",
19 | "format": "text"
20 | },
21 | {
22 | "name": "instance_name",
23 | "format": "text"
24 | }
25 | ],
26 | "credentialProperties": [
27 | {
28 | "name": "credentials",
29 | "sourceName": "credentials.json",
30 | "format": "json",
31 | "container": true
32 | }
33 | ]
34 | }
35 |
--------------------------------------------------------------------------------
/sap-service-operator/src/test/resources/SapServiceOperatorServiceBindingIoAccessorTest/PartiallyValidMixedBindings/secret-key-xsuaa-binding/credentials.json:
--------------------------------------------------------------------------------
1 | {
2 | "domain": "secret-key-xsuaa-domain-1",
3 | "domains": [
4 | "secret-key-xsuaa-domain-1"
5 | ],
6 | "clientid": "secret-key-xsuaa-clientid",
7 | "clientsecret": "secret-key-xsuaa-clientsecret",
8 | "url": "https://secret-key-xsuaa-domain-1.com",
9 | "zone_uuid": "secret-key-xsuaa-zone-uuid"
10 | }
--------------------------------------------------------------------------------
/sap-service-operator/src/test/resources/SapServiceOperatorServiceBindingIoAccessorTest/PartiallyValidMixedBindings/secret-key-xsuaa-binding/instance_guid:
--------------------------------------------------------------------------------
1 | secret-key-xsuaa-instance-guid
--------------------------------------------------------------------------------
/sap-service-operator/src/test/resources/SapServiceOperatorServiceBindingIoAccessorTest/PartiallyValidMixedBindings/secret-key-xsuaa-binding/instance_name:
--------------------------------------------------------------------------------
1 | secret-key-xsuaa-instance-name
--------------------------------------------------------------------------------
/sap-service-operator/src/test/resources/SapServiceOperatorServiceBindingIoAccessorTest/PartiallyValidMixedBindings/secret-key-xsuaa-binding/label:
--------------------------------------------------------------------------------
1 | xsuaa
--------------------------------------------------------------------------------
/sap-service-operator/src/test/resources/SapServiceOperatorServiceBindingIoAccessorTest/PartiallyValidMixedBindings/secret-key-xsuaa-binding/plan:
--------------------------------------------------------------------------------
1 | lite
--------------------------------------------------------------------------------
/sap-service-operator/src/test/resources/SapServiceOperatorServiceBindingIoAccessorTest/PartiallyValidMixedBindings/secret-key-xsuaa-binding/tags:
--------------------------------------------------------------------------------
1 | ["secret-key-xsuaa-tag-1", "secret-key-xsuaa-tag-2"]
--------------------------------------------------------------------------------
/sap-service-operator/src/test/resources/SapServiceOperatorServiceBindingIoAccessorTest/ValidMixedBindings/data-xsuaa-binding/.metadata:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://sap.com/schemas/service-bindings",
3 | "metaDataProperties": [
4 | {
5 | "name": "type",
6 | "sourceName": "label",
7 | "format": "text"
8 | },
9 | {
10 | "name": "tags",
11 | "format": "json"
12 | },
13 | {
14 | "name": "plan",
15 | "format": "text"
16 | },
17 | {
18 | "name": "instance_guid",
19 | "format": "text"
20 | },
21 | {
22 | "name": "instance_name",
23 | "format": "text"
24 | }
25 | ],
26 | "credentialProperties": [
27 | {
28 | "name": "clientid",
29 | "format": "text"
30 | },
31 | {
32 | "name": "clientsecret",
33 | "format": "text"
34 | },
35 | {
36 | "name": "domains",
37 | "format": "json"
38 | },
39 | {
40 | "name": "uaa",
41 | "format": "json"
42 | }
43 | ]
44 | }
45 |
--------------------------------------------------------------------------------
/sap-service-operator/src/test/resources/SapServiceOperatorServiceBindingIoAccessorTest/ValidMixedBindings/data-xsuaa-binding/clientid:
--------------------------------------------------------------------------------
1 | data-xsuaa-clientid
--------------------------------------------------------------------------------
/sap-service-operator/src/test/resources/SapServiceOperatorServiceBindingIoAccessorTest/ValidMixedBindings/data-xsuaa-binding/clientsecret:
--------------------------------------------------------------------------------
1 | data-xsuaa-clientsecret
--------------------------------------------------------------------------------
/sap-service-operator/src/test/resources/SapServiceOperatorServiceBindingIoAccessorTest/ValidMixedBindings/data-xsuaa-binding/domains:
--------------------------------------------------------------------------------
1 | ["data-xsuaa-domain-1", "data-xsuaa-domain-2"]
--------------------------------------------------------------------------------
/sap-service-operator/src/test/resources/SapServiceOperatorServiceBindingIoAccessorTest/ValidMixedBindings/data-xsuaa-binding/instance_guid:
--------------------------------------------------------------------------------
1 | data-xsuaa-instance-guid
--------------------------------------------------------------------------------
/sap-service-operator/src/test/resources/SapServiceOperatorServiceBindingIoAccessorTest/ValidMixedBindings/data-xsuaa-binding/instance_name:
--------------------------------------------------------------------------------
1 | data-xsuaa-instance-name
--------------------------------------------------------------------------------
/sap-service-operator/src/test/resources/SapServiceOperatorServiceBindingIoAccessorTest/ValidMixedBindings/data-xsuaa-binding/label:
--------------------------------------------------------------------------------
1 | xsuaa
--------------------------------------------------------------------------------
/sap-service-operator/src/test/resources/SapServiceOperatorServiceBindingIoAccessorTest/ValidMixedBindings/data-xsuaa-binding/plan:
--------------------------------------------------------------------------------
1 | application
--------------------------------------------------------------------------------
/sap-service-operator/src/test/resources/SapServiceOperatorServiceBindingIoAccessorTest/ValidMixedBindings/data-xsuaa-binding/tags:
--------------------------------------------------------------------------------
1 | ["data-xsuaa-tag-1", "data-xsuaa-tag-2"]
--------------------------------------------------------------------------------
/sap-service-operator/src/test/resources/SapServiceOperatorServiceBindingIoAccessorTest/ValidMixedBindings/data-xsuaa-binding/uaa:
--------------------------------------------------------------------------------
1 | {
2 | "text": "text",
3 | "number": 13.37,
4 | "bool": true,
5 | "list": [
6 | "some item",
7 | 4.2,
8 | false,
9 | {
10 | "text-in-object-in-list": "text-in-object-in-list",
11 | "number-in-object-in-list": 99.9,
12 | "bool-in-object-in-list": false
13 | },
14 | [
15 | "text in list in list",
16 | 0.01,
17 | true
18 | ]
19 | ],
20 | "object": {
21 | "text-in-object": "some entry",
22 | "number-in-object": -73.31,
23 | "bool-in-object": true,
24 | "object-in-object": {
25 | "text-in-object-in-object": "text-in-object-in-object",
26 | "number-in-object-in-object": -12.34,
27 | "bool-in-object-in-object": false
28 | },
29 | "list-in-object": [
30 | "text in list in object",
31 | -33.33,
32 | true
33 | ]
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/sap-service-operator/src/test/resources/SapServiceOperatorServiceBindingIoAccessorTest/ValidMixedBindings/secret-key-xsuaa-binding/.metadata:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://sap.com/schemas/service-bindings",
3 | "metaDataProperties": [
4 | {
5 | "name": "type",
6 | "sourceName": "label",
7 | "format": "text"
8 | },
9 | {
10 | "name": "tags",
11 | "format": "json"
12 | },
13 | {
14 | "name": "plan",
15 | "format": "text"
16 | },
17 | {
18 | "name": "instance_guid",
19 | "format": "text"
20 | },
21 | {
22 | "name": "instance_name",
23 | "format": "text"
24 | }
25 | ],
26 | "credentialProperties": [
27 | {
28 | "name": "credentials",
29 | "sourceName": "credentials.json",
30 | "format": "json",
31 | "container": true
32 | }
33 | ]
34 | }
35 |
--------------------------------------------------------------------------------
/sap-service-operator/src/test/resources/SapServiceOperatorServiceBindingIoAccessorTest/ValidMixedBindings/secret-key-xsuaa-binding/credentials.json:
--------------------------------------------------------------------------------
1 | {
2 | "domain": "secret-key-xsuaa-domain-1",
3 | "domains": [
4 | "secret-key-xsuaa-domain-1"
5 | ],
6 | "clientid": "secret-key-xsuaa-clientid",
7 | "clientsecret": "secret-key-xsuaa-clientsecret",
8 | "url": "https://secret-key-xsuaa-domain-1.com",
9 | "zone_uuid": "secret-key-xsuaa-zone-uuid"
10 | }
--------------------------------------------------------------------------------
/sap-service-operator/src/test/resources/SapServiceOperatorServiceBindingIoAccessorTest/ValidMixedBindings/secret-key-xsuaa-binding/instance_guid:
--------------------------------------------------------------------------------
1 | secret-key-xsuaa-instance-guid
--------------------------------------------------------------------------------
/sap-service-operator/src/test/resources/SapServiceOperatorServiceBindingIoAccessorTest/ValidMixedBindings/secret-key-xsuaa-binding/instance_name:
--------------------------------------------------------------------------------
1 | secret-key-xsuaa-instance-name
--------------------------------------------------------------------------------
/sap-service-operator/src/test/resources/SapServiceOperatorServiceBindingIoAccessorTest/ValidMixedBindings/secret-key-xsuaa-binding/label:
--------------------------------------------------------------------------------
1 | xsuaa
--------------------------------------------------------------------------------
/sap-service-operator/src/test/resources/SapServiceOperatorServiceBindingIoAccessorTest/ValidMixedBindings/secret-key-xsuaa-binding/plan:
--------------------------------------------------------------------------------
1 | lite
--------------------------------------------------------------------------------
/sap-service-operator/src/test/resources/SapServiceOperatorServiceBindingIoAccessorTest/ValidMixedBindings/secret-key-xsuaa-binding/tags:
--------------------------------------------------------------------------------
1 | ["secret-key-xsuaa-tag-1", "secret-key-xsuaa-tag-2"]
--------------------------------------------------------------------------------
/sap-spring-properties/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 4.0.0
4 |
5 | com.sap.cloud.environment.servicebinding
6 | java-parent
7 | 0.21.0
8 |
9 | java-sap-spring-properties
10 |
11 |
12 | ${project.basedir}/../
13 | 6.2.5
14 | 3.4.4
15 |
16 |
17 |
18 |
19 |
20 | com.sap.cloud.environment.servicebinding.api
21 | java-core-api
22 |
23 |
24 | com.sap.cloud.environment.servicebinding.api
25 | java-access-api
26 |
27 |
28 | org.springframework.boot
29 | spring-boot
30 | ${spring-boot.version}
31 |
32 |
33 | org.springframework
34 | spring-core
35 | ${spring.version}
36 |
37 |
38 | org.slf4j
39 | slf4j-api
40 |
41 |
42 |
43 |
44 | org.junit.jupiter
45 | junit-jupiter-api
46 | test
47 |
48 |
49 | org.junit.jupiter
50 | junit-jupiter
51 | test
52 |
53 |
54 | org.assertj
55 | assertj-core
56 | test
57 |
58 |
59 | org.mockito
60 | mockito-core
61 | test
62 |
63 |
64 | org.slf4j
65 | slf4j-simple
66 | test
67 |
68 |
69 | org.springframework
70 | spring-test
71 | ${spring.version}
72 | test
73 |
74 |
75 |
--------------------------------------------------------------------------------
/sap-spring-properties/src/main/java/com/sap/cloud/environment/servicebinding/SapServiceBindingsProperties.java:
--------------------------------------------------------------------------------
1 | package com.sap.cloud.environment.servicebinding;
2 |
3 | import org.springframework.util.StringUtils;
4 |
5 | import java.util.HashMap;
6 | import java.util.Map;
7 |
8 | /**
9 | * Properties of service bindings defined in a properties file such as Spring's default file 'application.properties'.
10 | *
11 | * @param serviceBindings properties of configured service bindings
12 | */
13 | public record SapServiceBindingsProperties(
14 | Map serviceBindings
15 | ) {
16 | /**
17 | * properties of a service binding
18 | *
19 | * @param name name of the service binding (optional)
20 | * @param serviceName name of the service
21 | * @param plan default plan used to create the service binding
22 | * @param tags tags of the service binding
23 | * @param credentials properties for credentials
24 | */
25 | public record ServiceBindingProperties(
26 | String name,
27 | String serviceName,
28 | String plan,
29 | String[] tags,
30 | Map credentials
31 | )
32 | {
33 | public ServiceBindingProperties(String name,
34 | String serviceName,
35 | String plan,
36 | String[] tags,
37 | Map credentials) {
38 | this.name = name;
39 | this.serviceName = serviceName;
40 | this.plan = StringUtils.hasText(plan) ? plan : "standard";
41 | this.tags = (tags == null || tags.length == 0) ? new String[0] : tags;
42 | this.credentials = (credentials == null || credentials.isEmpty()) ? new HashMap<>() : credentials;
43 | }
44 | }
45 | }
--------------------------------------------------------------------------------
/sap-spring-properties/src/main/java/com/sap/cloud/environment/servicebinding/SapSpringPropertiesServiceBindingAccessor.java:
--------------------------------------------------------------------------------
1 | package com.sap.cloud.environment.servicebinding;
2 |
3 | import com.sap.cloud.environment.servicebinding.SapServiceBindingsProperties.ServiceBindingProperties;
4 | import com.sap.cloud.environment.servicebinding.api.DefaultServiceBinding;
5 | import com.sap.cloud.environment.servicebinding.api.ServiceBinding;
6 | import com.sap.cloud.environment.servicebinding.api.ServiceBindingAccessor;
7 | import com.sap.cloud.environment.servicebinding.api.exception.ServiceBindingAccessException;
8 | import com.sap.cloud.environment.servicebinding.environment.SapServiceBindingsPropertiesAccessor;
9 | import org.slf4j.Logger;
10 | import org.slf4j.LoggerFactory;
11 | import org.springframework.util.StringUtils;
12 |
13 | import javax.annotation.Nonnull;
14 | import javax.annotation.Nullable;
15 | import java.util.*;
16 |
17 | /**
18 | * A {@link ServiceBindingAccessor} that is able to load {@link SapServiceBindingsProperties}s from Spring's application
19 | * properties.
20 | */
21 | public class SapSpringPropertiesServiceBindingAccessor implements ServiceBindingAccessor
22 | {
23 | @Nonnull
24 | private static final Logger logger = LoggerFactory.getLogger(SapSpringPropertiesServiceBindingAccessor.class);
25 |
26 | @Nonnull
27 | @Override
28 | public List getServiceBindings()
29 | throws ServiceBindingAccessException
30 | {
31 | List serviceBindings = fetchServiceBindings();
32 |
33 | serviceBindings
34 | .forEach(binding -> logger.debug("Service binding {} found in properties.", binding.getName().get()) //NOSONAR
35 | );
36 |
37 | return serviceBindings;
38 | }
39 |
40 | @Nonnull
41 | private List fetchServiceBindings()
42 | {
43 | Map serviceBindingsProperties =
44 | SapServiceBindingsPropertiesAccessor.getServiceBindingsProperties();
45 |
46 | if( serviceBindingsProperties.isEmpty() ) {
47 | logger.debug("No service bindings provided by application properties.");
48 | }
49 |
50 | return serviceBindingsProperties
51 | .entrySet()
52 | .stream()
53 | .map(entry -> toServiceBinding(entry.getKey(), entry.getValue()))
54 | .filter(Objects::nonNull)
55 | .toList();
56 | }
57 |
58 | @Nullable
59 | private ServiceBinding toServiceBinding(
60 | @Nonnull final String serviceBindingName,
61 | @Nonnull final ServiceBindingProperties serviceBindingProperties )
62 | {
63 | if( validateServiceBindingProperties(serviceBindingProperties, serviceBindingName) ) {
64 | return DefaultServiceBinding
65 | .builder()
66 | .copy(Collections.emptyMap())
67 | .withName(
68 | StringUtils.hasText(serviceBindingProperties.name())
69 | ? serviceBindingProperties.name()
70 | : serviceBindingName)
71 | .withServiceName(serviceBindingProperties.serviceName())
72 | .withServicePlan(serviceBindingProperties.plan())
73 | .withTags(Arrays.asList(serviceBindingProperties.tags()))
74 | .withCredentials(serviceBindingProperties.credentials())
75 | .build();
76 | } else {
77 | return null;
78 | }
79 | }
80 |
81 | private boolean validateServiceBindingProperties(
82 | @Nonnull final ServiceBindingProperties serviceBindingProperties,
83 | @Nonnull final String serviceBindingName )
84 | {
85 | boolean isValid = true;
86 | if( serviceBindingProperties.serviceName() == null || serviceBindingProperties.serviceName().isEmpty() ) {
87 | logger.error("Service binding properties of {} with no service name detected.", serviceBindingName);
88 | isValid = false;
89 | }
90 |
91 | if( serviceBindingProperties.credentials().isEmpty() ) {
92 | logger.error("Service binding properties of {} has no credentials.", serviceBindingName);
93 | isValid = false;
94 | }
95 |
96 | return isValid;
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/sap-spring-properties/src/main/java/com/sap/cloud/environment/servicebinding/environment/SapServiceBindingEnvironmentPostProcessor.java:
--------------------------------------------------------------------------------
1 | package com.sap.cloud.environment.servicebinding.environment;
2 |
3 | import org.slf4j.Logger;
4 | import org.slf4j.LoggerFactory;
5 | import org.springframework.boot.SpringApplication;
6 | import org.springframework.boot.context.properties.bind.BindResult;
7 | import org.springframework.boot.context.properties.bind.Bindable;
8 | import org.springframework.boot.context.properties.bind.Binder;
9 | import org.springframework.boot.env.EnvironmentPostProcessor;
10 | import org.springframework.core.env.ConfigurableEnvironment;
11 |
12 | import com.sap.cloud.environment.servicebinding.SapServiceBindingsProperties;
13 |
14 | import javax.annotation.Nonnull;
15 |
16 | /* An {@link EnvironmentPostProcessor} that binds service binding properties to {@link SapServiceBindingsProperties}. */
17 | public class SapServiceBindingEnvironmentPostProcessor implements EnvironmentPostProcessor
18 | {
19 | private static final Logger log = LoggerFactory.getLogger(SapServiceBindingEnvironmentPostProcessor.class);
20 |
21 | @Override
22 | public void postProcessEnvironment(
23 | @Nonnull final ConfigurableEnvironment environment,
24 | @Nonnull final SpringApplication application )
25 | {
26 | Bindable bindable = Bindable.of(SapServiceBindingsProperties.class);
27 |
28 | BindResult> bindResult = Binder.get(environment).bind("services", bindable);
29 | if( bindResult.isBound() ) {
30 | SapServiceBindingsPropertiesAccessor
31 | .setServiceBindingsProperties(((SapServiceBindingsProperties) bindResult.get()).serviceBindings());
32 | } else {
33 | log.debug("Could not bind 'services' from application properties.");
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/sap-spring-properties/src/main/java/com/sap/cloud/environment/servicebinding/environment/SapServiceBindingsPropertiesAccessor.java:
--------------------------------------------------------------------------------
1 | package com.sap.cloud.environment.servicebinding.environment;
2 |
3 | import com.sap.cloud.environment.servicebinding.SapServiceBindingsProperties.ServiceBindingProperties;
4 |
5 | import javax.annotation.Nonnull;
6 | import java.util.HashMap;
7 | import java.util.Map;
8 |
9 | /*
10 | * The {@link ServiceBindingsPropertiesAccessor} enables Java code not executed in a Spring context to access {@link SapServiceBindingsProperties}.
11 | * The {@link SapServiceBindingsProperties} are fetched by the {@link ServiceBindingEnvironmentPostProcessor} and set in the {@link ServiceBindingsPropertiesAccessor}.
12 | */
13 | public class SapServiceBindingsPropertiesAccessor
14 | {
15 | /* A map of {@link SapServiceBindingsProperties}. Key is the name of the service. */
16 | private static Map serviceBindingsProperties = new HashMap<>();
17 |
18 | private SapServiceBindingsPropertiesAccessor()
19 | {
20 | }
21 |
22 | public static void setServiceBindingsProperties(
23 | @Nonnull final Map serviceBindingsProperties )
24 | {
25 | SapServiceBindingsPropertiesAccessor.serviceBindingsProperties = serviceBindingsProperties;
26 | }
27 |
28 | @Nonnull
29 | public static Map getServiceBindingsProperties()
30 | {
31 | return SapServiceBindingsPropertiesAccessor.serviceBindingsProperties;
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/sap-spring-properties/src/main/resources/META-INF/services/com.sap.cloud.environment.servicebinding.api.ServiceBindingAccessor:
--------------------------------------------------------------------------------
1 | com.sap.cloud.environment.servicebinding.SapSpringPropertiesServiceBindingAccessor
2 |
--------------------------------------------------------------------------------
/sap-spring-properties/src/main/resources/META-INF/spring.factories:
--------------------------------------------------------------------------------
1 | org.springframework.boot.env.EnvironmentPostProcessor=com.sap.cloud.environment.servicebinding.environment.SapServiceBindingEnvironmentPostProcessor
--------------------------------------------------------------------------------
/sap-spring-properties/src/test/java/com/sap/cloud/environment/servicebinding/autoconfiguration/SapServiceBindingEnvironmentPostProcessorTest.java:
--------------------------------------------------------------------------------
1 | package com.sap.cloud.environment.servicebinding.autoconfiguration;
2 |
3 | import javax.annotation.Nonnull;
4 |
5 | import com.sap.cloud.environment.servicebinding.environment.SapServiceBindingEnvironmentPostProcessor;
6 | import com.sap.cloud.environment.servicebinding.environment.SapServiceBindingsPropertiesAccessor;
7 | import org.junit.jupiter.api.BeforeEach;
8 | import org.junit.jupiter.api.Test;
9 | import org.springframework.mock.env.MockEnvironment;
10 |
11 | import com.sap.cloud.environment.servicebinding.SapServiceBindingsProperties.ServiceBindingProperties;
12 | import com.sap.cloud.environment.servicebinding.TestResource;
13 |
14 | import java.util.HashMap;
15 |
16 | import static org.junit.jupiter.api.Assertions.*;
17 |
18 | class SapServiceBindingEnvironmentPostProcessorTest
19 | {
20 | final SapServiceBindingEnvironmentPostProcessor cut = new SapServiceBindingEnvironmentPostProcessor();
21 |
22 | @BeforeEach
23 | void setup()
24 | {
25 | SapServiceBindingsPropertiesAccessor.setServiceBindingsProperties(new HashMap<>());
26 | }
27 |
28 | @Test
29 | void bindServiceBindingsProperties()
30 | {
31 | final MockEnvironment mockEnvironment = TestResource.getAllBindingsProperties();
32 |
33 | cut.postProcessEnvironment(mockEnvironment, null);
34 |
35 | assertNotNull(SapServiceBindingsPropertiesAccessor.getServiceBindingsProperties());
36 | assertContainsXsuaaBindingProperties(
37 | SapServiceBindingsPropertiesAccessor.getServiceBindingsProperties().get("xsuaa-test"));
38 | assertContainsServiceManagerBindingProperties(
39 | SapServiceBindingsPropertiesAccessor.getServiceBindingsProperties().get("service-manager-test"));
40 | assertContainsServiceManagerBindingProperties(
41 | SapServiceBindingsPropertiesAccessor.getServiceBindingsProperties().get("service-manager-test2"));
42 | }
43 |
44 | @Test
45 | void bindEmptyServiceBindingsProperties()
46 | {
47 | final MockEnvironment mockEnvironment = TestResource.getEmptyProperties();
48 |
49 | cut.postProcessEnvironment(mockEnvironment, null);
50 |
51 | assertNotNull(SapServiceBindingsPropertiesAccessor.getServiceBindingsProperties());
52 | assertTrue(SapServiceBindingsPropertiesAccessor.getServiceBindingsProperties().isEmpty());
53 | }
54 |
55 | private void assertContainsXsuaaBindingProperties( @Nonnull final ServiceBindingProperties xsuaaBindingProperties )
56 | {
57 | assertNotNull(xsuaaBindingProperties);
58 | assertEquals("xsuaa", xsuaaBindingProperties.serviceName());
59 | assertEquals("broker", xsuaaBindingProperties.plan());
60 | assertNotNull(xsuaaBindingProperties.credentials());
61 | }
62 |
63 | private void assertContainsServiceManagerBindingProperties(
64 | @Nonnull final ServiceBindingProperties serviceManagerBindingProperties )
65 | {
66 | assertNotNull(serviceManagerBindingProperties);
67 | assertEquals("service-manager", serviceManagerBindingProperties.serviceName());
68 | assertEquals("standard", serviceManagerBindingProperties.plan());
69 | assertNotNull(serviceManagerBindingProperties.credentials());
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/sap-vcap-services/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 4.0.0
4 |
5 | com.sap.cloud.environment.servicebinding
6 | java-parent
7 | 0.21.0
8 |
9 | java-sap-vcap-services
10 |
11 |
12 | ${project.basedir}/../
13 |
14 |
15 |
16 |
17 |
18 | com.sap.cloud.environment.servicebinding.api
19 | java-core-api
20 |
21 |
22 | com.sap.cloud.environment.servicebinding.api
23 | java-access-api
24 |
25 |
26 | org.json
27 | json
28 |
29 |
30 | org.slf4j
31 | slf4j-api
32 |
33 |
34 |
35 |
36 | org.junit.jupiter
37 | junit-jupiter-api
38 | test
39 |
40 |
41 | org.junit.jupiter
42 | junit-jupiter
43 | test
44 |
45 |
46 | org.assertj
47 | assertj-core
48 | test
49 |
50 |
51 | org.mockito
52 | mockito-core
53 | test
54 |
55 |
56 | org.slf4j
57 | slf4j-simple
58 | test
59 |
60 |
61 |
62 |
--------------------------------------------------------------------------------
/sap-vcap-services/src/main/resources/META-INF/services/com.sap.cloud.environment.servicebinding.api.ServiceBindingAccessor:
--------------------------------------------------------------------------------
1 | com.sap.cloud.environment.servicebinding.SapVcapServicesServiceBindingAccessor
2 |
--------------------------------------------------------------------------------
/sap-vcap-services/src/test/java/com/sap/cloud/environment/servicebinding/TestResource.java:
--------------------------------------------------------------------------------
1 | package com.sap.cloud.environment.servicebinding;
2 |
3 | import java.io.IOException;
4 | import java.net.URISyntaxException;
5 | import java.net.URL;
6 | import java.nio.file.Files;
7 | import java.nio.file.Path;
8 | import java.nio.file.Paths;
9 |
10 | import javax.annotation.Nonnull;
11 |
12 | public final class TestResource
13 | {
14 | private TestResource()
15 | {
16 | throw new IllegalStateException("This utility class must not be initialized.");
17 | }
18 |
19 | @Nonnull
20 | public static String read( @Nonnull final Class> testClass, @Nonnull final String fileName )
21 | {
22 | final Path resourcePath = get(testClass, fileName);
23 | try {
24 | return String.join("\n", Files.readAllLines(resourcePath));
25 | }
26 | catch( final IOException e ) {
27 | throw new AssertionError(
28 | String
29 | .format("Unable to read test resource content from '%s/%s.'", testClass.getSimpleName(), fileName));
30 | }
31 | }
32 |
33 | @Nonnull
34 | public static Path get( @Nonnull final Class> testClass, @Nonnull final String fileName )
35 | {
36 | final URL url = testClass.getClassLoader().getResource(testClass.getSimpleName());
37 | if( url == null ) {
38 | throw new AssertionError(
39 | String.format("Unable to load test source from '%s/%s'", testClass.getSimpleName(), fileName));
40 | }
41 |
42 | final String rootFolder;
43 | try {
44 | rootFolder = Paths.get(url.toURI()).toString();
45 | return Paths.get(rootFolder, fileName);
46 | }
47 | catch( final URISyntaxException e ) {
48 | throw new AssertionError(
49 | String.format("Unable to load test source from '%s/%s'", testClass.getSimpleName(), fileName));
50 | }
51 | }
52 |
53 | @Nonnull
54 | public static String getPathAsString( @Nonnull final Class> testClass, @Nonnull final String fileName )
55 | {
56 | return get(testClass, fileName).toAbsolutePath().toString();
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/sap-vcap-services/src/test/resources/SapVcapServicesServiceBindingAccessorTest/FullVcapServices.json:
--------------------------------------------------------------------------------
1 | {
2 | "xsuaa": [
3 | {
4 | "name": "xsuaa-binding-1",
5 | "plan": "lite",
6 | "tags": [
7 | "xsuaa-binding-1-tag-1",
8 | "xsuaa-binding-1-tag-2"
9 | ],
10 | "credentials": {
11 | "uri": "https://xsuaa-1.domain.com",
12 | "clientid": "xsuaa-clientid-1",
13 | "clientsecret": "xsuaa-clientsecret-1"
14 | }
15 | },
16 | {
17 | "name": "xsuaa-binding-2",
18 | "plan": "application",
19 | "tags": [
20 | "xsuaa-binding-2-tag-1",
21 | "xsuaa-binding-2-tag-2"
22 | ],
23 | "credentials": {
24 | "uri": "https://xsuaa-2.domain.com",
25 | "clientid": "xsuaa-clientid-2",
26 | "clientsecret": "xsuaa-clientsecret-2"
27 | }
28 | }
29 | ],
30 | "destination": [
31 | {
32 | "name": "destination-binding-1",
33 | "plan": "broker",
34 | "tags": [
35 | "destination-binding-1-tag-1",
36 | "destination-binding-1-tag-2"
37 | ],
38 | "credentials": {
39 | "uri": "https://destination-1.domain.com",
40 | "clientid": "destination-clientid-1",
41 | "clientsecret": "destination-clientsecret-1"
42 | }
43 | }
44 | ]
45 | }
--------------------------------------------------------------------------------
/sap-vcap-services/src/test/resources/SapVcapServicesServiceBindingAccessorTest/VcapServicesWithBrokenBinding.json:
--------------------------------------------------------------------------------
1 | {
2 | "xsuaa": [
3 | {
4 | "name": "xsuaa-binding-1",
5 | "plan": "lite",
6 | "tags": [
7 | "xsuaa-binding-1-tag-1",
8 | "xsuaa-binding-1-tag-2"
9 | ],
10 | "credentials": {
11 | "uri": "https://xsuaa-1.domain.com",
12 | "clientid": "xsuaa-clientid-1",
13 | "clientsecret": "xsuaa-clientsecret-1"
14 | }
15 | },
16 | "this should not cause an error"
17 | ],
18 | "destination": {
19 | "key": "value"
20 | }
21 | }
--------------------------------------------------------------------------------