├── .codestyle ├── eclipse-codestyle.xml └── intellij_inspections.xml ├── .docs ├── architecture.drawio └── architecture.png ├── .etc └── suppression.xml ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── consultation_question.md │ └── feature_request.md ├── dependabot.yml ├── pull_request_template.md └── workflows │ ├── ci-build.yml │ ├── codeql.yml │ ├── dependabot-automerge.yml │ ├── fosstars-project-report.yml │ └── release.yml ├── .gitignore ├── .scripts └── bump-version │ ├── __tests__ │ ├── __init__.py │ ├── maven │ │ ├── __init__.py │ │ ├── resources │ │ │ ├── test_xml_maven_module_reader │ │ │ │ ├── multi_module │ │ │ │ │ ├── pom.xml │ │ │ │ │ └── sub-module │ │ │ │ │ │ └── pom.xml │ │ │ │ └── single_module │ │ │ │ │ └── pom.xml │ │ │ └── test_xml_maven_project │ │ │ │ ├── multi_module │ │ │ │ ├── pom.xml │ │ │ │ └── sub-module │ │ │ │ │ └── pom.xml │ │ │ │ └── single_module │ │ │ │ └── pom.xml │ │ ├── test_maven_module_identifier.py │ │ ├── test_maven_property.py │ │ ├── test_xml_maven_module_reader.py │ │ └── test_xml_maven_project.py │ └── utility │ │ ├── __init__.py │ │ ├── test_type_utility.py │ │ └── xml │ │ ├── __init__.py │ │ ├── test_e_tree_xml_document.py │ │ ├── test_e_tree_xml_node.py │ │ └── test_xml_node.py │ ├── maven │ ├── __init__.py │ ├── maven_module.py │ ├── maven_module_identifier.py │ ├── maven_module_reader.py │ ├── maven_project.py │ ├── maven_property.py │ ├── xml_maven_module.py │ ├── xml_maven_module_identifier.py │ ├── xml_maven_module_reader.py │ ├── xml_maven_project.py │ └── xml_maven_property.py │ ├── maven_version.py │ └── utility │ ├── __init__.py │ ├── type_utility.py │ └── xml │ ├── __init__.py │ ├── e_tree_xml_document.py │ ├── e_tree_xml_node.py │ ├── xml_document.py │ └── xml_node.py ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── LICENSES └── Apache-2.0.txt ├── README.md ├── REUSE.toml ├── api-parent ├── access-api │ ├── pom.xml │ └── src │ │ ├── main │ │ └── java │ │ │ └── com │ │ │ └── sap │ │ │ └── cloud │ │ │ └── environment │ │ │ └── servicebinding │ │ │ └── api │ │ │ ├── DefaultServiceBindingAccessor.java │ │ │ ├── ServiceBindingAccessor.java │ │ │ ├── ServiceBindingMerger.java │ │ │ ├── SimpleServiceBindingCache.java │ │ │ └── exception │ │ │ └── ServiceBindingAccessException.java │ │ └── test │ │ └── java │ │ └── com │ │ └── sap │ │ └── cloud │ │ └── environment │ │ └── servicebinding │ │ └── api │ │ ├── ServiceBindingMergerTest.java │ │ └── SimpleServiceBindingCacheTest.java ├── consumption-api │ ├── pom.xml │ └── src │ │ ├── main │ │ └── java │ │ │ └── com │ │ │ └── sap │ │ │ └── cloud │ │ │ └── environment │ │ │ └── servicebinding │ │ │ └── api │ │ │ ├── TypedListView.java │ │ │ ├── TypedMapView.java │ │ │ └── exception │ │ │ ├── KeyNotFoundException.java │ │ │ └── ValueCastException.java │ │ └── test │ │ ├── java │ │ └── com │ │ │ └── sap │ │ │ └── cloud │ │ │ └── environment │ │ │ └── servicebinding │ │ │ └── api │ │ │ ├── TypedListViewTest.java │ │ │ └── TypedMapViewTest.java │ │ └── resources │ │ └── mockito-extensions │ │ └── org.mockito.plugins.MockMaker ├── core-api │ ├── pom.xml │ └── src │ │ ├── main │ │ └── java │ │ │ └── com │ │ │ └── sap │ │ │ └── cloud │ │ │ └── environment │ │ │ └── servicebinding │ │ │ └── api │ │ │ ├── DefaultServiceBinding.java │ │ │ ├── DefaultServiceBindingBuilder.java │ │ │ ├── ServiceBinding.java │ │ │ ├── ServiceIdentifier.java │ │ │ └── exception │ │ │ └── UnsupportedPropertyTypeException.java │ │ └── test │ │ └── java │ │ └── com │ │ └── sap │ │ └── cloud │ │ └── environment │ │ └── servicebinding │ │ └── api │ │ ├── DefaultServiceBindingTest.java │ │ └── ServiceIdentifierTest.java └── pom.xml ├── bom └── pom.xml ├── modules-bom └── pom.xml ├── pom.xml ├── sap-service-operator ├── pom.xml └── src │ ├── main │ ├── java │ │ └── com │ │ │ └── sap │ │ │ └── cloud │ │ │ └── environment │ │ │ └── servicebinding │ │ │ ├── BindingMetadata.java │ │ │ ├── BindingMetadataFactory.java │ │ │ ├── BindingProperty.java │ │ │ ├── BindingPropertyFormat.java │ │ │ ├── LayeredDataParsingStrategy.java │ │ │ ├── LayeredParsingStrategy.java │ │ │ ├── LayeredPropertySetter.java │ │ │ ├── LayeredSecretKeyParsingStrategy.java │ │ │ ├── LayeredSecretRootKeyParsingStrategy.java │ │ │ ├── SapServiceOperatorLayeredServiceBindingAccessor.java │ │ │ └── SapServiceOperatorServiceBindingIoAccessor.java │ └── resources │ │ └── META-INF │ │ └── services │ │ └── com.sap.cloud.environment.servicebinding.api.ServiceBindingAccessor │ └── test │ ├── java │ └── com │ │ └── sap │ │ └── cloud │ │ └── environment │ │ └── servicebinding │ │ ├── BindingMetadataFactoryTest.java │ │ ├── LayeredDataParsingStrategyTest.java │ │ ├── LayeredSecretKeyParsingStrategyTest.java │ │ ├── LayeredSecretRootKeyParsingStrategyTest.java │ │ ├── SapServiceOperatorLayeredServiceBindingAccessorTest.java │ │ ├── SapServiceOperatorServiceBindingIoAccessorTest.java │ │ └── TestResource.java │ └── resources │ ├── BindingMetadataFactoryTest │ ├── container_metadata.json │ ├── flat_metadata.json │ ├── malformed_metadata.json │ └── multi_container_metadata.json │ ├── LayeredDataParsingStrategyTest │ ├── NoCredentials │ │ ├── instance_guid │ │ ├── instance_name │ │ ├── label │ │ ├── plan │ │ └── tags │ └── ValidBinding │ │ ├── clientid │ │ ├── clientsecret │ │ ├── domains │ │ ├── instance_guid │ │ ├── instance_name │ │ ├── label │ │ ├── plan │ │ └── tags │ ├── LayeredSecretKeyParsingStrategyTest │ ├── NoJsonFile │ │ └── file1 │ ├── TwoJsonFiles │ │ ├── file1.json │ │ └── file2.json │ └── ValidBinding │ │ ├── credentials.json │ │ ├── instance_guid │ │ ├── instance_name │ │ ├── label │ │ └── plan │ ├── LayeredSecretRootKeyParsingStrategyTest │ ├── MultipleFiles │ │ ├── file1 │ │ └── file2 │ ├── NotAJsonFile │ │ └── file │ └── ValidBinding │ │ └── binding.json │ ├── SapServiceOperatorLayeredServiceBindingAccessorTest │ ├── InvalidBinding │ │ └── xsuaa │ │ │ ├── data-binding │ │ │ ├── instance_guid │ │ │ ├── instance_name │ │ │ ├── label │ │ │ ├── plan │ │ │ └── tags │ │ │ ├── secret-key-binding │ │ │ ├── credentials.json │ │ │ ├── instance_guid │ │ │ ├── instance_name │ │ │ ├── label │ │ │ └── plan │ │ │ └── secret-root-key-binding │ │ │ └── binding.json │ ├── MixedBindings │ │ └── xsuaa │ │ │ ├── data-binding │ │ │ ├── clientid │ │ │ ├── clientsecret │ │ │ ├── domains │ │ │ ├── instance_guid │ │ │ ├── instance_name │ │ │ ├── label │ │ │ ├── plan │ │ │ └── tags │ │ │ ├── secret-key-binding │ │ │ ├── credentials.json │ │ │ ├── instance_guid │ │ │ ├── instance_name │ │ │ ├── label │ │ │ └── plan │ │ │ └── secret-root-key-binding │ │ │ └── binding.json │ └── WithMetadataFileStructure │ │ ├── data-xsuaa-binding │ │ ├── .metadata │ │ ├── clientid │ │ ├── clientsecret │ │ ├── domains │ │ ├── instance_guid │ │ ├── instance_name │ │ ├── label │ │ ├── plan │ │ ├── tags │ │ └── uaa │ │ └── secret-key-xsuaa-binding │ │ ├── .metadata │ │ ├── credentials.json │ │ ├── instance_guid │ │ ├── instance_name │ │ ├── label │ │ ├── plan │ │ └── tags │ └── SapServiceOperatorServiceBindingIoAccessorTest │ ├── PartiallyValidMixedBindings │ ├── data-xsuaa-binding │ │ ├── .metadata │ │ ├── clientid │ │ ├── clientsecret │ │ ├── domains │ │ ├── instance_guid │ │ ├── instance_name │ │ ├── label │ │ ├── plan │ │ ├── tags │ │ └── uaa │ ├── no-credentials │ │ ├── .metadata │ │ ├── instance_guid │ │ ├── instance_name │ │ ├── label │ │ ├── plan │ │ └── tags │ ├── no-metadata-file │ │ └── binding.json │ ├── no-type-property │ │ ├── .metadata │ │ ├── clientid │ │ ├── clientsecret │ │ ├── domains │ │ ├── instance_guid │ │ ├── instance_name │ │ ├── plan │ │ └── tags │ └── secret-key-xsuaa-binding │ │ ├── .metadata │ │ ├── credentials.json │ │ ├── instance_guid │ │ ├── instance_name │ │ ├── label │ │ ├── plan │ │ └── tags │ └── ValidMixedBindings │ ├── data-xsuaa-binding │ ├── .metadata │ ├── clientid │ ├── clientsecret │ ├── domains │ ├── instance_guid │ ├── instance_name │ ├── label │ ├── plan │ ├── tags │ └── uaa │ └── secret-key-xsuaa-binding │ ├── .metadata │ ├── credentials.json │ ├── instance_guid │ ├── instance_name │ ├── label │ ├── plan │ └── tags ├── sap-spring-properties ├── pom.xml └── src │ ├── main │ ├── java │ │ └── com │ │ │ └── sap │ │ │ └── cloud │ │ │ └── environment │ │ │ └── servicebinding │ │ │ ├── SapServiceBindingsProperties.java │ │ │ ├── SapSpringPropertiesServiceBindingAccessor.java │ │ │ └── environment │ │ │ ├── SapServiceBindingEnvironmentPostProcessor.java │ │ │ └── SapServiceBindingsPropertiesAccessor.java │ └── resources │ │ └── META-INF │ │ ├── services │ │ └── com.sap.cloud.environment.servicebinding.api.ServiceBindingAccessor │ │ └── spring.factories │ └── test │ └── java │ └── com │ └── sap │ └── cloud │ └── environment │ └── servicebinding │ ├── SapSpringPropertiesServiceBindingAccessorTest.java │ ├── TestResource.java │ └── autoconfiguration │ └── SapServiceBindingEnvironmentPostProcessorTest.java └── sap-vcap-services ├── pom.xml └── src ├── main ├── java │ └── com │ │ └── sap │ │ └── cloud │ │ └── environment │ │ └── servicebinding │ │ └── SapVcapServicesServiceBindingAccessor.java └── resources │ └── META-INF │ └── services │ └── com.sap.cloud.environment.servicebinding.api.ServiceBindingAccessor └── test ├── java └── com │ └── sap │ └── cloud │ └── environment │ └── servicebinding │ ├── SapVcapServicesServiceBindingAccessorTest.java │ └── TestResource.java └── resources └── SapVcapServicesServiceBindingAccessorTest ├── FullVcapServices.json └── VcapServicesWithBrokenBinding.json /.docs/architecture.drawio: -------------------------------------------------------------------------------- 1 | 7V1tc6M4Ev41qcp9yBXvxh8Te3IzdZmd1CQ3d/tRBtnmBoMXcBLvr1/JSNhIAssOAmdWVVMTI8Sb+lF36+mWdGVPVm//ysB6+TUNYXxlGeHblT29sizTt0foDy7ZliWWa/hlySKLQlJrX/AU/QlJoUFKN1EI81rFIk3jIlrXC4M0SWBQ1MpAlqWv9WrzNK4/dQ0W5InGvuApADHkqv03CotlWeq7B7U/w2ixpE82DXJmBWhlUpAvQZi+HhTZn67sSZamRflr9TaBMW492i7ldfcNZ6sXy2BSyFzw7YcDRp/vPt0/Plhf3D/+8/lxFd2MxuVtXkC8IV9M3rbY0iZYZOlmzT+NvMALzAr4JpIFmMWQ/VwEFJiuYJFtUT1ylUWbjGKEHL7u29s0fFK4PGhsh9YERMiL6t77dkA/SFOc0iy+oFm8GD33boZ+LPCPK/fuNghgnt/crqMrd0oroAfu67BtiZoyCSF+tIFOvy6jAj6tQYDPvqIehMqWxQq96tREP+dRHE/SOM1219rz+dwKAlSeF1n6Ex6cCb2Z53roDIijRYLKYjjHj8eyiRCWb0lxka6rlzoUZTs0WAHzghxQUvTGAknRv1FSwGyOG7mSSvUXNZ6JJfEEs5cogKhuGCWLUq6odVnx5a/RKgYJxMJJk+KJnEFtchcsozh8ANt0g785L0Dwkx7dLdMs+hPVB1Sy6HRWEGVneYyok3T3gP1FT/hmBDIZzNFlj1R4JlP0FbzVKj6AvCAFQRrHYJ1HZa/EF65AtoiSu7Qo0hWp1D02yFnXq3dyh+/klgg5rqUKOKYAOHfY4sCijob8+h9X9i068xChxiSgqldp7O+odQquxxIBC2Qu1X9zpDDQMx92dabOvuQ7aTZclKJr5/HO3iyjMIQJhkRaAKqVsbDXKeoau3Z1sTZDrT8x/unuVNkEHZv7Y/QPV8+KSZqgbwHRDh0QgesV5oU8bqreehw31Bh4cjih9brHicPhpC77rzBbQGlVYRxIOYBYMzXK+X0aZRD1IYC0Mo3iMG6DOZJUKdS4dw8Vl4PKDX4OsSb5MS2yNztam0hpE0caMxejTUZCiMA/NkhYxXaSrvBXZyVSPrGlGg8d44F6F8PhQTT+Ot19PYoU7biea2Zsv25mbJs3M7bVp+dqi4Y8dzu+A+6AcA1K/cH6qOhxxkx0ivi3szSNIUi0mjmiZuQx1Gx2hJBRpmZsWwAZRsowCW8xYYaOwgis0iR8Xka4xdGJ+wg/b9fe6Ij2f6dOV9S5DfgWFf/Dv1Hjl0e/H5yZvh0ebM/rxHm6yQIoISmktNCoTsIsw5CygRJMBx3RZjAGRfRSJwxFIiS3e8SIPfBjXWZk7DBcSfmZ5KpDgo+5kWceuVHZDNyNdoiqvvEdIONHSm0gm8UpNg53IciXO9SYLVgzrYvHmiOLNXM4rHks1twzseazWGNv1IA1JHywPahGtHfjC7sOM8izjdb3GrGDwnp99KN8g26Bz4/7nqLVOoZ1KzsBwRLKemaaJ+jKgRtZx3kCoTU2x6pIa9vjAINHgSGM4QIUsOIBBK5aIzWtnTRxn/xAThofdMKoCLDamG4ypPfTpIREdaRB0DEIBIRAzyDgCQEMAuQrgXiKdMNztIJPm/U6jihLVB0RvuDhsKqmElUhhbLIgyHF4WmASl2ETARLOn5l32ukdI0U03CGhgof69wpFfTxO6f0O0zgK3YRdyip6Q8NCAWAkKUQ1VkZPgzBiTmOduIrxUxzn9i0FDkZr5C0YrgX6jOW+fTG5ARv84K3BUKOwQzGj2kelS7RNCvrMsIfTr6upCupKhDpWALxYoY4SlBRFKLOTXMaXlJ0J93Du0aAN7jKP43x/cXIOCqx42QckckQZNyY5dBYvkGWjDON0ZE7dcTG+f4HYOMcnoaewjnYxMX7kvw0I9dd5k4dFdKEnK3MYIozd64sO0KWAyRY2WgujrOf9VQCObg4XSTt9GxLebpWe8udiVeWclPW90Vjobuy9yOj+YUoAOowaxUwgArwhh4wO6IJIQQk+QFIpMyFfa8HX30hxx/ceIgJfYSbBL4Sx1TrmAtAimkOHfuho6tLmozn+JKql8s26m4ynsj94ifjTdIM9jUVL3ShHzpc30NnfGtme11PxauAcdJUvJ7FJJpP9d6JeJzQdB7zyYgQD7pF8+8ckbKjWX3dp743eFTI5/433OZ7W7iPWRaZnmsnDZEmk3DS5BghJJRFpRoQEeBOiDxrBIvrn3BLcLEDg85cVwoJ0fyYXiEhSikmSqIBCt/WmOZAip4ojW+z/+N1IbTSUIMQQRpMvwjhmdsKIb+BVTWkYmGhbYlSWIhyXvrFhWjQUJ/er+ExHDwEGTD9wqOJ9N3D4xGNMzQ8hoGHIIGmX3g00b0IHs+gYUUQjQmlmPCH9kVFU7UJJiYZRA1bRCCuoPEVrFlk4Pm32h1VO4Y1hvZHTV51CNNPONE3sI2nJWO8mxeyBaE2YQOq4xEllqT7cJlrVC5HM9cofI5mrpFWGiRxrQ4Z12CgIJu3ZrlHbqR4wrIvoqx7jrdw66IJ4i2+Iep/6uItIt9YFG9J8s1q5xj3FXaBZujCEWcf0ZmxN7JB52EXefMznLRoHzqQ1vN2DUPkf/yI4CvX/i3ZjX+vCIq8cKUjKJXxPBS97amKoIyaljBEHSKdX+d1f6N53ZdWvPxdPNJzkgxceW+rJZdRBBp1vIfVhpnDcYyGz+XBR5Qr2St83JZVU3XUtoMR7+mYEE1E7hcTTSpFx20HA4VwznG/qGiK5reEbktuTE85VoAH0ZTjfvHQEsq/K9WAVhI9g0I0T7lfULRE778kBVzArAEUlBLSgOgSEIIYS8+AaAnbT9MNas4GPIS7kxoSnQ9YBQGVniHREqr/bbOaNaqI8qSGROeQsAf3LVvC8yUEGiBBx6IaEl3TWoO7ly3RecJNNWBC01dKkSGagNcrMujDRMjA+TvHoEHraGx0jQ1bNOOuXza8iaSg5OXznrfEePmUIHjA/HoF1hgbJUomMcjzKvaJ2QsDIQMmYY4hRG/Qnhb0rPlRZSizh/ZgPZ40b9csOkZ7doxWIkgrnNPrKxN+EzuOfVXUdNea0JCFgycPh0tZOsZrY8GjJMSfQaSvSXDVaBh8cWdPggMXgkJT4IogMfgqzp4EAy6EhLYXCuAw/FLN3nECXAgHzX8rQsTgazV7x/lvISI0/a0IEbKrO6tDxHH6W4gIzX4rQoQobNovIiTYbyEkNPmtdPgpCp72CoyRDPndjAzNfSuDhiiI2i80mkYeDdz3lwKu8usYIeJM6vtwsrTmu9Uha/DFUn2J3UX6XsfwxnT5ZnFEU7Uqj7/7dpFbyPAJrG9+BOg/Mtsi72Nq3dwPYBBwHQ+dmfku3t2z46l1owZBt06t61tePHWPZINFQyWjdxS4UrCjwAngkJ+a1+sW7T5P++NFf2HyEmVpskKf/wNkEdZl3yEI6U5+95skKLd15APEehbNKVaz6rkfJ1LkD7fsQYLe/2DdA3z4++G5/coHu6PmpQ+IqT66yAEVz0VvzzNyWQ/CYgyN9GbZI/ZOcuscnLo9DzWPTe8xrp9Xs/2OaUrsPKXa+/MYg3DjWLxJMC1f0MEddhGK7hY2oelOEv4fcSxuvq1hBgqkuvtY1BpAfx5IWwfWT/QCH87m8jp6D5STfMC+pUYXO6k7gUQ+VDzIF4MZu+uqdge7cQdPAQrt/8yqKrKz30aqNpkZ8ylg2CHM0rR4BPiVsP+3+6U9u1YwVN3x4yy5MBYPBtCn5qjJkWMPCriIINmkGckzhrVhAFEuj/v6C7jVIwFVeBl8jYWxiJg9fScFHjYIZltZY2S928p8sFzTM6yMSyVF44Bj3sxUW3H2wjuMm7JHsLKBdEkXvPBxPSKMGYZZ6bWIT65rRqppVVzxnvJaQ7VrqJN3FxZYNCHM1K1xaUisnHdAVoQRWKVJ+LyMkhaWwnJaWQp1izNWXf8ocTGW3Vd4PCBx4TL7Cp+7rbA3ar+P4tUZTYMfLhObNkVd9phd62Q51TOMgmkwkSeb9o3hVlQ1eA+0GrEG2W7lpkttzpF1ea3JZ3HXWvM7GtRdcIuyU2QuoUVFHm+zOfkYS/7KW5Wqfx43K+5wZsWz67hxz6XDR+z6weyNlBsWif2cf2m4WR8Abj4TfrFt6zy4sTfiRl8dRV9MkzFVlKhufDGntb6i8IwhkZzzS4Pf+QDgN037g6GfXcf8QsHPZ87zwZw6jfEl1XGcweI4rKtKR7aDJfaYBp+moTN7emTL9l3446T2mKZo3m8/Nrf33J5KQBed3OMwY5Abxz1zNOOxaULcnTqysNRiDpzdM9xQvXcoU4xcNpKZUceNzeZDyyLZ9Y/dqauRksGmZ9lkXfmmV6uuUAR+dIhTMQ6rZ2C9/JqGENf4Cw== -------------------------------------------------------------------------------- /.docs/architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAP/btp-environment-variable-access/41d0e1f3bdb0307819a38131a1e79c9da45ef756/.docs/architecture.png -------------------------------------------------------------------------------- /.etc/suppression.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Report a new bug 4 | title: '' 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | Please first read carefully [Report an issue](https://github.com/SAP/btp-environment-variable-access/blob/main/CONTRIBUTING.md#issues-and-planning) section before creating an Issue. 11 | 12 | **Issue Summary** 13 | - A clear and concise description of the issue. 14 | - Provide context information 15 | - dependency tree: ``mvn dependency:tree`` 16 | - debug logs 17 | - environment: Cloud Foundry or Kubernetes/Kyma 18 | - Steps to reproduce the problem: 1. 2. 3. 19 | - What is the expected result? 20 | - What happened instead? 21 | - Additional information? (attach screenshots if applicable) 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/consultation_question.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Consultation question 3 | about: ask a question 4 | title: '' 5 | labels: consultation 6 | assignees: '' 7 | --- 8 | 9 | Before creating a new consultation questions search first for [GitHub Consultation Questions](https://github.com/SAP/btp-environment-variable-access/issues?q=label%3Aconsultation+) for already replied questions. If you still don't find an answer create a new one. 10 | 11 | **Question Summary** 12 | If applicable, please mention: 13 | - client library version 14 | - Environment: Cloud Foundry or Kubernetes/Kyma 15 | - Additional information 16 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: 'Feat: ' 5 | labels: enhancement 6 | assignees: '' 7 | 8 | --- 9 | 10 | ## Context 11 | 12 | 13 | 14 | ## Request 15 | 16 | 17 | 18 | ## Suggestions 19 | 20 | 21 | 22 | ## Questions 23 | 24 | 25 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: maven 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | time: "06:00" 8 | timezone: Etc/UCT 9 | open-pull-requests-limit: 10 10 | groups: 11 | production-minor-patch: 12 | dependency-type: "production" 13 | update-types: [ "minor", "patch" ] 14 | exclude-patterns: 15 | - "*-plugin" 16 | production-major: 17 | dependency-type: "production" 18 | update-types: [ "major" ] 19 | exclude-patterns: 20 | - "*-plugin" 21 | plugins: 22 | dependency-type: "production" 23 | patterns: 24 | - "*-plugin" 25 | test: 26 | dependency-type: "development" 27 | ignore: 28 | - dependency-name: "com.github.ekryd.sortpom:sortpom-maven-plugin" # newer versions require Java > 8 29 | - dependency-name: "net.revelc.code.formatter:formatter-maven-plugin" # newer versions require Java > 8 30 | - dependency-name: "net.revelc.code:impsort-maven-plugin" # newer versions require Java > 8 31 | - dependency-name: "org.mockito:mockito-core" # newer versions require Java > 8 32 | 33 | - package-ecosystem: github-actions 34 | directory: "/" 35 | schedule: 36 | interval: monthly 37 | commit-message: 38 | prefix: 'chore: [DevOps] ' 39 | groups: 40 | github-actions: 41 | patterns: 42 | - "*" 43 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ## Context 2 | 3 | Fixes # 4 | 5 | 6 | 7 | ### Feature Scope 8 | 9 | - [ ] 10 | - [ ] 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 | *
  1. Use {@link ServiceBindingAccessor#getInstancesViaServiceLoader()} to retrieve a list of 46 | * {@link ServiceBindingAccessor} instances.
  2. 47 | *
  3. Combine instances of the found implementations using the {@link ServiceBindingMerger} (the merging strategy 48 | * used is {@link ServiceBindingMerger#KEEP_EVERYTHING}).
  4. 49 | *
  5. Wrap the resulting instance of {@link ServiceBindingMerger} into a {@link SimpleServiceBindingCache}.
  6. 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 | } --------------------------------------------------------------------------------