├── src ├── main │ └── java │ │ ├── com │ │ └── sap │ │ │ └── cloudfoundry │ │ │ └── client │ │ │ └── facade │ │ │ ├── package-info.java │ │ │ ├── Nullable.java │ │ │ ├── SkipNulls.java │ │ │ ├── domain │ │ │ ├── Derivable.java │ │ │ ├── PackageState.java │ │ │ ├── Status.java │ │ │ ├── ApplicationLogs.java │ │ │ ├── HealthCheckType.java │ │ │ ├── ServicePlanVisibility.java │ │ │ ├── LifecycleType.java │ │ │ ├── InstanceInfo.java │ │ │ ├── DockerCredentials.java │ │ │ ├── Upload.java │ │ │ ├── DockerInfo.java │ │ │ ├── CloudDomain.java │ │ │ ├── InstanceState.java │ │ │ ├── CloudOrganization.java │ │ │ ├── InstancesInfo.java │ │ │ ├── DockerData.java │ │ │ ├── DropletInfo.java │ │ │ ├── Lifecycle.java │ │ │ ├── ErrorDetails.java │ │ │ ├── CloudStack.java │ │ │ ├── CloudSpace.java │ │ │ ├── SecurityGroupRule.java │ │ │ ├── CloudEntity.java │ │ │ ├── RouteDestination.java │ │ │ ├── CloudServiceBinding.java │ │ │ ├── CloudApplication.java │ │ │ ├── CloudServiceBroker.java │ │ │ ├── CloudAsyncJob.java │ │ │ ├── BitsData.java │ │ │ ├── CloudMetadata.java │ │ │ ├── ApplicationLog.java │ │ │ ├── CloudServiceKey.java │ │ │ ├── CloudServicePlan.java │ │ │ ├── CloudProcess.java │ │ │ ├── UserRole.java │ │ │ ├── CloudServiceOffering.java │ │ │ ├── CloudJob.java │ │ │ ├── CloudEvent.java │ │ │ ├── CloudServiceInstance.java │ │ │ ├── CloudTask.java │ │ │ ├── CloudBuild.java │ │ │ ├── Staging.java │ │ │ ├── CloudPackage.java │ │ │ ├── CloudRoute.java │ │ │ ├── ServiceOperation.java │ │ │ └── ServiceCredentialBindingOperation.java │ │ │ ├── ApplicationServicesUpdateCallback.java │ │ │ ├── ServiceBindingOperationCallback.java │ │ │ ├── Constants.java │ │ │ ├── ImmutablesStyle.java │ │ │ ├── ApplicationLogListener.java │ │ │ ├── CloudException.java │ │ │ ├── adapters │ │ │ ├── ApplicationLogsResponse.java │ │ │ ├── RawUserRole.java │ │ │ ├── RawCloudDomain.java │ │ │ ├── RawCloudStack.java │ │ │ ├── OAuthTokenProvider.java │ │ │ ├── RawV3CloudServiceInstance.java │ │ │ ├── ApplicationLogEntity.java │ │ │ ├── RawCloudServiceBroker.java │ │ │ ├── RawCloudAsyncJob.java │ │ │ ├── RawCloudServicePlan.java │ │ │ ├── RawCloudServiceKey.java │ │ │ ├── RawCloudTask.java │ │ │ ├── RawInstancesInfo.java │ │ │ ├── RawCloudServiceBinding.java │ │ │ ├── RawCloudProcess.java │ │ │ ├── RawCloudServiceInstance.java │ │ │ ├── RawCloudEvent.java │ │ │ ├── RawCloudServiceOffering.java │ │ │ ├── RawCloudEntity.java │ │ │ ├── RawCloudBuild.java │ │ │ ├── RawCloudPackage.java │ │ │ ├── RawCloudApplication.java │ │ │ └── RawCloudRoute.java │ │ │ ├── util │ │ │ ├── UriUtil.java │ │ │ ├── AuthorizationEndpointGetter.java │ │ │ ├── JobV3Util.java │ │ │ ├── CloudUtil.java │ │ │ ├── JsonUtil.java │ │ │ └── RestUtil.java │ │ │ ├── oauth2 │ │ │ ├── Oauth2AccessTokenResponse.java │ │ │ ├── OAuth2AccessTokenWithAdditionalInfo.java │ │ │ └── TokenFactory.java │ │ │ ├── dto │ │ │ └── ApplicationToCreateDto.java │ │ │ ├── CloudControllerException.java │ │ │ ├── CloudServiceBrokerException.java │ │ │ ├── CloudOperationException.java │ │ │ ├── Messages.java │ │ │ ├── UploadStatusCallback.java │ │ │ └── rest │ │ │ ├── CloudControllerResponseErrorHandler.java │ │ │ └── CloudControllerRestClientFactory.java │ │ └── module-info.java └── test │ ├── resources │ └── com │ │ └── sap │ │ └── cloudfoundry │ │ └── client │ │ └── facade │ │ ├── staticfile.zip │ │ ├── rest │ │ ├── v2-error.json │ │ └── v3-error.json │ │ ├── service-broker-env.json │ │ └── util │ │ ├── build.json │ │ ├── package.json │ │ ├── task.json │ │ ├── service-instance.json │ │ └── application.json │ └── java │ └── com │ └── sap │ └── cloudfoundry │ └── client │ └── facade │ ├── util │ ├── UriUtilTest.java │ └── JsonUtilTest.java │ ├── broker │ ├── FailConfiguration.java │ └── ServiceBrokerConfiguration.java │ ├── adapters │ ├── RawCloudStackTest.java │ ├── RawUserRoleTest.java │ ├── RawCloudDomainTest.java │ ├── RawInstancesInfoTest.java │ ├── RawCloudServiceBrokerTest.java │ ├── RawCloudTaskTest.java │ ├── RawCloudAsyncJobTest.java │ ├── RawCloudServiceBindingTest.java │ └── RawCloudServiceKeyTest.java │ ├── IntegrationTestConstants.java │ ├── DomainsCloudControllerClientIntegrationTest.java │ ├── ITVariable.java │ ├── ApplicationUtil.java │ └── CloudControllerClientIntegrationTest.java ├── .gitignore ├── .travis.yml ├── .github └── workflows │ ├── pull-request-build.yml │ ├── main.yml │ └── codeql.yml ├── .travis.settings.xml ├── README.md └── .reuse └── dep5 /src/main/java/com/sap/cloudfoundry/client/facade/package-info.java: -------------------------------------------------------------------------------- 1 | @ImmutablesStyle 2 | package com.sap.cloudfoundry.client.facade; 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | target 3 | .project 4 | .settings/ 5 | .classpath 6 | *.log 7 | **/.factorypath 8 | *.iml 9 | .DS_Store 10 | 11 | -------------------------------------------------------------------------------- /src/main/java/com/sap/cloudfoundry/client/facade/Nullable.java: -------------------------------------------------------------------------------- 1 | package com.sap.cloudfoundry.client.facade; 2 | 3 | public @interface Nullable { 4 | 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/com/sap/cloudfoundry/client/facade/SkipNulls.java: -------------------------------------------------------------------------------- 1 | package com.sap.cloudfoundry.client.facade; 2 | 3 | public @interface SkipNulls { 4 | 5 | } 6 | -------------------------------------------------------------------------------- /src/test/resources/com/sap/cloudfoundry/client/facade/staticfile.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAP/cf-java-client-sap/HEAD/src/test/resources/com/sap/cloudfoundry/client/facade/staticfile.zip -------------------------------------------------------------------------------- /src/main/java/com/sap/cloudfoundry/client/facade/domain/Derivable.java: -------------------------------------------------------------------------------- 1 | package com.sap.cloudfoundry.client.facade.domain; 2 | 3 | public interface Derivable { 4 | 5 | T derive(); 6 | 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/com/sap/cloudfoundry/client/facade/domain/PackageState.java: -------------------------------------------------------------------------------- 1 | package com.sap.cloudfoundry.client.facade.domain; 2 | 3 | public enum PackageState { 4 | PENDING, STAGED, FAILED, 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/com/sap/cloudfoundry/client/facade/domain/Status.java: -------------------------------------------------------------------------------- 1 | package com.sap.cloudfoundry.client.facade.domain; 2 | 3 | public enum Status { 4 | AWAITING_UPLOAD, COPYING, EXPIRED, PROCESSING_UPLOAD, READY, FAILED, 5 | } -------------------------------------------------------------------------------- /src/main/java/com/sap/cloudfoundry/client/facade/domain/ApplicationLogs.java: -------------------------------------------------------------------------------- 1 | package com.sap.cloudfoundry.client.facade.domain; 2 | 3 | import java.util.ArrayList; 4 | 5 | public class ApplicationLogs extends ArrayList { 6 | 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/com/sap/cloudfoundry/client/facade/ApplicationServicesUpdateCallback.java: -------------------------------------------------------------------------------- 1 | package com.sap.cloudfoundry.client.facade; 2 | 3 | public interface ApplicationServicesUpdateCallback { 4 | 5 | void onError(CloudOperationException e, String applicationName, String serviceInstanceName); 6 | } 7 | -------------------------------------------------------------------------------- /src/main/java/com/sap/cloudfoundry/client/facade/ServiceBindingOperationCallback.java: -------------------------------------------------------------------------------- 1 | package com.sap.cloudfoundry.client.facade; 2 | 3 | import java.util.UUID; 4 | 5 | public interface ServiceBindingOperationCallback { 6 | 7 | void onError(CloudOperationException e, UUID serviceBindingGuid); 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/com/sap/cloudfoundry/client/facade/Constants.java: -------------------------------------------------------------------------------- 1 | package com.sap.cloudfoundry.client.facade; 2 | 3 | public class Constants { 4 | 5 | private Constants() { 6 | 7 | } 8 | 9 | public static final String PACKAGE = "package"; 10 | 11 | public static final String ORIGIN_KEY = "origin"; 12 | 13 | } -------------------------------------------------------------------------------- /src/main/java/com/sap/cloudfoundry/client/facade/domain/HealthCheckType.java: -------------------------------------------------------------------------------- 1 | package com.sap.cloudfoundry.client.facade.domain; 2 | 3 | public enum HealthCheckType { 4 | HTTP, PORT, PROCESS, @Deprecated NONE; 5 | 6 | @Override 7 | public String toString() { 8 | return this.name().toLowerCase(); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/com/sap/cloudfoundry/client/facade/domain/ServicePlanVisibility.java: -------------------------------------------------------------------------------- 1 | package com.sap.cloudfoundry.client.facade.domain; 2 | 3 | public enum ServicePlanVisibility { 4 | 5 | PUBLIC, ADMIN, ORGANIZATION; 6 | 7 | @Override 8 | public String toString() { 9 | return name().toLowerCase(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/com/sap/cloudfoundry/client/facade/domain/LifecycleType.java: -------------------------------------------------------------------------------- 1 | package com.sap.cloudfoundry.client.facade.domain; 2 | 3 | public enum LifecycleType { 4 | 5 | BUILDPACK, DOCKER, KPACK, CNB; 6 | 7 | public String toString() { 8 | return this.name() 9 | .toLowerCase(); 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /src/test/resources/com/sap/cloudfoundry/client/facade/rest/v2-error.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Request invalid due to parse error: Field: name, Error: Missing field name, Field: space_guid, Error: Missing field space_guid, Field: service_plan_guid, Error: Missing field service_plan_guid", 3 | "error_code": "CF-MessageParseError", 4 | "code": 1001 5 | } -------------------------------------------------------------------------------- /src/main/java/com/sap/cloudfoundry/client/facade/ImmutablesStyle.java: -------------------------------------------------------------------------------- 1 | package com.sap.cloudfoundry.client.facade; 2 | 3 | import org.immutables.value.Value; 4 | 5 | @Value.Style(depluralize = true, typeImmutable = "Immutable*", typeImmutableNested = "Immutable*", visibility = Value.Style.ImplementationVisibility.PUBLIC) 6 | @interface ImmutablesStyle { 7 | 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/com/sap/cloudfoundry/client/facade/ApplicationLogListener.java: -------------------------------------------------------------------------------- 1 | package com.sap.cloudfoundry.client.facade; 2 | 3 | import com.sap.cloudfoundry.client.facade.domain.ApplicationLog; 4 | 5 | public interface ApplicationLogListener { 6 | 7 | void onComplete(); 8 | 9 | void onError(Throwable exception); 10 | 11 | void onMessage(ApplicationLog log); 12 | 13 | } 14 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | jdk: 3 | - openjdk11 4 | notifications: 5 | email: 6 | recipients: 7 | - DL_563CD7577BCF844DBD000037@exchange.sap.corp 8 | on_success: change 9 | on_failure: always 10 | script: 11 | - 'if [ "$TRAVIS_PULL_REQUEST" != "false" ]; then mvn clean install; fi' 12 | - 'if [ "$TRAVIS_PULL_REQUEST" = "false" ]; then mvn deploy --settings .travis.settings.xml; fi' 13 | git: 14 | depth: false 15 | -------------------------------------------------------------------------------- /src/test/resources/com/sap/cloudfoundry/client/facade/rest/v3-error.json: -------------------------------------------------------------------------------- 1 | { 2 | "errors": [ 3 | { 4 | "detail": "memory_in_mb exceeds organization memory quota", 5 | "title": "CF-UnprocessableEntity", 6 | "code": 10008 7 | }, 8 | { 9 | "detail": "disk_in_mb exceeds organization disk quota", 10 | "title": "CF-UnprocessableEntity", 11 | "code": 10008 12 | } 13 | ] 14 | } -------------------------------------------------------------------------------- /.github/workflows/pull-request-build.yml: -------------------------------------------------------------------------------- 1 | name: SAP Java Client Maven build 2 | 3 | on: 4 | pull_request: 5 | branches: [ master ] 6 | 7 | jobs: 8 | build: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v3 12 | - name: Set up JDK 17 13 | uses: actions/setup-java@v4 14 | with: 15 | java-version: '17' 16 | distribution: 'sapmachine' 17 | 18 | - name: Build with Maven 19 | run: mvn clean install 20 | -------------------------------------------------------------------------------- /src/main/java/com/sap/cloudfoundry/client/facade/CloudException.java: -------------------------------------------------------------------------------- 1 | package com.sap.cloudfoundry.client.facade; 2 | 3 | @SuppressWarnings("serial") 4 | public class CloudException extends RuntimeException { 5 | 6 | public CloudException(Throwable cause) { 7 | super(cause); 8 | } 9 | 10 | public CloudException(String message, Throwable cause) { 11 | super(message, cause); 12 | } 13 | 14 | public CloudException(String message) { 15 | super(message); 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /.travis.settings.xml: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | ossrh 8 | ${env.BUILD_USERNAME} 9 | ${env.BUILD_PASSWORD} 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/main/java/com/sap/cloudfoundry/client/facade/domain/InstanceInfo.java: -------------------------------------------------------------------------------- 1 | package com.sap.cloudfoundry.client.facade.domain; 2 | 3 | import org.immutables.value.Value; 4 | 5 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 6 | import com.fasterxml.jackson.databind.annotation.JsonSerialize; 7 | 8 | @Value.Immutable 9 | @JsonSerialize(as = ImmutableInstanceInfo.class) 10 | @JsonDeserialize(as = ImmutableInstanceInfo.class) 11 | public interface InstanceInfo { 12 | 13 | int getIndex(); 14 | 15 | InstanceState getState(); 16 | 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/com/sap/cloudfoundry/client/facade/domain/DockerCredentials.java: -------------------------------------------------------------------------------- 1 | package com.sap.cloudfoundry.client.facade.domain; 2 | 3 | import org.immutables.value.Value; 4 | 5 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 6 | import com.fasterxml.jackson.databind.annotation.JsonSerialize; 7 | 8 | @Value.Immutable 9 | @JsonSerialize(as = ImmutableDockerCredentials.class) 10 | @JsonDeserialize(as = ImmutableDockerCredentials.class) 11 | public interface DockerCredentials { 12 | 13 | String getUsername(); 14 | 15 | String getPassword(); 16 | 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/com/sap/cloudfoundry/client/facade/domain/Upload.java: -------------------------------------------------------------------------------- 1 | package com.sap.cloudfoundry.client.facade.domain; 2 | 3 | import org.immutables.value.Value; 4 | 5 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 6 | import com.fasterxml.jackson.databind.annotation.JsonSerialize; 7 | import com.sap.cloudfoundry.client.facade.Nullable; 8 | 9 | @Value.Immutable 10 | @JsonSerialize(as = ImmutableUpload.class) 11 | @JsonDeserialize(as = ImmutableUpload.class) 12 | public interface Upload { 13 | 14 | Status getStatus(); 15 | 16 | @Nullable 17 | ErrorDetails getErrorDetails(); 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/com/sap/cloudfoundry/client/facade/domain/DockerInfo.java: -------------------------------------------------------------------------------- 1 | package com.sap.cloudfoundry.client.facade.domain; 2 | 3 | import org.immutables.value.Value; 4 | 5 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 6 | import com.fasterxml.jackson.databind.annotation.JsonSerialize; 7 | import com.sap.cloudfoundry.client.facade.Nullable; 8 | 9 | @Value.Immutable 10 | @JsonSerialize(as = ImmutableDockerInfo.class) 11 | @JsonDeserialize(as = ImmutableDockerInfo.class) 12 | public interface DockerInfo { 13 | 14 | String getImage(); 15 | 16 | @Nullable 17 | DockerCredentials getCredentials(); 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/com/sap/cloudfoundry/client/facade/domain/CloudDomain.java: -------------------------------------------------------------------------------- 1 | package com.sap.cloudfoundry.client.facade.domain; 2 | 3 | import org.immutables.value.Value; 4 | 5 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 6 | import com.fasterxml.jackson.databind.annotation.JsonSerialize; 7 | 8 | @Value.Immutable 9 | @JsonSerialize(as = ImmutableCloudDomain.class) 10 | @JsonDeserialize(as = ImmutableCloudDomain.class) 11 | public abstract class CloudDomain extends CloudEntity implements Derivable { 12 | 13 | @Override 14 | public CloudDomain derive() { 15 | return this; 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/com/sap/cloudfoundry/client/facade/domain/InstanceState.java: -------------------------------------------------------------------------------- 1 | package com.sap.cloudfoundry.client.facade.domain; 2 | 3 | import org.cloudfoundry.client.v3.processes.ProcessState; 4 | 5 | public enum InstanceState { 6 | CRASHED, DOWN, RUNNING, STARTING, UNKNOWN; 7 | 8 | public static InstanceState valueOfWithDefault(ProcessState state) { 9 | if (state == null) { 10 | return UNKNOWN; 11 | } 12 | try { 13 | return InstanceState.valueOf(state.getValue()); 14 | } catch (IllegalArgumentException e) { 15 | return InstanceState.UNKNOWN; 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/com/sap/cloudfoundry/client/facade/adapters/ApplicationLogsResponse.java: -------------------------------------------------------------------------------- 1 | package com.sap.cloudfoundry.client.facade.adapters; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import com.fasterxml.jackson.annotation.JsonRootName; 5 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 6 | import org.immutables.value.Value; 7 | 8 | import java.util.List; 9 | 10 | @Value.Immutable 11 | @JsonRootName("envelopes") 12 | @JsonDeserialize(as = ImmutableApplicationLogsResponse.class) 13 | public interface ApplicationLogsResponse { 14 | 15 | @JsonProperty("batch") 16 | List getLogs(); 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/com/sap/cloudfoundry/client/facade/domain/CloudOrganization.java: -------------------------------------------------------------------------------- 1 | package com.sap.cloudfoundry.client.facade.domain; 2 | 3 | import org.immutables.value.Value; 4 | 5 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 6 | import com.fasterxml.jackson.databind.annotation.JsonSerialize; 7 | 8 | @Value.Immutable 9 | @JsonSerialize(as = ImmutableCloudOrganization.class) 10 | @JsonDeserialize(as = ImmutableCloudOrganization.class) 11 | public abstract class CloudOrganization extends CloudEntity implements Derivable { 12 | 13 | @Override 14 | public CloudOrganization derive() { 15 | return this; 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/com/sap/cloudfoundry/client/facade/util/UriUtil.java: -------------------------------------------------------------------------------- 1 | package com.sap.cloudfoundry.client.facade.util; 2 | 3 | import org.springframework.web.util.UriUtils; 4 | 5 | import java.nio.charset.Charset; 6 | import java.util.List; 7 | 8 | public class UriUtil { 9 | 10 | private UriUtil() { 11 | // prevents initialization 12 | } 13 | 14 | public static String encodeChars(String queryParam, List charsToEncode) { 15 | for (String charToEncode: charsToEncode) { 16 | queryParam = queryParam.replaceAll(charToEncode, UriUtils.encode(charToEncode, Charset.defaultCharset())); 17 | } 18 | return queryParam; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/com/sap/cloudfoundry/client/facade/domain/InstancesInfo.java: -------------------------------------------------------------------------------- 1 | package com.sap.cloudfoundry.client.facade.domain; 2 | 3 | import java.util.Collections; 4 | import java.util.List; 5 | 6 | import org.immutables.value.Value; 7 | 8 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 9 | import com.fasterxml.jackson.databind.annotation.JsonSerialize; 10 | 11 | @Value.Immutable 12 | @JsonSerialize(as = ImmutableInstancesInfo.class) 13 | @JsonDeserialize(as = ImmutableInstancesInfo.class) 14 | public interface InstancesInfo { 15 | 16 | @Value.Default 17 | default List getInstances() { 18 | return Collections.emptyList(); 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/com/sap/cloudfoundry/client/facade/domain/DockerData.java: -------------------------------------------------------------------------------- 1 | package com.sap.cloudfoundry.client.facade.domain; 2 | 3 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 4 | import com.fasterxml.jackson.databind.annotation.JsonSerialize; 5 | import com.sap.cloudfoundry.client.facade.Nullable; 6 | import org.immutables.value.Value; 7 | 8 | @Value.Immutable 9 | @JsonSerialize(as = ImmutableDockerData.class) 10 | @JsonDeserialize(as = ImmutableDockerData.class) 11 | public interface DockerData extends CloudPackage.PackageData { 12 | 13 | String getImage(); 14 | 15 | @Nullable 16 | String getUsername(); 17 | 18 | @Nullable 19 | String getPassword(); 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/com/sap/cloudfoundry/client/facade/domain/DropletInfo.java: -------------------------------------------------------------------------------- 1 | package com.sap.cloudfoundry.client.facade.domain; 2 | 3 | import java.util.UUID; 4 | 5 | import org.immutables.value.Value; 6 | 7 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 8 | import com.fasterxml.jackson.databind.annotation.JsonSerialize; 9 | import com.sap.cloudfoundry.client.facade.Nullable; 10 | 11 | @Value.Immutable 12 | @JsonSerialize(as = ImmutableDropletInfo.class) 13 | @JsonDeserialize(as = ImmutableDropletInfo.class) 14 | public interface DropletInfo { 15 | 16 | @Nullable 17 | @Value.Parameter 18 | UUID getGuid(); 19 | 20 | @Nullable 21 | @Value.Parameter 22 | UUID getPackageGuid(); 23 | } -------------------------------------------------------------------------------- /src/main/java/com/sap/cloudfoundry/client/facade/domain/Lifecycle.java: -------------------------------------------------------------------------------- 1 | package com.sap.cloudfoundry.client.facade.domain; 2 | 3 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 4 | import com.fasterxml.jackson.databind.annotation.JsonSerialize; 5 | import com.sap.cloudfoundry.client.facade.Nullable; 6 | import org.cloudfoundry.AllowNulls; 7 | import org.immutables.value.Value; 8 | 9 | import java.util.Map; 10 | 11 | @Value.Immutable 12 | @JsonSerialize(as = ImmutableLifecycle.class) 13 | @JsonDeserialize(as = ImmutableLifecycle.class) 14 | public interface Lifecycle { 15 | 16 | LifecycleType getType(); 17 | 18 | @Nullable 19 | @AllowNulls 20 | Map getData(); 21 | 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/sap/cloudfoundry/client/facade/adapters/RawUserRole.java: -------------------------------------------------------------------------------- 1 | package com.sap.cloudfoundry.client.facade.adapters; 2 | 3 | import com.sap.cloudfoundry.client.facade.domain.Derivable; 4 | import org.cloudfoundry.client.v3.roles.RoleResource; 5 | import org.immutables.value.Value; 6 | 7 | import com.sap.cloudfoundry.client.facade.domain.UserRole; 8 | 9 | @Value.Immutable 10 | public abstract class RawUserRole implements Derivable { 11 | 12 | @Value.Parameter 13 | public abstract RoleResource getRoleResource(); 14 | 15 | @Override 16 | public UserRole derive() { 17 | RoleResource role = getRoleResource(); 18 | return UserRole.fromRoleType(role.getType()); 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/com/sap/cloudfoundry/client/facade/domain/ErrorDetails.java: -------------------------------------------------------------------------------- 1 | package com.sap.cloudfoundry.client.facade.domain; 2 | 3 | import org.immutables.value.Value; 4 | 5 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 6 | import com.fasterxml.jackson.databind.annotation.JsonSerialize; 7 | import com.sap.cloudfoundry.client.facade.Nullable; 8 | 9 | @Value.Immutable 10 | @JsonSerialize(as = ImmutableErrorDetails.class) 11 | @JsonDeserialize(as = ImmutableErrorDetails.class) 12 | public interface ErrorDetails { 13 | 14 | @Value.Default 15 | default long getCode() { 16 | return 0; 17 | } 18 | 19 | @Nullable 20 | String getDescription(); 21 | 22 | @Nullable 23 | String getErrorCode(); 24 | 25 | } -------------------------------------------------------------------------------- /src/test/java/com/sap/cloudfoundry/client/facade/util/UriUtilTest.java: -------------------------------------------------------------------------------- 1 | package com.sap.cloudfoundry.client.facade.util; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertEquals; 4 | 5 | import java.util.List; 6 | 7 | import org.junit.jupiter.api.Test; 8 | 9 | class UriUtilTest { 10 | 11 | @Test 12 | void testEncodeCharsWithOneChar() { 13 | String encodedString = UriUtil.encodeChars("space,name", List.of(",")); 14 | assertEquals("space%2Cname", encodedString); 15 | } 16 | 17 | @Test 18 | void testEncodeCharsWithMultipleChars() { 19 | String encodedString = UriUtil.encodeChars("org space,name", List.of(",", " ")); 20 | assertEquals("org%20space%2Cname", encodedString); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/test/resources/com/sap/cloudfoundry/client/facade/service-broker-env.json: -------------------------------------------------------------------------------- 1 | { 2 | "TARGET_RUNTIME": "tomcat", 3 | "CATALOG": { 4 | "services": [ 5 | { 6 | "name": "finch", 7 | "description": "finch is a small bird", 8 | "bindable": true, 9 | "plan_updateable": true, 10 | "instances_retrievable": true, 11 | "bindings_retrievable": true, 12 | "plans": [ 13 | { 14 | "name": "white", 15 | "description": "white finch", 16 | "free": true 17 | }, 18 | { 19 | "name": "grey", 20 | "description": "grey finch", 21 | "free": true 22 | } 23 | ] 24 | } 25 | ] 26 | } 27 | } -------------------------------------------------------------------------------- /src/main/java/com/sap/cloudfoundry/client/facade/domain/CloudStack.java: -------------------------------------------------------------------------------- 1 | package com.sap.cloudfoundry.client.facade.domain; 2 | 3 | import org.immutables.value.Value; 4 | 5 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 6 | import com.fasterxml.jackson.databind.annotation.JsonSerialize; 7 | import com.sap.cloudfoundry.client.facade.Nullable; 8 | 9 | @Value.Immutable 10 | @JsonSerialize(as = ImmutableCloudStack.class) 11 | @JsonDeserialize(as = ImmutableCloudStack.class) 12 | public abstract class CloudStack extends CloudEntity implements Derivable { 13 | 14 | @Nullable 15 | public abstract String getDescription(); 16 | 17 | @Override 18 | public CloudStack derive() { 19 | return this; 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: SAP Java Client Main 2 | 3 | on: 4 | push: 5 | branches: [ "master" ] 6 | workflow_dispatch: 7 | 8 | jobs: 9 | publish-maven-central: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v3 13 | - name: Set up Maven Central Repository 14 | uses: actions/setup-java@v4 15 | with: 16 | java-version: '17' 17 | distribution: 'sapmachine' 18 | server-id: ossrh 19 | server-username: MAVEN_USERNAME 20 | server-password: MAVEN_PASSWORD 21 | - name: Publish package 22 | run: mvn --batch-mode deploy 23 | env: 24 | MAVEN_USERNAME: ${{ secrets.OSSRH_USERNAME }} 25 | MAVEN_PASSWORD: ${{ secrets.OSSRH_TOKEN }} 26 | -------------------------------------------------------------------------------- /src/main/java/com/sap/cloudfoundry/client/facade/domain/CloudSpace.java: -------------------------------------------------------------------------------- 1 | package com.sap.cloudfoundry.client.facade.domain; 2 | 3 | import org.immutables.value.Value; 4 | 5 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 6 | import com.fasterxml.jackson.databind.annotation.JsonSerialize; 7 | import com.sap.cloudfoundry.client.facade.Nullable; 8 | 9 | @Value.Immutable 10 | @JsonSerialize(as = ImmutableCloudSpace.class) 11 | @JsonDeserialize(as = ImmutableCloudSpace.class) 12 | public abstract class CloudSpace extends CloudEntity implements Derivable { 13 | 14 | @Nullable 15 | public abstract CloudOrganization getOrganization(); 16 | 17 | @Override 18 | public CloudSpace derive() { 19 | return this; 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/sap/cloudfoundry/client/facade/domain/SecurityGroupRule.java: -------------------------------------------------------------------------------- 1 | package com.sap.cloudfoundry.client.facade.domain; 2 | 3 | import org.immutables.value.Value; 4 | 5 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 6 | import com.fasterxml.jackson.databind.annotation.JsonSerialize; 7 | import com.sap.cloudfoundry.client.facade.Nullable; 8 | 9 | @Value.Immutable 10 | @JsonSerialize(as = ImmutableSecurityGroupRule.class) 11 | @JsonDeserialize(as = ImmutableSecurityGroupRule.class) 12 | public interface SecurityGroupRule { 13 | 14 | String getProtocol(); 15 | 16 | String getPorts(); 17 | 18 | String getDestination(); 19 | 20 | @Nullable 21 | Boolean getLog(); 22 | 23 | @Nullable 24 | Integer getType(); 25 | 26 | @Nullable 27 | Integer getCode(); 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/com/sap/cloudfoundry/client/facade/domain/CloudEntity.java: -------------------------------------------------------------------------------- 1 | package com.sap.cloudfoundry.client.facade.domain; 2 | 3 | import java.util.UUID; 4 | 5 | import org.cloudfoundry.client.v3.Metadata; 6 | 7 | import com.sap.cloudfoundry.client.facade.Nullable; 8 | 9 | /** 10 | * Do not extend {@code Derivable} in this interface. It is tempting, because all of its children have the same implementation, but 11 | * implementing the {@code derive()} method here leads to this bug: https://github.com/immutables/immutables/issues/1045 12 | * 13 | */ 14 | public abstract class CloudEntity { 15 | 16 | @Nullable 17 | public abstract String getName(); 18 | 19 | @Nullable 20 | public abstract CloudMetadata getMetadata(); 21 | 22 | @Nullable 23 | public abstract Metadata getV3Metadata(); 24 | 25 | public UUID getGuid() { 26 | return getMetadata().getGuid(); 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/com/sap/cloudfoundry/client/facade/adapters/RawCloudDomain.java: -------------------------------------------------------------------------------- 1 | package com.sap.cloudfoundry.client.facade.adapters; 2 | 3 | import org.cloudfoundry.client.v3.domains.Domain; 4 | import org.immutables.value.Value; 5 | 6 | import com.sap.cloudfoundry.client.facade.domain.CloudDomain; 7 | import com.sap.cloudfoundry.client.facade.domain.ImmutableCloudDomain; 8 | 9 | @Value.Immutable 10 | public abstract class RawCloudDomain extends RawCloudEntity { 11 | 12 | @Value.Parameter 13 | public abstract Domain getResource(); 14 | 15 | @Override 16 | public CloudDomain derive() { 17 | Domain resource = getResource(); 18 | return ImmutableCloudDomain.builder() 19 | .metadata(parseResourceMetadata(resource)) 20 | .name(resource.getName()) 21 | .build(); 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /src/test/resources/com/sap/cloudfoundry/client/facade/util/build.json: -------------------------------------------------------------------------------- 1 | { 2 | "guid": "585bc3c1-3743-497d-88b0-403ad6b56d16", 3 | "created_at": "2016-03-28T23:39:34Z", 4 | "updated_at": "2016-06-08T19:41:26Z", 5 | "created_by": { 6 | "guid": "3cb4e243-bed4-49d5-8739-f8b45abdec1c", 7 | "name": "bill", 8 | "email": "bill@example.com" 9 | }, 10 | "state": "STAGING", 11 | "error": null, 12 | "lifecycle": { 13 | "type": "buildpack", 14 | "data": { 15 | "buildpacks": [ "ruby_buildpack" ], 16 | "stack": "cflinuxfs2" 17 | } 18 | }, 19 | "package": { 20 | "guid": "8e4da443-f255-499c-8b47-b3729b5b7432" 21 | }, 22 | "droplet": null, 23 | "links": { 24 | "self": { 25 | "href": "https://api.example.org/v3/builds/585bc3c1-3743-497d-88b0-403ad6b56d16" 26 | }, 27 | "app": { 28 | "href": "https://api.example.org/v3/apps/7b34f1cf-7e73-428a-bb5a-8a17a8058396" 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /src/test/java/com/sap/cloudfoundry/client/facade/broker/FailConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.sap.cloudfoundry.client.facade.broker; 2 | 3 | import java.util.List; 4 | import java.util.UUID; 5 | 6 | import org.immutables.value.Value; 7 | 8 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 9 | import com.fasterxml.jackson.databind.annotation.JsonSerialize; 10 | 11 | @Value.Immutable 12 | @JsonSerialize(as = ImmutableFailConfiguration.class) 13 | @JsonDeserialize(as = ImmutableFailConfiguration.class) 14 | public interface FailConfiguration { 15 | 16 | Integer getStatus(); 17 | 18 | String getOperationType(); 19 | 20 | List getInstanceIds(); 21 | 22 | enum OperationType { 23 | CREATE, UPDATE, DELETE, BIND, UNBIND, CREATE_SERVICE_KEY, DELETE_SERVICE_KEY; 24 | 25 | @Override 26 | public String toString() { 27 | return name().toLowerCase(); 28 | } 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/com/sap/cloudfoundry/client/facade/adapters/RawCloudStack.java: -------------------------------------------------------------------------------- 1 | package com.sap.cloudfoundry.client.facade.adapters; 2 | 3 | import org.cloudfoundry.client.v3.stacks.Stack; 4 | import org.immutables.value.Value; 5 | 6 | import com.sap.cloudfoundry.client.facade.domain.CloudStack; 7 | import com.sap.cloudfoundry.client.facade.domain.ImmutableCloudStack; 8 | 9 | @Value.Immutable 10 | public abstract class RawCloudStack extends RawCloudEntity { 11 | 12 | @Value.Parameter 13 | public abstract Stack getStack(); 14 | 15 | @Override 16 | public CloudStack derive() { 17 | Stack stack = getStack(); 18 | return ImmutableCloudStack.builder() 19 | .metadata(parseResourceMetadata(stack)) 20 | .name(stack.getName()) 21 | .description(stack.getDescription()) 22 | .build(); 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/test/resources/com/sap/cloudfoundry/client/facade/util/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "guid": "44f7c078-0934-470f-9883-4fcddc5b8f13", 3 | "type": "bits", 4 | "data": { 5 | "checksum": { 6 | "type": "sha256", 7 | "value": null 8 | }, 9 | "error": null 10 | }, 11 | "state": "PROCESSING_UPLOAD", 12 | "created_at": "2015-11-13T19:02:56Z", 13 | "updated_at": "2016-06-08T19:41:26Z", 14 | "links": { 15 | "self": { 16 | "href": "https://api.example.org/v3/packages/44f7c078-0934-470f-9883-4fcddc5b8f13" 17 | }, 18 | "upload": { 19 | "href": "https://api.example.org/v3/packages/44f7c078-0934-470f-9883-4fcddc5b8f13/upload", 20 | "method": "POST" 21 | }, 22 | "download": { 23 | "href": "https://api.example.org/v3/packages/44f7c078-0934-470f-9883-4fcddc5b8f13/download", 24 | "method": "GET" 25 | }, 26 | "app": { 27 | "href": "https://api.example.org/v3/apps/1d3bf0ec-5806-43c4-b64e-8364dba1086a" 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /src/main/java/com/sap/cloudfoundry/client/facade/domain/RouteDestination.java: -------------------------------------------------------------------------------- 1 | package com.sap.cloudfoundry.client.facade.domain; 2 | 3 | import java.util.UUID; 4 | 5 | import org.immutables.value.Value; 6 | 7 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 8 | import com.fasterxml.jackson.databind.annotation.JsonSerialize; 9 | import com.sap.cloudfoundry.client.facade.Nullable; 10 | 11 | @Value.Immutable 12 | @JsonSerialize(as = ImmutableRouteDestination.class) 13 | @JsonDeserialize(as = ImmutableRouteDestination.class) 14 | public abstract class RouteDestination extends CloudEntity implements Derivable { 15 | 16 | public abstract UUID getApplicationGuid(); 17 | 18 | @Nullable 19 | public abstract Integer getPort(); 20 | 21 | @Nullable 22 | public abstract Integer getWeight(); 23 | 24 | public abstract String getProtocol(); 25 | 26 | @Override 27 | public RouteDestination derive() { 28 | return this; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/sap/cloudfoundry/client/facade/domain/CloudServiceBinding.java: -------------------------------------------------------------------------------- 1 | package com.sap.cloudfoundry.client.facade.domain; 2 | 3 | import java.util.UUID; 4 | 5 | import org.cloudfoundry.Nullable; 6 | import org.immutables.value.Value; 7 | 8 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 9 | import com.fasterxml.jackson.databind.annotation.JsonSerialize; 10 | 11 | @Value.Immutable 12 | @JsonSerialize(as = ImmutableCloudServiceBinding.class) 13 | @JsonDeserialize(as = ImmutableCloudServiceBinding.class) 14 | public abstract class CloudServiceBinding extends CloudEntity implements Derivable { 15 | 16 | @Nullable 17 | public abstract UUID getApplicationGuid(); 18 | 19 | public abstract UUID getServiceInstanceGuid(); 20 | 21 | @Nullable 22 | public abstract ServiceCredentialBindingOperation getServiceBindingOperation(); 23 | 24 | @Override 25 | public CloudServiceBinding derive() { 26 | return this; 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/com/sap/cloudfoundry/client/facade/adapters/OAuthTokenProvider.java: -------------------------------------------------------------------------------- 1 | package com.sap.cloudfoundry.client.facade.adapters; 2 | 3 | import org.cloudfoundry.reactor.ConnectionContext; 4 | import org.cloudfoundry.reactor.TokenProvider; 5 | 6 | import com.sap.cloudfoundry.client.facade.oauth2.OAuth2AccessTokenWithAdditionalInfo; 7 | import com.sap.cloudfoundry.client.facade.oauth2.OAuthClient; 8 | 9 | import reactor.core.publisher.Mono; 10 | 11 | public class OAuthTokenProvider implements TokenProvider { 12 | 13 | private final OAuthClient oAuthClient; 14 | 15 | public OAuthTokenProvider(OAuthClient oAuthClient) { 16 | this.oAuthClient = oAuthClient; 17 | } 18 | 19 | @Override 20 | public Mono getToken(ConnectionContext connectionContext) { 21 | return Mono.fromSupplier(() -> { 22 | OAuth2AccessTokenWithAdditionalInfo token = oAuthClient.getToken(); 23 | return token.getAuthorizationHeaderValue(); 24 | }); 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/com/sap/cloudfoundry/client/facade/domain/CloudApplication.java: -------------------------------------------------------------------------------- 1 | package com.sap.cloudfoundry.client.facade.domain; 2 | 3 | import org.immutables.value.Value; 4 | 5 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 6 | import com.fasterxml.jackson.databind.annotation.JsonSerialize; 7 | import com.sap.cloudfoundry.client.facade.Nullable; 8 | 9 | @Value.Immutable 10 | @JsonSerialize(as = ImmutableCloudApplication.class) 11 | @JsonDeserialize(as = ImmutableCloudApplication.class) 12 | public abstract class CloudApplication extends CloudEntity implements Derivable { 13 | 14 | public enum State { 15 | STARTED, STOPPED 16 | } 17 | 18 | @Nullable 19 | public abstract State getState(); 20 | 21 | @Nullable 22 | public abstract Lifecycle getLifecycle(); 23 | 24 | @Nullable 25 | public abstract CloudSpace getSpace(); 26 | 27 | @Override 28 | public CloudApplication derive() { 29 | return this; 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/com/sap/cloudfoundry/client/facade/domain/CloudServiceBroker.java: -------------------------------------------------------------------------------- 1 | package com.sap.cloudfoundry.client.facade.domain; 2 | 3 | import org.immutables.value.Value; 4 | 5 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 6 | import com.fasterxml.jackson.databind.annotation.JsonSerialize; 7 | import com.sap.cloudfoundry.client.facade.Nullable; 8 | 9 | @Value.Immutable 10 | @JsonSerialize(as = ImmutableCloudServiceBroker.class) 11 | @JsonDeserialize(as = ImmutableCloudServiceBroker.class) 12 | public abstract class CloudServiceBroker extends CloudEntity implements Derivable { 13 | 14 | @Nullable 15 | public abstract String getUsername(); 16 | 17 | @Nullable 18 | public abstract String getPassword(); 19 | 20 | @Nullable 21 | public abstract String getUrl(); 22 | 23 | @Nullable 24 | public abstract String getSpaceGuid(); 25 | 26 | @Override 27 | public CloudServiceBroker derive() { 28 | return this; 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/com/sap/cloudfoundry/client/facade/domain/CloudAsyncJob.java: -------------------------------------------------------------------------------- 1 | package com.sap.cloudfoundry.client.facade.domain; 2 | 3 | import org.cloudfoundry.client.v3.jobs.JobState; 4 | import org.immutables.value.Value; 5 | 6 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 7 | import com.fasterxml.jackson.databind.annotation.JsonSerialize; 8 | import com.sap.cloudfoundry.client.facade.Nullable; 9 | 10 | @Value.Immutable 11 | @JsonSerialize(as = ImmutableCloudAsyncJob.class) 12 | @JsonDeserialize(as = ImmutableCloudAsyncJob.class) 13 | public abstract class CloudAsyncJob extends CloudEntity implements Derivable { 14 | 15 | public abstract JobState getState(); 16 | 17 | @Nullable 18 | public abstract String getOperation(); 19 | 20 | @Nullable 21 | public abstract String getWarnings(); 22 | 23 | @Nullable 24 | public abstract String getErrors(); 25 | 26 | @Override 27 | public CloudAsyncJob derive() { 28 | return this; 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/com/sap/cloudfoundry/client/facade/domain/BitsData.java: -------------------------------------------------------------------------------- 1 | package com.sap.cloudfoundry.client.facade.domain; 2 | 3 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 4 | import com.fasterxml.jackson.databind.annotation.JsonSerialize; 5 | import com.sap.cloudfoundry.client.facade.Nullable; 6 | import org.immutables.value.Value; 7 | 8 | @Value.Immutable 9 | @Value.Enclosing 10 | @JsonSerialize(as = ImmutableBitsData.class) 11 | @JsonDeserialize(as = ImmutableBitsData.class) 12 | public interface BitsData extends CloudPackage.PackageData { 13 | 14 | @Nullable 15 | Checksum getChecksum(); 16 | 17 | @Nullable 18 | String getError(); 19 | 20 | @Value.Immutable 21 | @JsonSerialize(as = ImmutableBitsData.ImmutableChecksum.class) 22 | @JsonDeserialize(as = ImmutableBitsData.ImmutableChecksum.class) 23 | interface Checksum { 24 | 25 | @Nullable 26 | String getAlgorithm(); 27 | 28 | @Nullable 29 | String getValue(); 30 | 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/com/sap/cloudfoundry/client/facade/oauth2/Oauth2AccessTokenResponse.java: -------------------------------------------------------------------------------- 1 | package com.sap.cloudfoundry.client.facade.oauth2; 2 | 3 | import org.immutables.value.Value; 4 | 5 | import com.fasterxml.jackson.annotation.JsonProperty; 6 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 7 | import com.fasterxml.jackson.databind.annotation.JsonSerialize; 8 | 9 | @Value.Immutable 10 | @JsonSerialize(as = ImmutableOauth2AccessTokenResponse.class) 11 | @JsonDeserialize(as = ImmutableOauth2AccessTokenResponse.class) 12 | public interface Oauth2AccessTokenResponse { 13 | 14 | @JsonProperty("access_token") 15 | String getAccessToken(); 16 | 17 | @JsonProperty("token_type") 18 | String getTokenType(); 19 | 20 | @JsonProperty("id_token") 21 | String getIdToken(); 22 | 23 | @JsonProperty("refresh_token") 24 | String getRefreshToken(); 25 | 26 | @JsonProperty("expires_in") 27 | long getExpiresIn(); 28 | 29 | String getScope(); 30 | 31 | String getJti(); 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/com/sap/cloudfoundry/client/facade/domain/CloudMetadata.java: -------------------------------------------------------------------------------- 1 | package com.sap.cloudfoundry.client.facade.domain; 2 | 3 | import java.time.LocalDateTime; 4 | import java.util.UUID; 5 | 6 | import org.immutables.value.Value; 7 | 8 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 9 | import com.fasterxml.jackson.databind.annotation.JsonSerialize; 10 | import com.sap.cloudfoundry.client.facade.Nullable; 11 | 12 | @Value.Immutable 13 | @JsonSerialize(as = ImmutableCloudMetadata.class) 14 | @JsonDeserialize(as = ImmutableCloudMetadata.class) 15 | public interface CloudMetadata { 16 | 17 | @Nullable 18 | @Value.Parameter 19 | UUID getGuid(); 20 | 21 | @Nullable 22 | LocalDateTime getCreatedAt(); 23 | 24 | @Nullable 25 | LocalDateTime getUpdatedAt(); 26 | 27 | @Nullable 28 | String getUrl(); 29 | 30 | static CloudMetadata defaultMetadata() { 31 | return ImmutableCloudMetadata.builder() 32 | .build(); 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/test/resources/com/sap/cloudfoundry/client/facade/util/task.json: -------------------------------------------------------------------------------- 1 | { 2 | "guid": "d5cc22ec-99a3-4e6a-af91-a44b4ab7b6fa", 3 | "sequence_id": 1, 4 | "name": "migrate", 5 | "command": "rake db:migrate", 6 | "state": "RUNNING", 7 | "memory_in_mb": 512, 8 | "disk_in_mb": 1024, 9 | "result": { 10 | "failure_reason": null 11 | }, 12 | "droplet_guid": "740ebd2b-162b-469a-bd72-3edb96fabd9a", 13 | "created_at": "2016-05-04T17:00:41Z", 14 | "updated_at": "2016-05-04T17:00:42Z", 15 | "links": { 16 | "self": { 17 | "href": "https://api.example.org/v3/tasks/d5cc22ec-99a3-4e6a-af91-a44b4ab7b6fa" 18 | }, 19 | "app": { 20 | "href": "https://api.example.org/v3/apps/ccc25a0f-c8f4-4b39-9f1b-de9f328d0ee5" 21 | }, 22 | "cancel": { 23 | "href": "https://api.example.org/v3/tasks/d5cc22ec-99a3-4e6a-af91-a44b4ab7b6fa/actions/cancel", 24 | "method": "POST" 25 | }, 26 | "droplet": { 27 | "href": "https://api.example.org/v3/droplets/740ebd2b-162b-469a-bd72-3edb96fabd9a" 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /src/main/java/com/sap/cloudfoundry/client/facade/domain/ApplicationLog.java: -------------------------------------------------------------------------------- 1 | package com.sap.cloudfoundry.client.facade.domain; 2 | 3 | import java.time.LocalDateTime; 4 | 5 | import org.immutables.value.Value; 6 | 7 | @Value.Immutable 8 | public abstract class ApplicationLog implements Comparable { 9 | 10 | public enum MessageType { 11 | STDOUT, STDERR 12 | } 13 | 14 | public abstract String getApplicationGuid(); 15 | 16 | public abstract String getMessage(); 17 | 18 | public abstract LocalDateTime getTimestamp(); 19 | 20 | public abstract MessageType getMessageType(); 21 | 22 | public abstract String getSourceName(); 23 | 24 | @Override 25 | public int compareTo(ApplicationLog other) { 26 | return getTimestamp().compareTo(other.getTimestamp()); 27 | } 28 | 29 | @Override 30 | public String toString() { 31 | return String.format("%s [%s] %s (%s, %s)", getApplicationGuid(), getTimestamp(), getMessage(), 32 | getMessageType(), getSourceName()); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/com/sap/cloudfoundry/client/facade/domain/CloudServiceKey.java: -------------------------------------------------------------------------------- 1 | package com.sap.cloudfoundry.client.facade.domain; 2 | 3 | import java.util.Map; 4 | 5 | import org.cloudfoundry.AllowNulls; 6 | import org.immutables.value.Value; 7 | 8 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 9 | import com.fasterxml.jackson.databind.annotation.JsonSerialize; 10 | import com.sap.cloudfoundry.client.facade.Nullable; 11 | 12 | @Value.Immutable 13 | @JsonSerialize(as = ImmutableCloudServiceKey.class) 14 | @JsonDeserialize(as = ImmutableCloudServiceKey.class) 15 | public abstract class CloudServiceKey extends CloudEntity implements Derivable { 16 | 17 | @Nullable 18 | @AllowNulls 19 | public abstract Map getCredentials(); 20 | 21 | @Nullable 22 | public abstract CloudServiceInstance getServiceInstance(); 23 | 24 | @Nullable 25 | public abstract ServiceCredentialBindingOperation getServiceKeyOperation(); 26 | 27 | @Override 28 | public CloudServiceKey derive() { 29 | return this; 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/com/sap/cloudfoundry/client/facade/domain/CloudServicePlan.java: -------------------------------------------------------------------------------- 1 | package com.sap.cloudfoundry.client.facade.domain; 2 | 3 | import java.util.Map; 4 | 5 | import org.immutables.value.Value; 6 | 7 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 8 | import com.fasterxml.jackson.databind.annotation.JsonSerialize; 9 | import com.sap.cloudfoundry.client.facade.Nullable; 10 | 11 | @Value.Immutable 12 | @JsonSerialize(as = ImmutableCloudServicePlan.class) 13 | @JsonDeserialize(as = ImmutableCloudServicePlan.class) 14 | public abstract class CloudServicePlan extends CloudEntity implements Derivable { 15 | 16 | @Nullable 17 | public abstract String getDescription(); 18 | 19 | @Nullable 20 | public abstract Map getExtra(); 21 | 22 | @Nullable 23 | public abstract String getUniqueId(); 24 | 25 | @Nullable 26 | public abstract String getServiceOfferingId(); 27 | 28 | @Nullable 29 | public abstract Boolean isFree(); 30 | 31 | @Nullable 32 | public abstract Boolean isPublic(); 33 | 34 | @Override 35 | public CloudServicePlan derive() { 36 | return this; 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/com/sap/cloudfoundry/client/facade/dto/ApplicationToCreateDto.java: -------------------------------------------------------------------------------- 1 | package com.sap.cloudfoundry.client.facade.dto; 2 | 3 | import java.util.Map; 4 | import java.util.Set; 5 | 6 | import org.cloudfoundry.client.v3.Metadata; 7 | import org.immutables.value.Value; 8 | 9 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 10 | import com.fasterxml.jackson.databind.annotation.JsonSerialize; 11 | import com.sap.cloudfoundry.client.facade.Nullable; 12 | import com.sap.cloudfoundry.client.facade.domain.CloudRoute; 13 | import com.sap.cloudfoundry.client.facade.domain.Staging; 14 | 15 | @Value.Immutable 16 | @JsonSerialize(as = ImmutableApplicationToCreateDto.class) 17 | @JsonDeserialize(as = ImmutableApplicationToCreateDto.class) 18 | public interface ApplicationToCreateDto { 19 | 20 | String getName(); 21 | 22 | @Nullable 23 | Staging getStaging(); 24 | 25 | @Nullable 26 | Integer getDiskQuotaInMb(); 27 | 28 | @Nullable 29 | Integer getMemoryInMb(); 30 | 31 | @Nullable 32 | Metadata getMetadata(); 33 | 34 | @Nullable 35 | Set getRoutes(); 36 | 37 | @Nullable 38 | Map getEnv(); 39 | 40 | } 41 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Project Status: Archived 2 | 3 | ⚠️ **This project has been archived** ⚠️ 4 | 5 | The **cf-java-client-sap** repository is no longer actively maintained. 6 | It was previously a facade/wrapper around the [cf-java-client](https://github.com/cloudfoundry/cf-java-client). 7 | The code has now been merged into another [project](https://github.com/cloudfoundry/multiapps-controller) for internal use only. 8 | 9 | --- 10 | 11 | ## Guidance for Users 12 | 13 | If you are currently using **cf-java-client-sap**, 14 | ➡️ the recommended path forward is to **use the official [cf-java-client](https://github.com/cloudfoundry/cf-java-client) directly**. 15 | 16 | This ensures you are always up to date with the latest features, fixes, and improvements from the Cloud Foundry community. 17 | 18 | --- 19 | 20 | ## Why was this change made? 21 | 22 | - Maintaining a custom facade added unnecessary complexity. 23 | - The functionality has been consolidated into a project that depends on it internally. 24 | - For external consumers, using the pure **cf-java-client** is simpler and future-proof. 25 | 26 | --- 27 | 28 | 🙏 We thank all contributors and users of **cf-java-client-sap** for their support! 29 | -------------------------------------------------------------------------------- /src/main/java/com/sap/cloudfoundry/client/facade/domain/CloudProcess.java: -------------------------------------------------------------------------------- 1 | package com.sap.cloudfoundry.client.facade.domain; 2 | 3 | import org.immutables.value.Value; 4 | 5 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 6 | import com.fasterxml.jackson.databind.annotation.JsonSerialize; 7 | import com.sap.cloudfoundry.client.facade.Nullable; 8 | 9 | @Value.Immutable 10 | @JsonSerialize(as = ImmutableCloudProcess.class) 11 | @JsonDeserialize(as = ImmutableCloudProcess.class) 12 | public abstract class CloudProcess extends CloudEntity implements Derivable { 13 | 14 | public abstract String getCommand(); 15 | 16 | public abstract Integer getDiskInMb(); 17 | 18 | public abstract Integer getInstances(); 19 | 20 | public abstract Integer getMemoryInMb(); 21 | 22 | public abstract HealthCheckType getHealthCheckType(); 23 | 24 | @Nullable 25 | public abstract String getHealthCheckHttpEndpoint(); 26 | 27 | @Nullable 28 | public abstract Integer getHealthCheckInvocationTimeout(); 29 | 30 | @Nullable 31 | public abstract Integer getHealthCheckTimeout(); 32 | 33 | @Override 34 | public CloudProcess derive() { 35 | return this; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/com/sap/cloudfoundry/client/facade/adapters/RawV3CloudServiceInstance.java: -------------------------------------------------------------------------------- 1 | package com.sap.cloudfoundry.client.facade.adapters; 2 | 3 | import com.sap.cloudfoundry.client.facade.domain.CloudServiceInstance; 4 | import com.sap.cloudfoundry.client.facade.domain.ImmutableCloudServiceInstance; 5 | import org.cloudfoundry.client.v3.serviceinstances.ServiceInstance; 6 | import org.immutables.value.Value; 7 | 8 | @Value.Immutable 9 | public abstract class RawV3CloudServiceInstance extends RawCloudEntity { 10 | 11 | @Value.Parameter 12 | public abstract ServiceInstance getServiceInstance(); 13 | 14 | @Override 15 | public CloudServiceInstance derive() { 16 | ServiceInstance serviceInstance = getServiceInstance(); 17 | return ImmutableCloudServiceInstance.builder() 18 | .metadata(parseResourceMetadata(serviceInstance)) 19 | .v3Metadata(serviceInstance.getMetadata()) 20 | .name(serviceInstance.getName()) 21 | .tags(serviceInstance.getTags()) 22 | .build(); 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/sap/cloudfoundry/client/facade/domain/UserRole.java: -------------------------------------------------------------------------------- 1 | package com.sap.cloudfoundry.client.facade.domain; 2 | 3 | import java.util.Arrays; 4 | import java.util.Map; 5 | import java.util.stream.Collectors; 6 | 7 | import org.cloudfoundry.client.v3.roles.RoleType; 8 | 9 | public enum UserRole { 10 | 11 | ORGANIZATION_AUDITOR, 12 | ORGANIZATION_BILLING_MANAGER, 13 | ORGANIZATION_MANAGER, 14 | ORGANIZATION_USER, 15 | SPACE_AUDITOR, 16 | SPACE_DEVELOPER, 17 | SPACE_MANAGER; 18 | 19 | private static final Map NAMES_TO_VALUES = Arrays.stream(values()) 20 | .collect(Collectors.toMap(UserRole::getName, 21 | roleType -> roleType)); 22 | 23 | public static UserRole fromRoleType(RoleType roleType) { 24 | UserRole userRole = NAMES_TO_VALUES.get(roleType.getValue()); 25 | if (userRole == null) { 26 | throw new IllegalArgumentException("Unknown user role: " + roleType.getValue()); 27 | } 28 | return userRole; 29 | } 30 | 31 | public String getName() { 32 | return name().toLowerCase(); 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/test/java/com/sap/cloudfoundry/client/facade/broker/ServiceBrokerConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.sap.cloudfoundry.client.facade.broker; 2 | 3 | import java.util.List; 4 | 5 | import com.fasterxml.jackson.annotation.JsonProperty; 6 | import org.immutables.value.Value; 7 | 8 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 9 | import com.fasterxml.jackson.databind.annotation.JsonSerialize; 10 | import com.sap.cloudfoundry.client.facade.Nullable; 11 | 12 | @Value.Immutable 13 | @JsonSerialize(as = ImmutableServiceBrokerConfiguration.class) 14 | @JsonDeserialize(as = ImmutableServiceBrokerConfiguration.class) 15 | public interface ServiceBrokerConfiguration { 16 | 17 | @Nullable 18 | @JsonProperty("asyncDuration") 19 | Integer getAsyncDurationInMillis(); 20 | 21 | @Nullable 22 | @JsonProperty("syncDuration") 23 | Integer getSyncDurationInMillis(); 24 | 25 | @Nullable 26 | @JsonProperty("asyncDurationForServiceCredentialBindings") 27 | Integer getAsyncDurationForServiceCredentialBindingsInMillis(); 28 | 29 | @Nullable 30 | @JsonProperty("syncDurationForServiceCredentialBindings") 31 | Integer getSyncDurationForServiceCredentialBindingsInMillis(); 32 | 33 | List getFailConfigurations(); 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/com/sap/cloudfoundry/client/facade/oauth2/OAuth2AccessTokenWithAdditionalInfo.java: -------------------------------------------------------------------------------- 1 | package com.sap.cloudfoundry.client.facade.oauth2; 2 | 3 | import java.util.Map; 4 | 5 | import org.springframework.security.oauth2.core.OAuth2AccessToken; 6 | 7 | public class OAuth2AccessTokenWithAdditionalInfo { 8 | 9 | private OAuth2AccessToken oAuth2AccessToken; 10 | private Map additionalInfo; 11 | 12 | public OAuth2AccessTokenWithAdditionalInfo(OAuth2AccessToken oAuth2AccessToken) { 13 | this.oAuth2AccessToken = oAuth2AccessToken; 14 | } 15 | 16 | public OAuth2AccessTokenWithAdditionalInfo(OAuth2AccessToken oAuth2AccessToken, Map additionalInfo) { 17 | this.oAuth2AccessToken = oAuth2AccessToken; 18 | this.additionalInfo = additionalInfo; 19 | } 20 | 21 | public OAuth2AccessToken getOAuth2AccessToken() { 22 | return oAuth2AccessToken; 23 | } 24 | 25 | public Map getAdditionalInfo() { 26 | return additionalInfo; 27 | } 28 | 29 | public String getAuthorizationHeaderValue() { 30 | return getOAuth2AccessToken().getTokenType() 31 | .getValue() 32 | + " " + getOAuth2AccessToken().getTokenValue(); 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/module-info.java: -------------------------------------------------------------------------------- 1 | open module com.sap.cloudfoundry.client.facade { 2 | 3 | requires transitive org.cloudfoundry.client; 4 | 5 | requires org.cloudfoundry.client.reactor; 6 | requires org.cloudfoundry.util; 7 | requires com.fasterxml.jackson.databind; 8 | requires org.apache.commons.io; 9 | requires org.slf4j; 10 | requires spring.core; 11 | requires spring.security.core; 12 | requires spring.security.oauth2.core; 13 | requires spring.security.oauth2.client; 14 | requires spring.web; 15 | requires spring.webflux; 16 | requires reactor.core; 17 | requires io.netty.handler; 18 | requires reactor.netty.core; 19 | requires reactor.netty.http; 20 | requires org.apache.commons.logging; 21 | requires org.reactivestreams; 22 | requires java.net.http; 23 | 24 | requires static java.compiler; 25 | requires static org.immutables.value; 26 | 27 | exports com.sap.cloudfoundry.client.facade; 28 | exports com.sap.cloudfoundry.client.facade.rest; 29 | exports com.sap.cloudfoundry.client.facade.oauth2; 30 | exports com.sap.cloudfoundry.client.facade.domain; 31 | exports com.sap.cloudfoundry.client.facade.adapters; 32 | exports com.sap.cloudfoundry.client.facade.util; 33 | exports com.sap.cloudfoundry.client.facade.dto; 34 | 35 | } -------------------------------------------------------------------------------- /.github/workflows/codeql.yml: -------------------------------------------------------------------------------- 1 | name: "CodeQL" 2 | 3 | on: 4 | push: 5 | branches: [ "master" ] 6 | pull_request: 7 | # The branches below must be a subset of the branches above 8 | branches: [ "master" ] 9 | schedule: 10 | - cron: '23 6 * * 5' 11 | 12 | jobs: 13 | analyze: 14 | name: Analyze 15 | runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }} 16 | timeout-minutes: ${{ (matrix.language == 'swift' && 120) || 360 }} 17 | permissions: 18 | actions: read 19 | contents: read 20 | security-events: write 21 | 22 | strategy: 23 | fail-fast: false 24 | matrix: 25 | language: [ 'java' ] 26 | 27 | steps: 28 | - name: Checkout repository 29 | uses: actions/checkout@v3 30 | 31 | - name: Setup Java JDK 32 | uses: actions/setup-java@v4 33 | with: 34 | java-version: 17 35 | distribution: 'sapmachine' 36 | 37 | # Initializes the CodeQL tools for scanning. 38 | - name: Initialize CodeQL 39 | uses: github/codeql-action/init@v3 40 | with: 41 | languages: ${{ matrix.language }} 42 | 43 | - name: Autobuild 44 | uses: github/codeql-action/autobuild@v3 45 | 46 | - name: Perform CodeQL Analysis 47 | uses: github/codeql-action/analyze@v3 48 | with: 49 | category: "/language:${{matrix.language}}" 50 | -------------------------------------------------------------------------------- /src/main/java/com/sap/cloudfoundry/client/facade/util/AuthorizationEndpointGetter.java: -------------------------------------------------------------------------------- 1 | package com.sap.cloudfoundry.client.facade.util; 2 | 3 | import java.util.Map; 4 | 5 | import org.springframework.web.reactive.function.client.WebClient; 6 | 7 | public class AuthorizationEndpointGetter { 8 | 9 | private final WebClient webClient; 10 | 11 | public AuthorizationEndpointGetter(WebClient webClient) { 12 | this.webClient = webClient; 13 | } 14 | 15 | public String getAuthorizationEndpoint() { 16 | return getAuthorizationEndpoint(""); 17 | } 18 | 19 | public String getAuthorizationEndpoint(String controllerUrl) { 20 | String response = webClient.get() 21 | .uri(controllerUrl + "/") 22 | .retrieve() 23 | .bodyToMono(String.class) 24 | .block(); 25 | return getLoginHref(response); 26 | } 27 | 28 | @SuppressWarnings("unchecked") 29 | private String getLoginHref(String response) { 30 | Map resource = JsonUtil.convertJsonToMap(response); 31 | Map links = (Map) resource.get("links"); 32 | Map login = (Map) links.get("login"); 33 | return (String) login.get("href"); 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/com/sap/cloudfoundry/client/facade/domain/CloudServiceOffering.java: -------------------------------------------------------------------------------- 1 | package com.sap.cloudfoundry.client.facade.domain; 2 | 3 | import java.util.List; 4 | import java.util.Map; 5 | 6 | import org.immutables.value.Value; 7 | 8 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 9 | import com.fasterxml.jackson.databind.annotation.JsonSerialize; 10 | import com.sap.cloudfoundry.client.facade.Nullable; 11 | 12 | @Value.Immutable 13 | @JsonSerialize(as = ImmutableCloudServiceOffering.class) 14 | @JsonDeserialize(as = ImmutableCloudServiceOffering.class) 15 | public abstract class CloudServiceOffering extends CloudEntity implements Derivable { 16 | 17 | @Nullable 18 | public abstract Boolean isAvailable(); 19 | 20 | @Nullable 21 | public abstract Boolean isBindable(); 22 | 23 | @Nullable 24 | public abstract Boolean isShareable(); 25 | 26 | public abstract List getServicePlans(); 27 | 28 | @Nullable 29 | public abstract String getDescription(); 30 | 31 | @Nullable 32 | public abstract String getDocUrl(); 33 | 34 | @Nullable 35 | public abstract Map getExtra(); 36 | 37 | @Nullable 38 | public abstract String getBrokerId(); 39 | 40 | @Nullable 41 | public abstract String getUniqueId(); 42 | 43 | @Override 44 | public CloudServiceOffering derive() { 45 | return this; 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/com/sap/cloudfoundry/client/facade/adapters/ApplicationLogEntity.java: -------------------------------------------------------------------------------- 1 | package com.sap.cloudfoundry.client.facade.adapters; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 5 | import org.immutables.value.Value; 6 | 7 | import java.util.Map; 8 | 9 | @Value.Immutable 10 | @Value.Enclosing 11 | @JsonDeserialize(as = ImmutableApplicationLogEntity.class) 12 | public abstract class ApplicationLogEntity implements Comparable { 13 | 14 | @JsonProperty("timestamp") 15 | public abstract Long getTimestampInNanoseconds(); 16 | 17 | @JsonProperty("source_id") 18 | public abstract String getSourceId(); 19 | 20 | @JsonProperty("instance_id") 21 | public abstract String getInstanceId(); 22 | 23 | public abstract Map getTags(); 24 | 25 | @JsonProperty("log") 26 | public abstract LogBody getLogBody(); 27 | 28 | @Value.Immutable 29 | @JsonDeserialize(as = ImmutableApplicationLogEntity.ImmutableLogBody.class) 30 | public interface LogBody { 31 | 32 | @JsonProperty("payload") 33 | String getMessage(); 34 | 35 | @JsonProperty("type") 36 | String getMessageType(); 37 | } 38 | 39 | @Override 40 | public int compareTo(ApplicationLogEntity otherLog) { 41 | return getTimestampInNanoseconds().compareTo(otherLog.getTimestampInNanoseconds()); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/com/sap/cloudfoundry/client/facade/CloudControllerException.java: -------------------------------------------------------------------------------- 1 | package com.sap.cloudfoundry.client.facade; 2 | 3 | import java.text.MessageFormat; 4 | 5 | import org.springframework.http.HttpStatus; 6 | 7 | public class CloudControllerException extends CloudOperationException { 8 | 9 | private static final long serialVersionUID = 1L; 10 | private static final String DEFAULT_CLOUD_CONTROLLER_ERROR_MESSAGE = "Controller operation failed: {0}"; 11 | 12 | public CloudControllerException(CloudOperationException cloudOperationException) { 13 | super(cloudOperationException.getStatusCode(), 14 | cloudOperationException.getStatusText(), 15 | cloudOperationException.getDescription(), 16 | cloudOperationException); 17 | } 18 | 19 | public CloudControllerException(HttpStatus statusCode) { 20 | super(statusCode); 21 | } 22 | 23 | public CloudControllerException(HttpStatus statusCode, String statusText) { 24 | super(statusCode, statusText); 25 | } 26 | 27 | public CloudControllerException(HttpStatus statusCode, String statusText, String description) { 28 | super(statusCode, statusText, description); 29 | } 30 | 31 | @Override 32 | public String getMessage() { 33 | return decorateExceptionMessage(super.getMessage()); 34 | } 35 | 36 | private String decorateExceptionMessage(String exceptionMessage) { 37 | return MessageFormat.format(DEFAULT_CLOUD_CONTROLLER_ERROR_MESSAGE, exceptionMessage); 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/com/sap/cloudfoundry/client/facade/CloudServiceBrokerException.java: -------------------------------------------------------------------------------- 1 | package com.sap.cloudfoundry.client.facade; 2 | 3 | import java.text.MessageFormat; 4 | 5 | import org.springframework.http.HttpStatus; 6 | 7 | public class CloudServiceBrokerException extends CloudOperationException { 8 | 9 | private static final long serialVersionUID = 1L; 10 | private static final String DEFAULT_SERVICE_BROKER_ERROR_MESSAGE = "Service broker operation failed: {0}"; 11 | 12 | public CloudServiceBrokerException(CloudOperationException cloudOperationException) { 13 | super(cloudOperationException.getStatusCode(), 14 | cloudOperationException.getStatusText(), 15 | cloudOperationException.getDescription(), 16 | cloudOperationException); 17 | } 18 | 19 | public CloudServiceBrokerException(HttpStatus statusCode) { 20 | super(statusCode); 21 | } 22 | 23 | public CloudServiceBrokerException(HttpStatus statusCode, String statusText) { 24 | super(statusCode, statusText); 25 | } 26 | 27 | public CloudServiceBrokerException(HttpStatus statusCode, String statusText, String description) { 28 | super(statusCode, statusText, description); 29 | } 30 | 31 | @Override 32 | public String getMessage() { 33 | return decorateExceptionMessage(super.getMessage()); 34 | } 35 | 36 | private String decorateExceptionMessage(String exceptionMessage) { 37 | return MessageFormat.format(DEFAULT_SERVICE_BROKER_ERROR_MESSAGE, exceptionMessage); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/com/sap/cloudfoundry/client/facade/domain/CloudJob.java: -------------------------------------------------------------------------------- 1 | package com.sap.cloudfoundry.client.facade.domain; 2 | 3 | import org.immutables.value.Value; 4 | 5 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 6 | import com.fasterxml.jackson.databind.annotation.JsonSerialize; 7 | import com.sap.cloudfoundry.client.facade.Nullable; 8 | 9 | @Value.Immutable 10 | @JsonSerialize(as = ImmutableCloudJob.class) 11 | @JsonDeserialize(as = ImmutableCloudJob.class) 12 | public abstract class CloudJob extends CloudEntity implements Derivable { 13 | 14 | @Nullable 15 | public abstract Status getStatus(); 16 | 17 | @Nullable 18 | public abstract ErrorDetails getErrorDetails(); 19 | 20 | @Override 21 | public CloudJob derive() { 22 | return this; 23 | } 24 | 25 | public enum Status { 26 | 27 | FAILED("failed"), FINISHED("finished"), QUEUED("queued"), RUNNING("running"); 28 | 29 | private final String value; 30 | 31 | Status(String value) { 32 | this.value = value; 33 | } 34 | 35 | public static Status fromString(String value) { 36 | for (Status status : Status.values()) { 37 | if (status.value.equals(value)) { 38 | return status; 39 | } 40 | } 41 | throw new IllegalArgumentException("Invalid job status: " + value); 42 | } 43 | 44 | @Override 45 | public String toString() { 46 | return value; 47 | } 48 | 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/com/sap/cloudfoundry/client/facade/domain/CloudEvent.java: -------------------------------------------------------------------------------- 1 | package com.sap.cloudfoundry.client.facade.domain; 2 | 3 | import java.time.LocalDateTime; 4 | import java.util.UUID; 5 | 6 | import org.immutables.value.Value; 7 | 8 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 9 | import com.fasterxml.jackson.databind.annotation.JsonSerialize; 10 | import com.sap.cloudfoundry.client.facade.Nullable; 11 | import com.sap.cloudfoundry.client.facade.domain.ImmutableCloudEvent.ImmutableParticipant; 12 | 13 | @Value.Enclosing 14 | @Value.Immutable 15 | @JsonSerialize(as = ImmutableCloudEvent.class) 16 | @JsonDeserialize(as = ImmutableCloudEvent.class) 17 | public abstract class CloudEvent extends CloudEntity implements Derivable { 18 | 19 | @Nullable 20 | public abstract String getType(); 21 | 22 | @Nullable 23 | public abstract Participant getActor(); 24 | 25 | @Nullable 26 | public abstract Participant getTarget(); 27 | 28 | @Nullable 29 | public LocalDateTime getTimestamp() { 30 | return getMetadata().getCreatedAt(); 31 | } 32 | 33 | @Override 34 | public CloudEvent derive() { 35 | return this; 36 | } 37 | 38 | @Value.Immutable 39 | @JsonSerialize(as = ImmutableParticipant.class) 40 | @JsonDeserialize(as = ImmutableParticipant.class) 41 | public interface Participant { 42 | 43 | @Nullable 44 | UUID getGuid(); 45 | 46 | @Nullable 47 | String getName(); 48 | 49 | @Nullable 50 | String getType(); 51 | 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /src/test/java/com/sap/cloudfoundry/client/facade/adapters/RawCloudStackTest.java: -------------------------------------------------------------------------------- 1 | package com.sap.cloudfoundry.client.facade.adapters; 2 | 3 | import org.cloudfoundry.client.v3.stacks.Stack; 4 | import org.cloudfoundry.client.v3.stacks.StackResource; 5 | import org.junit.jupiter.api.Test; 6 | 7 | import com.sap.cloudfoundry.client.facade.domain.CloudStack; 8 | import com.sap.cloudfoundry.client.facade.domain.ImmutableCloudStack; 9 | 10 | class RawCloudStackTest { 11 | 12 | private static final String NAME = "cflinuxfs3"; 13 | 14 | @Test 15 | void testDerive() { 16 | RawCloudEntityTest.testDerive(buildExpectedStack(), buildRawStack()); 17 | } 18 | 19 | private static CloudStack buildExpectedStack() { 20 | return ImmutableCloudStack.builder() 21 | .metadata(RawCloudEntityTest.EXPECTED_METADATA_V3) 22 | .name(NAME) 23 | .build(); 24 | } 25 | 26 | private static RawCloudStack buildRawStack() { 27 | return ImmutableRawCloudStack.of(buildTestResource()); 28 | } 29 | 30 | private static Stack buildTestResource() { 31 | return StackResource.builder() 32 | .id(RawCloudEntityTest.GUID_STRING) 33 | .createdAt(RawCloudEntityTest.CREATED_AT_STRING) 34 | .updatedAt(RawCloudEntityTest.UPDATED_AT_STRING) 35 | .metadata(RawCloudEntityTest.V3_METADATA) 36 | .name(NAME) 37 | .build(); 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/test/resources/com/sap/cloudfoundry/client/facade/util/service-instance.json: -------------------------------------------------------------------------------- 1 | { 2 | "metadata": { 3 | "guid": "cc3b67fa-cda6-4df7-ba47-eb5f2a123992", 4 | "url": "/v2/service_instances/cc3b67fa-cda6-4df7-ba47-eb5f2a123992", 5 | "created_at": "2016-06-08T16:41:29Z", 6 | "updated_at": "2016-06-08T16:41:26Z" 7 | }, 8 | "entity": { 9 | "name": "my-service-instance", 10 | "credentials": { 11 | 12 | }, 13 | "service_plan_guid": "fe173a83-df28-4891-8d91-46334e04600d", 14 | "space_guid": "bbbeed31-f908-477a-aab9-8cdcd19e1348", 15 | "gateway_data": null, 16 | "dashboard_url": null, 17 | "type": "managed_service_instance", 18 | "last_operation": { 19 | "type": "create", 20 | "state": "in progress", 21 | "description": "", 22 | "updated_at": "2016-06-08T16:41:26Z", 23 | "created_at": "2016-06-08T16:41:29Z" 24 | }, 25 | "tags": [ 26 | "accounting", 27 | "mongodb" 28 | ], 29 | "space_url": "/v2/spaces/bbbeed31-f908-477a-aab9-8cdcd19e1348", 30 | "service_plan_url": "/v2/service_plans/fe173a83-df28-4891-8d91-46334e04600d", 31 | "service_bindings_url": "/v2/service_instances/cc3b67fa-cda6-4df7-ba47-eb5f2a123992/service_bindings", 32 | "service_keys_url": "/v2/service_instances/cc3b67fa-cda6-4df7-ba47-eb5f2a123992/service_keys", 33 | "routes_url": "/v2/service_instances/cc3b67fa-cda6-4df7-ba47-eb5f2a123992/routes", 34 | "shared_from_url": "/v2/service_instances/0d632575-bb06-4ea5-bb19-a451a9644d92/shared_from", 35 | "shared_to_url": "/v2/service_instances/0d632575-bb06-4ea5-bb19-a451a9644d92/shared_to" 36 | } 37 | } -------------------------------------------------------------------------------- /src/test/resources/com/sap/cloudfoundry/client/facade/util/application.json: -------------------------------------------------------------------------------- 1 | { 2 | "metadata": { 3 | "guid": "83d048bc-a301-4b48-94a8-848e477fac4d", 4 | "url": "/v2/apps/83d048bc-a301-4b48-94a8-848e477fac4d", 5 | "created_at": "2019-07-18T17:19:27Z", 6 | "updated_at": "2019-07-18T19:55:52Z" 7 | }, 8 | "entity": { 9 | "name": "test-flask-5", 10 | "production": false, 11 | "space_guid": "bbbeed31-f908-477a-aab9-8cdcd19e1348", 12 | "stack_guid": "bbbeed31-f908-477a-aab9-8cdcd19e1348", 13 | "buildpack": null, 14 | "detected_buildpack": "python", 15 | "detected_buildpack_guid": "5eb740a2-f70b-4e38-957c-e1c513142c4c", 16 | "environment_json": null, 17 | "memory": 1024, 18 | "instances": 1, 19 | "disk_quota": 1024, 20 | "state": "STOPPED", 21 | "version": "bbbeed31-f908-477a-aab9-8cdcd19e1348", 22 | "command": null, 23 | "console": false, 24 | "debug": null, 25 | "staging_task_id": "a50db5f9-713b-4e46-9560-21bd4992df1c", 26 | "package_state": "STAGED", 27 | "health_check_type": "port", 28 | "health_check_timeout": null, 29 | "health_check_http_endpoint": null, 30 | "staging_failed_reason": null, 31 | "staging_failed_description": null, 32 | "diego": true, 33 | "docker_image": null, 34 | "docker_credentials": { 35 | "username": null, 36 | "password": null 37 | }, 38 | "package_updated_at": "2019-07-18T17:20:54Z", 39 | "detected_start_command": "python app.py", 40 | "enable_ssh": true, 41 | "ports": [ 42 | 8080 43 | ], 44 | "space_url": "/v2/spaces/e1210c89-158b-4298-a417-96f80d5b9e52" 45 | } 46 | } -------------------------------------------------------------------------------- /src/main/java/com/sap/cloudfoundry/client/facade/domain/CloudServiceInstance.java: -------------------------------------------------------------------------------- 1 | package com.sap.cloudfoundry.client.facade.domain; 2 | 3 | import java.util.List; 4 | import java.util.Map; 5 | 6 | import org.cloudfoundry.AllowNulls; 7 | import org.cloudfoundry.client.v3.serviceinstances.ServiceInstanceType; 8 | import org.immutables.value.Value; 9 | 10 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 11 | import com.fasterxml.jackson.databind.annotation.JsonSerialize; 12 | import com.sap.cloudfoundry.client.facade.Nullable; 13 | 14 | @Value.Immutable 15 | @JsonSerialize(as = ImmutableCloudServiceInstance.class) 16 | @JsonDeserialize(as = ImmutableCloudServiceInstance.class) 17 | public abstract class CloudServiceInstance extends CloudEntity implements Derivable { 18 | 19 | @Nullable 20 | public abstract String getLabel(); 21 | 22 | @Nullable 23 | public abstract String getPlan(); 24 | 25 | @Nullable 26 | public abstract String getProvider(); 27 | 28 | @Nullable 29 | public abstract String getBroker(); 30 | 31 | @Nullable 32 | public abstract String getVersion(); 33 | 34 | @AllowNulls 35 | public abstract Map getCredentials(); 36 | 37 | @Nullable 38 | public abstract String getSyslogDrainUrl(); 39 | 40 | public abstract List getTags(); 41 | 42 | @Nullable 43 | public abstract ServiceInstanceType getType(); 44 | 45 | @Nullable 46 | public abstract ServiceOperation getLastOperation(); 47 | 48 | public boolean isUserProvided() { 49 | return getType() != null && getType().equals(ServiceInstanceType.USER_PROVIDED); 50 | } 51 | 52 | @Override 53 | public CloudServiceInstance derive() { 54 | return this; 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/com/sap/cloudfoundry/client/facade/domain/CloudTask.java: -------------------------------------------------------------------------------- 1 | package com.sap.cloudfoundry.client.facade.domain; 2 | 3 | import org.immutables.value.Value; 4 | 5 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 6 | import com.fasterxml.jackson.databind.annotation.JsonSerialize; 7 | import com.sap.cloudfoundry.client.facade.Nullable; 8 | import com.sap.cloudfoundry.client.facade.domain.ImmutableCloudTask.ImmutableLimits; 9 | import com.sap.cloudfoundry.client.facade.domain.ImmutableCloudTask.ImmutableResult; 10 | 11 | @Value.Enclosing 12 | @Value.Immutable 13 | @JsonSerialize(as = ImmutableCloudTask.class) 14 | @JsonDeserialize(as = ImmutableCloudTask.class) 15 | public abstract class CloudTask extends CloudEntity implements Derivable { 16 | 17 | @Nullable 18 | public abstract String getCommand(); 19 | 20 | @Nullable 21 | public abstract Limits getLimits(); 22 | 23 | @Nullable 24 | public abstract Result getResult(); 25 | 26 | @Nullable 27 | public abstract State getState(); 28 | 29 | @Override 30 | public CloudTask derive() { 31 | return this; 32 | } 33 | 34 | public enum State { 35 | PENDING, RUNNING, SUCCEEDED, CANCELING, FAILED 36 | } 37 | 38 | @Value.Immutable 39 | @JsonSerialize(as = ImmutableResult.class) 40 | @JsonDeserialize(as = ImmutableResult.class) 41 | public interface Result { 42 | 43 | @Nullable 44 | @Value.Parameter 45 | String getFailureReason(); 46 | 47 | } 48 | 49 | @Value.Immutable 50 | @JsonSerialize(as = ImmutableLimits.class) 51 | @JsonDeserialize(as = ImmutableLimits.class) 52 | public interface Limits { 53 | 54 | @Nullable 55 | Integer getDisk(); 56 | 57 | @Nullable 58 | Integer getMemory(); 59 | 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/com/sap/cloudfoundry/client/facade/CloudOperationException.java: -------------------------------------------------------------------------------- 1 | package com.sap.cloudfoundry.client.facade; 2 | 3 | import org.springframework.http.HttpStatus; 4 | 5 | @SuppressWarnings("serial") 6 | public class CloudOperationException extends CloudException { 7 | 8 | private final HttpStatus statusCode; 9 | private final String statusText; 10 | private final String description; 11 | 12 | public CloudOperationException(HttpStatus statusCode) { 13 | this(statusCode, statusCode.getReasonPhrase()); 14 | } 15 | 16 | public CloudOperationException(HttpStatus statusCode, String statusText) { 17 | this(statusCode, statusText, null); 18 | } 19 | 20 | public CloudOperationException(HttpStatus statusCode, String statusText, String description) { 21 | this(statusCode, statusText, description, null); 22 | } 23 | 24 | public CloudOperationException(HttpStatus statusCode, String statusText, String description, Throwable cause) { 25 | super(getExceptionMessage(statusCode, statusText, description), cause); 26 | this.statusCode = statusCode; 27 | this.statusText = statusText; 28 | this.description = description; 29 | } 30 | 31 | private static String getExceptionMessage(HttpStatus statusCode, String statusText, String description) { 32 | if (description != null) { 33 | return String.format("%d %s: %s", statusCode.value(), statusText, description); 34 | } 35 | return String.format("%d %s", statusCode.value(), statusText); 36 | } 37 | 38 | public HttpStatus getStatusCode() { 39 | return statusCode; 40 | } 41 | 42 | public String getStatusText() { 43 | return statusText; 44 | } 45 | 46 | public String getDescription() { 47 | return description; 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/com/sap/cloudfoundry/client/facade/adapters/RawCloudServiceBroker.java: -------------------------------------------------------------------------------- 1 | package com.sap.cloudfoundry.client.facade.adapters; 2 | 3 | import java.util.Optional; 4 | 5 | import org.cloudfoundry.client.v3.Relationship; 6 | import org.cloudfoundry.client.v3.ToOneRelationship; 7 | import org.cloudfoundry.client.v3.servicebrokers.ServiceBroker; 8 | import org.cloudfoundry.client.v3.servicebrokers.ServiceBrokerRelationships; 9 | import org.immutables.value.Value; 10 | 11 | import com.sap.cloudfoundry.client.facade.domain.CloudServiceBroker; 12 | import com.sap.cloudfoundry.client.facade.domain.ImmutableCloudServiceBroker; 13 | 14 | @Value.Immutable 15 | public abstract class RawCloudServiceBroker extends RawCloudEntity { 16 | 17 | @Value.Parameter 18 | public abstract ServiceBroker getServiceBroker(); 19 | 20 | @Override 21 | public CloudServiceBroker derive() { 22 | ServiceBroker serviceBroker = getServiceBroker(); 23 | String spaceGuid = getSpaceGuid(serviceBroker); 24 | return ImmutableCloudServiceBroker.builder() 25 | .metadata(parseResourceMetadata(serviceBroker)) 26 | .name(serviceBroker.getName()) 27 | .url(serviceBroker.getUrl()) 28 | .spaceGuid(spaceGuid) 29 | .build(); 30 | } 31 | 32 | private String getSpaceGuid(ServiceBroker serviceBroker) { 33 | return Optional.ofNullable(serviceBroker.getRelationships()) 34 | .map(ServiceBrokerRelationships::getSpace) 35 | .map(ToOneRelationship::getData) 36 | .map(Relationship::getId) 37 | .orElse(null); 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/com/sap/cloudfoundry/client/facade/adapters/RawCloudAsyncJob.java: -------------------------------------------------------------------------------- 1 | package com.sap.cloudfoundry.client.facade.adapters; 2 | 3 | import java.util.stream.Collectors; 4 | 5 | import org.cloudfoundry.client.v3.jobs.Job; 6 | import org.cloudfoundry.client.v3.jobs.Warning; 7 | import org.immutables.value.Value; 8 | 9 | import com.sap.cloudfoundry.client.facade.domain.CloudAsyncJob; 10 | import com.sap.cloudfoundry.client.facade.domain.ImmutableCloudAsyncJob; 11 | 12 | @Value.Immutable 13 | public abstract class RawCloudAsyncJob extends RawCloudEntity { 14 | 15 | @Value.Parameter 16 | public abstract Job getJob(); 17 | 18 | @Override 19 | public CloudAsyncJob derive() { 20 | Job job = getJob(); 21 | return ImmutableCloudAsyncJob.builder() 22 | .metadata(parseResourceMetadata(job)) 23 | .state(job.getState()) 24 | .operation(job.getOperation()) 25 | .warnings(getWarnings(job)) 26 | .errors(getErrors(job)) 27 | .build(); 28 | } 29 | 30 | private String getWarnings(Job job) { 31 | return job.getWarnings() 32 | .stream() 33 | .map(Warning::getDetail) 34 | .collect(Collectors.joining(",")); 35 | } 36 | 37 | private String getErrors(Job job) { 38 | return job.getErrors() 39 | .stream() 40 | .map(this::joinErrorDetails) 41 | .collect(Collectors.joining(",")); 42 | } 43 | 44 | private String joinErrorDetails(org.cloudfoundry.client.v3.Error error) { 45 | return String.join(" ", error.getCode() 46 | .toString(), 47 | error.getTitle(), error.getDetail()); 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/com/sap/cloudfoundry/client/facade/Messages.java: -------------------------------------------------------------------------------- 1 | package com.sap.cloudfoundry.client.facade; 2 | 3 | public final class Messages { 4 | 5 | private Messages() { 6 | 7 | } 8 | 9 | // INFO messages 10 | public static final String CALLING_CF_ROOT_0_TO_ACCESS_LOG_CACHE_URL = "Calling CF root: {0} to access log-cache URL"; 11 | public static final String CF_ROOT_REQUEST_FINISHED = "CF root request finished"; 12 | public static final String TRYING_TO_GET_APP_LOGS = "Trying to get app logs"; 13 | public static final String APP_LOGS_WERE_FETCHED_SUCCESSFULLY = "App logs were fetched successfully"; 14 | 15 | // WARN messages 16 | public static final String RETRYING_OPERATION = "Retrying operation that failed with: {0}"; 17 | public static final String CALL_TO_0_FAILED_WITH_1 = "Calling {0} failed with: {1}"; 18 | 19 | // ERROR messages 20 | public static final String UNKNOWN_PACKAGE_TYPE = "Unknown package type: %s"; 21 | public static final String CANT_CREATE_SERVICE_KEY_FOR_USER_PROVIDED_SERVICE = "Service keys can't be created for user-provided service instance \"%s\""; 22 | public static final String NO_SERVICE_PLAN_FOUND = "Service plan with guid \"{0}\" for service instance with name \"{1}\" was not found."; 23 | public static final String SERVICE_PLAN_WITH_GUID_0_NOT_AVAILABLE_FOR_SERVICE_INSTANCE_1 = "Service plan with guid \"{0}\" is not available for service instance \"{1}\"."; 24 | public static final String SERVICE_OFFERING_WITH_GUID_0_IS_NOT_AVAILABLE = "Service offering with guid \"{0}\" is not available."; 25 | public static final String SERVICE_OFFERING_WITH_GUID_0_NOT_FOUND = "Service offering with guid \"{0}\" not found."; 26 | public static final String FAILED_TO_FETCH_APP_LOGS_FOR_APP = "Failed to fetch app logs for app: %s"; 27 | 28 | public static final String BUILDPACKS_ARE_REQUIRED_FOR_CNB_LIFECYCLE_TYPE = "Buildpacks are required for CNB lifecycle type."; 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/sap/cloudfoundry/client/facade/domain/CloudBuild.java: -------------------------------------------------------------------------------- 1 | package com.sap.cloudfoundry.client.facade.domain; 2 | 3 | import java.util.UUID; 4 | 5 | import org.immutables.value.Value; 6 | 7 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 8 | import com.fasterxml.jackson.databind.annotation.JsonSerialize; 9 | import com.sap.cloudfoundry.client.facade.Nullable; 10 | import com.sap.cloudfoundry.client.facade.domain.ImmutableCloudBuild.ImmutableCreatedBy; 11 | import com.sap.cloudfoundry.client.facade.domain.ImmutableCloudBuild.ImmutablePackageInfo; 12 | 13 | @Value.Enclosing 14 | @Value.Immutable 15 | @JsonSerialize(as = ImmutableCloudBuild.class) 16 | @JsonDeserialize(as = ImmutableCloudBuild.class) 17 | public abstract class CloudBuild extends CloudEntity implements Derivable { 18 | 19 | @Nullable 20 | public abstract State getState(); 21 | 22 | @Nullable 23 | public abstract CreatedBy getCreatedBy(); 24 | 25 | @Nullable 26 | public abstract DropletInfo getDropletInfo(); 27 | 28 | @Nullable 29 | public abstract PackageInfo getPackageInfo(); 30 | 31 | @Nullable 32 | public abstract String getError(); 33 | 34 | @Override 35 | public CloudBuild derive() { 36 | return this; 37 | } 38 | 39 | public enum State { 40 | FAILED, STAGED, STAGING 41 | } 42 | 43 | @Value.Immutable 44 | @JsonSerialize(as = ImmutablePackageInfo.class) 45 | @JsonDeserialize(as = ImmutablePackageInfo.class) 46 | public interface PackageInfo { 47 | 48 | @Nullable 49 | @Value.Parameter 50 | UUID getGuid(); 51 | 52 | } 53 | 54 | @Value.Immutable 55 | @JsonSerialize(as = ImmutableCreatedBy.class) 56 | @JsonDeserialize(as = ImmutableCreatedBy.class) 57 | public interface CreatedBy { 58 | 59 | @Nullable 60 | UUID getGuid(); 61 | 62 | @Nullable 63 | String getName(); 64 | 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /.reuse/dep5: -------------------------------------------------------------------------------- 1 | Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ 2 | Upstream-Name: cf-java-client-sap 3 | Upstream-Contact: boyan.velinov@sap.com 4 | Source: https://github.com/SAP/cf-java-client-sap 5 | Disclaimer: The code in this project may include calls to APIs (“API Calls”) of 6 | SAP or third-party products or services developed outside of this project 7 | (“External Products”). 8 | “APIs” means application programming interfaces, as well as their respective 9 | specifications and implementing code that allows software to communicate with 10 | other software. 11 | API Calls to External Products are not licensed under the open source license 12 | that governs this project. The use of such API Calls and related External 13 | Products are subject to applicable additional agreements with the relevant 14 | provider of the External Products. In no event shall the open source license 15 | that governs this project grant any rights in or to any External Products,or 16 | alter, expand or supersede any terms of the applicable additional agreements. 17 | If you have a valid license agreement with SAP for the use of a particular SAP 18 | External Product, then you may make use of any API Calls included in this 19 | project’s code for that SAP External Product, subject to the terms of such 20 | license agreement. If you do not have a valid license agreement for the use of 21 | a particular SAP External Product, then you may only make use of any API Calls 22 | in this project for that SAP External Product for your internal, non-productive 23 | and non-commercial test and evaluation of such API Calls. Nothing herein grants 24 | you any rights to use or access any SAP External Product, or provide any third 25 | parties the right to use of access any SAP External Product, through API Calls. 26 | 27 | Files: * 28 | Copyright: 2017-2020 SAP SE or an SAP affiliate company and cf-java-client-sap contributors 29 | License: Apache-2.0 30 | -------------------------------------------------------------------------------- /src/test/java/com/sap/cloudfoundry/client/facade/IntegrationTestConstants.java: -------------------------------------------------------------------------------- 1 | package com.sap.cloudfoundry.client.facade; 2 | 3 | public class IntegrationTestConstants { 4 | 5 | private IntegrationTestConstants() { 6 | } 7 | 8 | public static final String HEALTH_CHECK_ENDPOINT = "/public/ping"; 9 | public static final String JAVA_BUILDPACK = "java_buildpack"; 10 | public static final String NODEJS_BUILDPACK = "nodejs_buildpack"; 11 | public static final String STATICFILE_BUILDPACK = "staticfile_buildpack"; 12 | public static final int HEALTH_CHECK_TIMEMOUT = 100; 13 | public static final int DISK_IN_MB = 128; 14 | public static final int MEMORY_IN_MB = 128; 15 | public static final String DEFAULT_DOMAIN = "deploy-service.custom.domain.for.integration.tests"; 16 | public static final String APPLICATION_HOST = "test-application-hostname-ztana-test"; 17 | public static final String STATICFILE_APPLICATION_CONTENT = "staticfile.zip"; 18 | public static final String JAVA_BUILDPACK_URL = "https://github.com/paketo-buildpacks/java"; 19 | public static final String NODEJS_BUILDPACK_URL = "https://github.com/paketo-buildpacks/nodejs"; 20 | 21 | // Service broker constants 22 | public static final int SERVICE_BROKER_DISK_IN_MB = 256; 23 | public static final int SERVICE_BROKER_MEMORY_IN_MB = 1024; 24 | public static final String SERVICE_BROKER_HOST = "test-service-foo-broker"; 25 | public static final String SERVICE_BROKER_APP_NAME = "test-service-broker-app"; 26 | public static final String SERVICE_BROKER_NAME = "test-service-broker"; 27 | public static final String SERVICE_BROKER_ENV_CONTENT = "service-broker-env.json"; 28 | public static final String SERVICE_BROKER_USERNAME = "test-user"; 29 | public static final String SERVICE_BROKER_PASSWORD = "test-password"; 30 | public static final String SERVICE_OFFERING = "finch"; 31 | public static final String SERVICE_PLAN = "grey"; 32 | public static final String SERVICE_PLAN_2 = "white"; 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/com/sap/cloudfoundry/client/facade/adapters/RawCloudServicePlan.java: -------------------------------------------------------------------------------- 1 | package com.sap.cloudfoundry.client.facade.adapters; 2 | 3 | import org.cloudfoundry.client.v3.serviceplans.ServicePlan; 4 | import org.cloudfoundry.client.v3.serviceplans.Visibility; 5 | import org.immutables.value.Value; 6 | 7 | import com.sap.cloudfoundry.client.facade.domain.CloudServicePlan; 8 | import com.sap.cloudfoundry.client.facade.domain.ImmutableCloudServicePlan; 9 | 10 | @Value.Immutable 11 | public abstract class RawCloudServicePlan extends RawCloudEntity { 12 | 13 | @Value.Parameter 14 | public abstract ServicePlan getResource(); 15 | 16 | @Override 17 | public CloudServicePlan derive() { 18 | ServicePlan resource = getResource(); 19 | return ImmutableCloudServicePlan.builder() 20 | .metadata(parseResourceMetadata(resource)) 21 | .name(resource.getName()) 22 | .description(resource.getDescription()) 23 | .extra(resource.getBrokerCatalog() 24 | .getMetadata()) 25 | .uniqueId(resource.getBrokerCatalog() 26 | .getBrokerCatalogId()) 27 | .serviceOfferingId(resource.getRelationships() 28 | .getServiceOffering() 29 | .getData() 30 | .getId()) 31 | .isPublic(resource.getVisibilityType() 32 | .equals(Visibility.PUBLIC)) 33 | .isFree(resource.getFree()) 34 | .build(); 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/com/sap/cloudfoundry/client/facade/adapters/RawCloudServiceKey.java: -------------------------------------------------------------------------------- 1 | package com.sap.cloudfoundry.client.facade.adapters; 2 | 3 | import java.util.Map; 4 | 5 | import org.cloudfoundry.AllowNulls; 6 | import org.cloudfoundry.client.v3.servicebindings.ServiceBindingResource; 7 | import org.immutables.value.Value; 8 | 9 | import com.sap.cloudfoundry.client.facade.Nullable; 10 | import com.sap.cloudfoundry.client.facade.domain.CloudServiceInstance; 11 | import com.sap.cloudfoundry.client.facade.domain.CloudServiceKey; 12 | import com.sap.cloudfoundry.client.facade.domain.Derivable; 13 | import com.sap.cloudfoundry.client.facade.domain.ImmutableCloudServiceKey; 14 | import com.sap.cloudfoundry.client.facade.domain.ServiceCredentialBindingOperation; 15 | 16 | @Value.Immutable 17 | public abstract class RawCloudServiceKey extends RawCloudEntity { 18 | 19 | public abstract ServiceBindingResource getServiceBindingResource(); 20 | 21 | @Nullable 22 | @AllowNulls 23 | public abstract Map getCredentials(); 24 | 25 | public abstract Derivable getServiceInstance(); 26 | 27 | @Override 28 | public CloudServiceKey derive() { 29 | ServiceBindingResource serviceBindingResource = getServiceBindingResource(); 30 | Derivable serviceInstance = getServiceInstance(); 31 | return ImmutableCloudServiceKey.builder() 32 | .metadata(parseResourceMetadata(serviceBindingResource)) 33 | .v3Metadata(serviceBindingResource.getMetadata()) 34 | .name(serviceBindingResource.getName()) 35 | .credentials(getCredentials()) 36 | .serviceInstance(serviceInstance.derive()) 37 | .serviceKeyOperation(ServiceCredentialBindingOperation.from(serviceBindingResource.getLastOperation())) 38 | .build(); 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/com/sap/cloudfoundry/client/facade/UploadStatusCallback.java: -------------------------------------------------------------------------------- 1 | package com.sap.cloudfoundry.client.facade; 2 | 3 | import java.util.Set; 4 | 5 | /** 6 | * Reports status information when uploading an application. 7 | */ 8 | public interface UploadStatusCallback { 9 | 10 | /** 11 | * Empty implementation 12 | */ 13 | UploadStatusCallback NONE = new UploadStatusCallback() { 14 | @Override 15 | public void onCheckResources() { 16 | } 17 | 18 | @Override 19 | public void onMatchedFileNames(Set matchedFileNames) { 20 | } 21 | 22 | @Override 23 | public void onProcessMatchedResources(int length) { 24 | } 25 | 26 | @Override 27 | public boolean onProgress(String status) { 28 | return false; 29 | } 30 | 31 | @Override 32 | public void onError(String description) { 33 | } 34 | }; 35 | 36 | /** 37 | * Called after the /resources call is made. 38 | */ 39 | void onCheckResources(); 40 | 41 | /** 42 | * Called after the files to be uploaded have been identified. 43 | * 44 | * @param matchedFileNames the files to be uploaded 45 | */ 46 | void onMatchedFileNames(Set matchedFileNames); 47 | 48 | /** 49 | * Called after the data to be uploaded has been processed 50 | * 51 | * @param length the size of the upload data (before compression) 52 | */ 53 | void onProcessMatchedResources(int length); 54 | 55 | /** 56 | * Called during asynchronous upload process. 57 | * 58 | * Implementation can return true to unsubscribe from progress update reports. This is useful if the caller want to unblock the thread 59 | * that initiated the upload. Note, however, that the upload job that has been asynchronously started will continue to execute on the 60 | * server. 61 | * 62 | * @param status string such as "queued", "finished" 63 | * @return true to unsubscribe from update report 64 | */ 65 | boolean onProgress(String status); 66 | 67 | void onError(String description); 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/com/sap/cloudfoundry/client/facade/adapters/RawCloudTask.java: -------------------------------------------------------------------------------- 1 | package com.sap.cloudfoundry.client.facade.adapters; 2 | 3 | import java.util.Optional; 4 | 5 | import org.cloudfoundry.client.v3.tasks.Result; 6 | import org.cloudfoundry.client.v3.tasks.Task; 7 | import org.cloudfoundry.client.v3.tasks.TaskState; 8 | import org.immutables.value.Value; 9 | 10 | import com.sap.cloudfoundry.client.facade.domain.CloudTask; 11 | import com.sap.cloudfoundry.client.facade.domain.ImmutableCloudTask; 12 | 13 | @Value.Immutable 14 | public abstract class RawCloudTask extends RawCloudEntity { 15 | 16 | @Value.Parameter 17 | public abstract Task getResource(); 18 | 19 | @Override 20 | public CloudTask derive() { 21 | Task resource = getResource(); 22 | return ImmutableCloudTask.builder() 23 | .metadata(parseResourceMetadata(resource)) 24 | .name(resource.getName()) 25 | .command(resource.getCommand()) 26 | .limits(parseLimits(resource)) 27 | .result(parseResult(resource)) 28 | .state(parseState(resource.getState())) 29 | .build(); 30 | } 31 | 32 | private static CloudTask.Result parseResult(Task resource) { 33 | return Optional.ofNullable(resource.getResult()) 34 | .map(Result::getFailureReason) 35 | .map(ImmutableCloudTask.ImmutableResult::of) 36 | .orElse(null); 37 | } 38 | 39 | private static CloudTask.Limits parseLimits(Task resource) { 40 | return ImmutableCloudTask.ImmutableLimits.builder() 41 | .disk(resource.getDiskInMb()) 42 | .memory(resource.getMemoryInMb()) 43 | .build(); 44 | } 45 | 46 | private static CloudTask.State parseState(TaskState state) { 47 | return parseEnum(state, CloudTask.State.class); 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/com/sap/cloudfoundry/client/facade/adapters/RawInstancesInfo.java: -------------------------------------------------------------------------------- 1 | package com.sap.cloudfoundry.client.facade.adapters; 2 | 3 | import java.util.Collections; 4 | import java.util.List; 5 | import java.util.stream.Collectors; 6 | 7 | import org.cloudfoundry.client.v3.applications.GetApplicationProcessStatisticsResponse; 8 | import org.cloudfoundry.client.v3.processes.ProcessStatisticsResource; 9 | import org.immutables.value.Value; 10 | 11 | import com.sap.cloudfoundry.client.facade.domain.ImmutableInstanceInfo; 12 | import com.sap.cloudfoundry.client.facade.domain.ImmutableInstancesInfo; 13 | import com.sap.cloudfoundry.client.facade.domain.InstanceInfo; 14 | import com.sap.cloudfoundry.client.facade.domain.InstanceState; 15 | import com.sap.cloudfoundry.client.facade.domain.InstancesInfo; 16 | 17 | @Value.Immutable 18 | public abstract class RawInstancesInfo extends RawCloudEntity { 19 | 20 | @Value.Parameter 21 | public abstract GetApplicationProcessStatisticsResponse getProcessStatisticsResponse(); 22 | 23 | @Override 24 | public InstancesInfo derive() { 25 | var processStats = getProcessStatisticsResponse(); 26 | return ImmutableInstancesInfo.builder() 27 | .instances(parseProcessStatistics(processStats.getResources())) 28 | .build(); 29 | } 30 | 31 | private static List parseProcessStatistics(List stats) { 32 | if (stats == null) { 33 | return Collections.emptyList(); 34 | } 35 | return stats.stream() 36 | .map(RawInstancesInfo::parseProcessStatistic) 37 | .collect(Collectors.toList()); 38 | } 39 | 40 | private static InstanceInfo parseProcessStatistic(ProcessStatisticsResource statsResource) { 41 | return ImmutableInstanceInfo.builder() 42 | .index(statsResource.getIndex()) 43 | .state(InstanceState.valueOfWithDefault(statsResource.getState())) 44 | .build(); 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/com/sap/cloudfoundry/client/facade/adapters/RawCloudServiceBinding.java: -------------------------------------------------------------------------------- 1 | package com.sap.cloudfoundry.client.facade.adapters; 2 | 3 | import java.util.UUID; 4 | 5 | import org.cloudfoundry.client.v3.servicebindings.ServiceBinding; 6 | import org.cloudfoundry.client.v3.servicebindings.ServiceBindingResource; 7 | import org.immutables.value.Value; 8 | 9 | import com.sap.cloudfoundry.client.facade.domain.CloudServiceBinding; 10 | import com.sap.cloudfoundry.client.facade.domain.ImmutableCloudServiceBinding; 11 | import com.sap.cloudfoundry.client.facade.domain.ServiceCredentialBindingOperation; 12 | 13 | @Value.Immutable 14 | public abstract class RawCloudServiceBinding extends RawCloudEntity { 15 | 16 | @Value.Parameter 17 | public abstract ServiceBindingResource getServiceBinding(); 18 | 19 | @Override 20 | public CloudServiceBinding derive() { 21 | ServiceBinding serviceBinding = getServiceBinding(); 22 | var appRelationship = serviceBinding.getRelationships() 23 | .getApplication(); 24 | return ImmutableCloudServiceBinding.builder() 25 | .metadata(parseResourceMetadata(serviceBinding)) 26 | .applicationGuid(parseNullableGuid(appRelationship == null ? null 27 | : appRelationship.getData() 28 | .getId())) 29 | .serviceInstanceGuid(UUID.fromString(serviceBinding.getRelationships() 30 | .getServiceInstance() 31 | .getData() 32 | .getId())) 33 | .serviceBindingOperation(ServiceCredentialBindingOperation.from(getServiceBinding().getLastOperation())) 34 | .build(); 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/com/sap/cloudfoundry/client/facade/domain/Staging.java: -------------------------------------------------------------------------------- 1 | package com.sap.cloudfoundry.client.facade.domain; 2 | 3 | import java.util.List; 4 | 5 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 6 | import com.fasterxml.jackson.databind.annotation.JsonSerialize; 7 | import com.sap.cloudfoundry.client.facade.Nullable; 8 | import com.sap.cloudfoundry.client.facade.SkipNulls; 9 | import org.immutables.value.Value; 10 | 11 | @Value.Immutable 12 | @JsonSerialize(as = ImmutableStaging.class) 13 | @JsonDeserialize(as = ImmutableStaging.class) 14 | public interface Staging { 15 | /** 16 | * @return The buildpacks, or empty to use the default buildpack detected based on application content 17 | */ 18 | @SkipNulls 19 | List getBuildpacks(); 20 | 21 | /** 22 | * @return The start command to use 23 | */ 24 | @Nullable 25 | String getCommand(); 26 | 27 | /** 28 | * @return Raw, free-form information regarding a detected buildpack, or null if no detected buildpack was resolved. For example, if the 29 | * application is stopped, the detected buildpack may be null. 30 | */ 31 | @Nullable 32 | String getDetectedBuildpack(); 33 | 34 | /** 35 | * @return the health check timeout value 36 | */ 37 | @Nullable 38 | Integer getHealthCheckTimeout(); 39 | 40 | /** 41 | * @return health check type 42 | */ 43 | @Nullable 44 | String getHealthCheckType(); 45 | 46 | /** 47 | * @return health check http endpoint value 48 | */ 49 | @Nullable 50 | String getHealthCheckHttpEndpoint(); 51 | 52 | /** 53 | * @return boolean value to see if ssh is enabled 54 | */ 55 | @Nullable 56 | Boolean isSshEnabled(); 57 | 58 | /** 59 | * @return the stack to use when staging the application, or null to use the default stack 60 | */ 61 | @Nullable 62 | String getStackName(); 63 | 64 | @Nullable 65 | DockerInfo getDockerInfo(); 66 | 67 | @Nullable 68 | Integer getInvocationTimeout(); 69 | 70 | @Nullable 71 | LifecycleType getLifecycleType(); 72 | 73 | default String getBuildpack() { 74 | return getBuildpacks().isEmpty() ? null : getBuildpacks().get(0); 75 | } 76 | 77 | } 78 | -------------------------------------------------------------------------------- /src/main/java/com/sap/cloudfoundry/client/facade/domain/CloudPackage.java: -------------------------------------------------------------------------------- 1 | package com.sap.cloudfoundry.client.facade.domain; 2 | 3 | import java.util.Arrays; 4 | import java.util.Objects; 5 | 6 | import org.immutables.value.Value; 7 | 8 | import com.fasterxml.jackson.annotation.JsonCreator; 9 | import com.fasterxml.jackson.annotation.JsonSubTypes; 10 | import com.fasterxml.jackson.annotation.JsonTypeInfo; 11 | import com.fasterxml.jackson.annotation.JsonValue; 12 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 13 | import com.fasterxml.jackson.databind.annotation.JsonSerialize; 14 | import com.sap.cloudfoundry.client.facade.Messages; 15 | import com.sap.cloudfoundry.client.facade.Nullable; 16 | 17 | @Value.Immutable 18 | @JsonSerialize(as = ImmutableCloudPackage.class) 19 | @JsonDeserialize(as = ImmutableCloudPackage.class) 20 | public abstract class CloudPackage extends CloudEntity implements Derivable { 21 | 22 | @Nullable 23 | public abstract Type getType(); 24 | 25 | @Nullable 26 | @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXTERNAL_PROPERTY, property = "type") 27 | @JsonSubTypes({ @JsonSubTypes.Type(name = "bits", value = BitsData.class), 28 | @JsonSubTypes.Type(name = "docker", value = DockerData.class) }) 29 | public abstract PackageData getData(); 30 | 31 | @Nullable 32 | public abstract Status getStatus(); 33 | 34 | @Override 35 | public CloudPackage derive() { 36 | return this; 37 | } 38 | 39 | public enum Type { 40 | BITS, DOCKER; 41 | 42 | @JsonCreator 43 | public static Type from(String s) { 44 | Objects.requireNonNull(s); 45 | return Arrays.stream(Type.values()) 46 | .filter(type -> s.toLowerCase() 47 | .equals(type.toString())) 48 | .findFirst() 49 | .orElseThrow(() -> new IllegalArgumentException(String.format(Messages.UNKNOWN_PACKAGE_TYPE, s))); 50 | } 51 | 52 | @JsonValue 53 | @Override 54 | public String toString() { 55 | return name().toLowerCase(); 56 | } 57 | } 58 | 59 | public interface PackageData { 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /src/test/java/com/sap/cloudfoundry/client/facade/adapters/RawUserRoleTest.java: -------------------------------------------------------------------------------- 1 | package com.sap.cloudfoundry.client.facade.adapters; 2 | 3 | import org.cloudfoundry.client.v3.Relationship; 4 | import org.cloudfoundry.client.v3.ToOneRelationship; 5 | import org.cloudfoundry.client.v3.roles.RoleRelationships; 6 | import org.cloudfoundry.client.v3.roles.RoleResource; 7 | import org.cloudfoundry.client.v3.roles.RoleType; 8 | import org.junit.jupiter.api.Test; 9 | 10 | import com.sap.cloudfoundry.client.facade.domain.UserRole; 11 | 12 | class RawUserRoleTest { 13 | 14 | private static final String USER_ROLE_GUID = "c94b4588-3431-11ed-a261-0242ac120002"; 15 | private static final String USER_GUID = "328eaf4e-3432-11ed-a261-0242ac120002"; 16 | private static final String DATE = "2022-01-01T13:35:11Z"; 17 | private static final RoleType USER_ROLE_TYPE = RoleType.SPACE_DEVELOPER; 18 | 19 | @Test 20 | void testDerive() { 21 | RawCloudEntityTest.testDerive(buildExpectedUserRole(), buildActualUserRole()); 22 | } 23 | 24 | private UserRole buildExpectedUserRole() { 25 | return UserRole.SPACE_DEVELOPER; 26 | } 27 | 28 | private RawUserRole buildActualUserRole() { 29 | return ImmutableRawUserRole.builder() 30 | .roleResource(buildRoleResource()) 31 | .build(); 32 | } 33 | 34 | private RoleResource buildRoleResource() { 35 | return RoleResource.builder() 36 | .createdAt(DATE) 37 | .id(USER_ROLE_GUID) 38 | .type(USER_ROLE_TYPE) 39 | .relationships(RoleRelationships.builder() 40 | .user(ToOneRelationship.builder() 41 | .data(Relationship.builder() 42 | .id(USER_GUID) 43 | .build()) 44 | .build()) 45 | .build()) 46 | .build(); 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/com/sap/cloudfoundry/client/facade/util/JobV3Util.java: -------------------------------------------------------------------------------- 1 | package com.sap.cloudfoundry.client.facade.util; 2 | 3 | import java.time.Duration; 4 | import java.util.EnumSet; 5 | import java.util.List; 6 | import java.util.Set; 7 | 8 | import org.cloudfoundry.client.CloudFoundryClient; 9 | import org.cloudfoundry.client.v3.ClientV3Exception; 10 | import org.cloudfoundry.client.v3.Error; 11 | import org.cloudfoundry.client.v3.jobs.GetJobRequest; 12 | import org.cloudfoundry.client.v3.jobs.GetJobResponse; 13 | import org.cloudfoundry.client.v3.jobs.Job; 14 | import org.cloudfoundry.client.v3.jobs.JobState; 15 | import org.cloudfoundry.util.DelayUtils; 16 | 17 | import reactor.core.publisher.Mono; 18 | 19 | public class JobV3Util { 20 | 21 | private JobV3Util() { 22 | } 23 | 24 | private static final Set FINAL_STATES = EnumSet.of(JobState.COMPLETE, JobState.FAILED); 25 | 26 | public static Mono waitForCompletion(CloudFoundryClient cloudFoundryClient, Duration completionTimeout, String jobId) { 27 | return requestJobV3(cloudFoundryClient, jobId).filter(job -> FINAL_STATES.contains(job.getState())) 28 | .repeatWhenEmpty(DelayUtils.exponentialBackOff(Duration.ofSeconds(1), 29 | Duration.ofSeconds(15), 30 | completionTimeout)) 31 | .filter(job -> JobState.FAILED == job.getState()) 32 | .flatMap(JobV3Util::getError); 33 | } 34 | 35 | private static Mono requestJobV3(CloudFoundryClient cloudFoundryClient, String jobId) { 36 | return cloudFoundryClient.jobsV3() 37 | .get(GetJobRequest.builder() 38 | .jobId(jobId) 39 | .build()); 40 | } 41 | 42 | private static Mono getError(Job job) { 43 | List errors = job.getErrors(); 44 | // Status code must be set, otherwise it will throw NPE during getStatusCode() invocation 45 | return Mono.error(new ClientV3Exception(200, errors)); 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/com/sap/cloudfoundry/client/facade/adapters/RawCloudProcess.java: -------------------------------------------------------------------------------- 1 | package com.sap.cloudfoundry.client.facade.adapters; 2 | 3 | import org.cloudfoundry.client.v3.processes.Data; 4 | import org.cloudfoundry.client.v3.processes.HealthCheck; 5 | import org.cloudfoundry.client.v3.processes.Process; 6 | import org.immutables.value.Value; 7 | 8 | import com.sap.cloudfoundry.client.facade.domain.CloudProcess; 9 | import com.sap.cloudfoundry.client.facade.domain.HealthCheckType; 10 | import com.sap.cloudfoundry.client.facade.domain.ImmutableCloudProcess; 11 | 12 | @Value.Immutable 13 | public abstract class RawCloudProcess extends RawCloudEntity { 14 | 15 | @Value.Parameter 16 | public abstract Process getProcess(); 17 | 18 | @Override 19 | public CloudProcess derive() { 20 | Process process = getProcess(); 21 | HealthCheck healthCheck = process.getHealthCheck(); 22 | Integer healthCheckTimeout = null; 23 | String healthCheckHttpEndpoint = null; 24 | Integer healthCheckInvocationTimeout = null; 25 | if (healthCheck.getData() != null) { 26 | Data healthCheckData = healthCheck.getData(); 27 | healthCheckTimeout = healthCheckData.getTimeout(); 28 | healthCheckInvocationTimeout = healthCheckData.getInvocationTimeout(); 29 | healthCheckHttpEndpoint = healthCheckData.getEndpoint(); 30 | } 31 | return ImmutableCloudProcess.builder() 32 | .command(process.getCommand()) 33 | .instances(process.getInstances()) 34 | .memoryInMb(process.getMemoryInMb()) 35 | .diskInMb(process.getDiskInMb()) 36 | .healthCheckType(HealthCheckType.valueOf(healthCheck.getType() 37 | .getValue() 38 | .toUpperCase())) 39 | .healthCheckHttpEndpoint(healthCheckHttpEndpoint) 40 | .healthCheckTimeout(healthCheckTimeout) 41 | .healthCheckInvocationTimeout(healthCheckInvocationTimeout) 42 | .build(); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/com/sap/cloudfoundry/client/facade/adapters/RawCloudServiceInstance.java: -------------------------------------------------------------------------------- 1 | package com.sap.cloudfoundry.client.facade.adapters; 2 | 3 | import java.util.Optional; 4 | 5 | import org.cloudfoundry.client.v3.serviceinstances.ServiceInstanceResource; 6 | import org.cloudfoundry.client.v3.serviceofferings.ServiceOffering; 7 | import org.cloudfoundry.client.v3.serviceplans.ServicePlan; 8 | import org.immutables.value.Value; 9 | 10 | import com.sap.cloudfoundry.client.facade.Nullable; 11 | import com.sap.cloudfoundry.client.facade.domain.CloudServiceInstance; 12 | import com.sap.cloudfoundry.client.facade.domain.ImmutableCloudServiceInstance; 13 | import com.sap.cloudfoundry.client.facade.domain.ServiceOperation; 14 | 15 | @Value.Immutable 16 | public abstract class RawCloudServiceInstance extends RawCloudEntity { 17 | 18 | @Value.Parameter 19 | public abstract ServiceInstanceResource getResource(); 20 | 21 | @Nullable 22 | public abstract ServicePlan getServicePlan(); 23 | 24 | @Nullable 25 | public abstract ServiceOffering getServiceOffering(); 26 | 27 | @Override 28 | public CloudServiceInstance derive() { 29 | ServiceInstanceResource resource = getResource(); 30 | return ImmutableCloudServiceInstance.builder() 31 | .metadata(parseResourceMetadata(resource)) 32 | .v3Metadata(resource.getMetadata()) 33 | .name(resource.getName()) 34 | .plan(getServicePlanName()) 35 | .label(getLabelName()) 36 | .type(resource.getType()) 37 | .tags(resource.getTags()) 38 | .lastOperation(ServiceOperation.fromLastOperation(resource.getLastOperation())) 39 | .syslogDrainUrl(resource.getSyslogDrainUrl()) 40 | .build(); 41 | } 42 | 43 | private String getServicePlanName() { 44 | return Optional.ofNullable(getServicePlan()) 45 | .map(ServicePlan::getName) 46 | .orElse(null); 47 | } 48 | 49 | private String getLabelName() { 50 | return Optional.ofNullable(getServiceOffering()) 51 | .map(ServiceOffering::getName) 52 | .orElse(null); 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/com/sap/cloudfoundry/client/facade/domain/CloudRoute.java: -------------------------------------------------------------------------------- 1 | package com.sap.cloudfoundry.client.facade.domain; 2 | 3 | import java.util.List; 4 | import java.util.Objects; 5 | 6 | import org.immutables.value.Value; 7 | 8 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 9 | import com.fasterxml.jackson.databind.annotation.JsonSerialize; 10 | import com.sap.cloudfoundry.client.facade.Nullable; 11 | 12 | @Value.Immutable 13 | @JsonSerialize(as = ImmutableCloudRoute.class) 14 | @JsonDeserialize(as = ImmutableCloudRoute.class) 15 | public abstract class CloudRoute extends CloudEntity implements Derivable { 16 | 17 | @Value.Default 18 | public int getAppsUsingRoute() { 19 | return 0; 20 | } 21 | 22 | public abstract CloudDomain getDomain(); 23 | 24 | @Nullable 25 | public abstract String getHost(); 26 | 27 | @Nullable 28 | public abstract String getPath(); 29 | 30 | @Nullable 31 | public abstract Integer getPort(); 32 | 33 | @Nullable 34 | public abstract String getRequestedProtocol(); 35 | 36 | @Nullable 37 | public abstract List getDestinations(); 38 | 39 | public abstract String getUrl(); 40 | 41 | @Override 42 | public CloudRoute derive() { 43 | return this; 44 | } 45 | 46 | @Override 47 | public String toString() { 48 | return getUrl(); 49 | } 50 | 51 | @Override 52 | public int hashCode() { 53 | return Objects.hash(getDomain().getName(), getHost(), getPath(), getPort()); 54 | } 55 | 56 | @Override 57 | public boolean equals(Object object) { 58 | if (object == this) { 59 | return true; 60 | } 61 | if (!(object instanceof CloudRoute)) { 62 | return false; 63 | } 64 | var otherRoute = (CloudRoute) object; 65 | var thisDomain = getDomain().getName(); 66 | var otherDomain = otherRoute.getDomain() 67 | .getName(); 68 | // @formatter:off 69 | return thisDomain.equals(otherDomain) 70 | && areEmptyOrEqual(getHost(), otherRoute.getHost()) 71 | && areEmptyOrEqual(getPath(), otherRoute.getPath()) 72 | && Objects.equals(getPort(), otherRoute.getPort()); 73 | // @formatter:on 74 | } 75 | 76 | private static boolean areEmptyOrEqual(String lhs, String rhs) { 77 | if (lhs == null || lhs.isEmpty()) { 78 | return rhs == null || rhs.isEmpty(); 79 | } 80 | return lhs.equals(rhs); 81 | } 82 | 83 | } 84 | -------------------------------------------------------------------------------- /src/main/java/com/sap/cloudfoundry/client/facade/adapters/RawCloudEvent.java: -------------------------------------------------------------------------------- 1 | package com.sap.cloudfoundry.client.facade.adapters; 2 | 3 | import org.cloudfoundry.client.v3.auditevents.AuditEventActor; 4 | import org.cloudfoundry.client.v3.auditevents.AuditEventResource; 5 | import org.cloudfoundry.client.v3.auditevents.AuditEventTarget; 6 | import org.immutables.value.Value; 7 | 8 | import com.sap.cloudfoundry.client.facade.domain.CloudEvent; 9 | import com.sap.cloudfoundry.client.facade.domain.CloudEvent.Participant; 10 | import com.sap.cloudfoundry.client.facade.domain.ImmutableCloudEvent; 11 | import com.sap.cloudfoundry.client.facade.domain.ImmutableCloudEvent.ImmutableParticipant; 12 | 13 | @Value.Immutable 14 | public abstract class RawCloudEvent extends RawCloudEntity { 15 | 16 | @Value.Parameter 17 | public abstract AuditEventResource getResource(); 18 | 19 | @Override 20 | public CloudEvent derive() { 21 | AuditEventResource resource = getResource(); 22 | return ImmutableCloudEvent.builder() 23 | .metadata(parseResourceMetadata(resource)) 24 | .target(parseTarget(resource)) 25 | .actor(parseActor(resource)) 26 | .type(resource.getType()) 27 | .build(); 28 | } 29 | 30 | private static Participant parseTarget(AuditEventResource resource) { 31 | AuditEventTarget target = resource.getAuditEventTarget(); 32 | if (target == null) { 33 | return ImmutableParticipant.builder() 34 | .build(); 35 | } 36 | return ImmutableParticipant.builder() 37 | .guid(parseNullableGuid(target.getId())) 38 | .name(target.getName()) 39 | .type(target.getType()) 40 | .build(); 41 | } 42 | 43 | private static Participant parseActor(AuditEventResource resource) { 44 | AuditEventActor actor = resource.getAuditEventActor(); 45 | if (actor == null) { 46 | return ImmutableParticipant.builder() 47 | .build(); 48 | } 49 | return ImmutableParticipant.builder() 50 | .guid(parseNullableGuid(actor.getId())) 51 | .name(actor.getName()) 52 | .type(actor.getType()) 53 | .build(); 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /src/test/java/com/sap/cloudfoundry/client/facade/adapters/RawCloudDomainTest.java: -------------------------------------------------------------------------------- 1 | package com.sap.cloudfoundry.client.facade.adapters; 2 | 3 | import org.cloudfoundry.client.v3.ToManyRelationship; 4 | import org.cloudfoundry.client.v3.ToOneRelationship; 5 | import org.cloudfoundry.client.v3.domains.Domain; 6 | import org.cloudfoundry.client.v3.domains.DomainRelationships; 7 | import org.cloudfoundry.client.v3.organizations.GetOrganizationDefaultDomainResponse; 8 | import org.junit.jupiter.api.Test; 9 | 10 | import com.sap.cloudfoundry.client.facade.domain.CloudDomain; 11 | import com.sap.cloudfoundry.client.facade.domain.ImmutableCloudDomain; 12 | 13 | class RawCloudDomainTest { 14 | 15 | private static final String DOMAIN_NAME = "example.com"; 16 | 17 | @Test 18 | void testDerive() { 19 | RawCloudEntityTest.testDerive(buildExpectedDomain(), buildRawDomain()); 20 | } 21 | 22 | private static CloudDomain buildExpectedDomain() { 23 | return ImmutableCloudDomain.builder() 24 | .metadata(RawCloudEntityTest.EXPECTED_METADATA_PARSED_FROM_V3_RESOURCE) 25 | .name(DOMAIN_NAME) 26 | .build(); 27 | } 28 | 29 | private static RawCloudDomain buildRawDomain() { 30 | return ImmutableRawCloudDomain.of(buildTestResource()); 31 | } 32 | 33 | private static Domain buildTestResource() { 34 | return GetOrganizationDefaultDomainResponse.builder() 35 | .id(RawCloudEntityTest.GUID_STRING) 36 | .createdAt(RawCloudEntityTest.CREATED_AT_STRING) 37 | .updatedAt(RawCloudEntityTest.UPDATED_AT_STRING) 38 | .name(DOMAIN_NAME) 39 | .isInternal(false) 40 | .relationships(DomainRelationships.builder() 41 | .organization(ToOneRelationship.builder() 42 | .build()) 43 | .sharedOrganizations(ToManyRelationship.builder() 44 | .build()) 45 | .build()) 46 | .build(); 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/com/sap/cloudfoundry/client/facade/adapters/RawCloudServiceOffering.java: -------------------------------------------------------------------------------- 1 | package com.sap.cloudfoundry.client.facade.adapters; 2 | 3 | import java.util.List; 4 | 5 | import org.cloudfoundry.client.v3.serviceofferings.ServiceOfferingResource; 6 | import org.immutables.value.Value; 7 | 8 | import com.sap.cloudfoundry.client.facade.domain.CloudServiceOffering; 9 | import com.sap.cloudfoundry.client.facade.domain.CloudServicePlan; 10 | import com.sap.cloudfoundry.client.facade.domain.Derivable; 11 | import com.sap.cloudfoundry.client.facade.domain.ImmutableCloudServiceOffering; 12 | 13 | @Value.Immutable 14 | public abstract class RawCloudServiceOffering extends RawCloudEntity { 15 | 16 | public abstract ServiceOfferingResource getServiceOffering(); 17 | 18 | public abstract List> getServicePlans(); 19 | 20 | @Override 21 | public CloudServiceOffering derive() { 22 | ServiceOfferingResource serviceOffering = getServiceOffering(); 23 | return ImmutableCloudServiceOffering.builder() 24 | .metadata(parseResourceMetadata(serviceOffering)) 25 | .name(serviceOffering.getName()) 26 | .isAvailable(serviceOffering.getAvailable()) 27 | .isBindable(serviceOffering.getBrokerCatalog() 28 | .getFeatures() 29 | .getBindable()) 30 | .description(serviceOffering.getDescription()) 31 | .isShareable(serviceOffering.getShareable()) 32 | .extra(serviceOffering.getBrokerCatalog() 33 | .getMetadata()) 34 | .docUrl(serviceOffering.getDocumentationUrl()) 35 | .brokerId(serviceOffering.getRelationships() 36 | .getServiceBroker() 37 | .getData() 38 | .getId()) 39 | .uniqueId(serviceOffering.getBrokerCatalog() 40 | .getBrokerCatalogId()) 41 | .servicePlans(derive(getServicePlans())) 42 | .build(); 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /src/test/java/com/sap/cloudfoundry/client/facade/DomainsCloudControllerClientIntegrationTest.java: -------------------------------------------------------------------------------- 1 | package com.sap.cloudfoundry.client.facade; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertEquals; 4 | import static org.junit.jupiter.api.Assertions.fail; 5 | 6 | import java.io.IOException; 7 | 8 | import org.junit.jupiter.api.DisplayName; 9 | import org.junit.jupiter.api.Test; 10 | import org.springframework.http.HttpStatus; 11 | 12 | import com.sap.cloudfoundry.client.facade.domain.CloudDomain; 13 | 14 | class DomainsCloudControllerClientIntegrationTest extends CloudControllerClientIntegrationTest { 15 | 16 | @Test 17 | @DisplayName("Add missing domain") 18 | void addDomain() throws IOException { 19 | String domainName = ITVariable.DOMAIN_NAME.getValue(); 20 | assertDomainExists(domainName, false); 21 | try { 22 | client.addDomain(domainName); 23 | assertDomainExists(domainName, true); 24 | } finally { 25 | client.deleteDomain(domainName); 26 | } 27 | } 28 | 29 | @Test 30 | @DisplayName("Add existing domain and verify addition does not fail") 31 | void addDomainAlreadyExists() throws IOException { 32 | String domainName = ITVariable.DOMAIN_NAME.getValue(); 33 | try { 34 | client.addDomain(domainName); 35 | assertDomainExists(domainName, true); 36 | client.addDomain(domainName); 37 | } finally { 38 | client.deleteDomain(domainName); 39 | } 40 | } 41 | 42 | @Test 43 | @DisplayName("Delete existing domain") 44 | void deleteDomain() throws IOException { 45 | String domainName = "delete-domain" + ITVariable.DOMAIN_NAME.getValue(); 46 | client.addDomain(domainName); 47 | assertDomainExists(domainName, true); 48 | client.deleteDomain(domainName); 49 | assertDomainExists(domainName, false); 50 | } 51 | 52 | @Test 53 | @DisplayName("Delete missing domain and verify deletion fails") 54 | void deleteDomainMissing() throws IOException { 55 | String domainName = "delete-domain-missing" + ITVariable.DOMAIN_NAME.getValue(); 56 | assertDomainExists(domainName, false); 57 | try { 58 | client.deleteDomain(domainName); 59 | fail(); 60 | } catch (CloudOperationException e) { 61 | assertEquals(HttpStatus.NOT_FOUND, e.getStatusCode()); 62 | } 63 | } 64 | 65 | private static void assertDomainExists(String domainName, boolean domainExists) { 66 | boolean actualDomainExists = client.getDomainsForOrganization() 67 | .stream() 68 | .map(CloudDomain::getName) 69 | .anyMatch(domainName::equals); 70 | assertEquals(domainExists, actualDomainExists); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/test/java/com/sap/cloudfoundry/client/facade/ITVariable.java: -------------------------------------------------------------------------------- 1 | package com.sap.cloudfoundry.client.facade; 2 | 3 | public enum ITVariable { 4 | 5 | // @formatter:off 6 | CF_API(Constants.CF_API_ENV, Constants.CF_API_PROPERTY), 7 | USER_EMAIL(Constants.USER_EMAIL_ENV, Constants.USER_EMAIL_PROPERTY), 8 | USER_PASSWORD(Constants.USER_PASSWORD_ENV, Constants.USER_PASSWORD_PROPERTY), 9 | USER_ORIGIN(Constants.USER_ORIGIN_ENV, Constants.USER_ORIGIN_PROPERTY, false), 10 | ORG(Constants.ORG_ENV, Constants.ORG_PROPERTY), 11 | SPACE(Constants.SPACE_ENV, Constants.SPACE_PROPERTY), 12 | DOMAIN_NAME(Constants.DOMAIN_NAME_ENV, Constants.DOMAIN_NAME_PROPERTY), 13 | PATH_TO_SERVICE_BROKER_APPLICATION(Constants.PATH_TO_SERVICE_BROKER_ENV, Constants.PATH_TO_SERVICE_BROKER_PROPERTY, false); 14 | // @formatter:on 15 | 16 | private final String envVariable; 17 | private final String property; 18 | private final boolean required; 19 | 20 | ITVariable(String envVariable, String property, boolean required) { 21 | this.envVariable = envVariable; 22 | this.property = property; 23 | this.required = required; 24 | } 25 | 26 | ITVariable(String envVariable, String property) { 27 | this.envVariable = envVariable; 28 | this.property = property; 29 | this.required = true; 30 | } 31 | 32 | public String getEnvVariable() { 33 | return envVariable; 34 | } 35 | 36 | public String getProperty() { 37 | return property; 38 | } 39 | 40 | public boolean isRequired() { 41 | return required; 42 | } 43 | 44 | public String getValue() { 45 | String envVariable = System.getenv(this.getEnvVariable()); 46 | String property = System.getProperty(this.getProperty()); 47 | return property != null ? property : envVariable; 48 | } 49 | 50 | private static class Constants { 51 | public static final String CF_API_ENV = "CF_API"; 52 | public static final String USER_EMAIL_ENV = "CF_USER_EMAIL"; 53 | public static final String USER_PASSWORD_ENV = "CF_USER_PASSWORD"; 54 | public static final String USER_ORIGIN_ENV = "CF_USER_ORIGIN"; 55 | public static final String ORG_ENV = "CF_ORG"; 56 | public static final String SPACE_ENV = "CF_SPACE"; 57 | public static final String DOMAIN_NAME_ENV = "DOMAIN_NAME"; 58 | public static final String PATH_TO_SERVICE_BROKER_ENV = "PATH_TO_SERVICE_BROKER"; 59 | 60 | public static final String CF_API_PROPERTY = "api"; 61 | public static final String USER_EMAIL_PROPERTY = "user.email"; 62 | public static final String USER_PASSWORD_PROPERTY = "user.password"; 63 | public static final String USER_ORIGIN_PROPERTY = "user.origin"; 64 | public static final String ORG_PROPERTY = "org"; 65 | public static final String SPACE_PROPERTY = "space"; 66 | public static final String DOMAIN_NAME_PROPERTY = "domain.name"; 67 | public static final String PATH_TO_SERVICE_BROKER_PROPERTY = "path.servicebroker"; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/test/java/com/sap/cloudfoundry/client/facade/adapters/RawInstancesInfoTest.java: -------------------------------------------------------------------------------- 1 | package com.sap.cloudfoundry.client.facade.adapters; 2 | 3 | import org.cloudfoundry.client.v3.applications.GetApplicationProcessStatisticsResponse; 4 | import org.cloudfoundry.client.v3.processes.ProcessState; 5 | import org.cloudfoundry.client.v3.processes.ProcessStatisticsResource; 6 | import org.junit.jupiter.api.Test; 7 | 8 | import com.sap.cloudfoundry.client.facade.domain.ImmutableInstanceInfo; 9 | import com.sap.cloudfoundry.client.facade.domain.ImmutableInstancesInfo; 10 | import com.sap.cloudfoundry.client.facade.domain.InstanceState; 11 | import com.sap.cloudfoundry.client.facade.domain.InstancesInfo; 12 | 13 | class RawInstancesInfoTest { 14 | 15 | @Test 16 | void testDerive() { 17 | RawCloudEntityTest.testDerive(buildExpectedInstancesInfo(), buildActualInstancesInfo()); 18 | } 19 | 20 | private InstancesInfo buildExpectedInstancesInfo() { 21 | return ImmutableInstancesInfo.builder() 22 | .addInstance(ImmutableInstanceInfo.builder() 23 | .index(0) 24 | .state(InstanceState.RUNNING) 25 | .build()) 26 | .build(); 27 | } 28 | 29 | private RawInstancesInfo buildActualInstancesInfo() { 30 | return ImmutableRawInstancesInfo.builder() 31 | .processStatisticsResponse(getApplicationProcessStatisticsResponse()) 32 | .build(); 33 | } 34 | 35 | private GetApplicationProcessStatisticsResponse getApplicationProcessStatisticsResponse() { 36 | return GetApplicationProcessStatisticsResponse.builder() 37 | .resource(ProcessStatisticsResource.builder() 38 | .index(0) 39 | .details("Instance is in running state") 40 | .diskQuota(1024L) 41 | .state(ProcessState.RUNNING) 42 | .memoryQuota(1024L) 43 | .type("web") 44 | .uptime(9042L) 45 | .fileDescriptorQuota(1024L) 46 | .host("10.244.16.10") 47 | .build()) 48 | .build(); 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /src/test/java/com/sap/cloudfoundry/client/facade/adapters/RawCloudServiceBrokerTest.java: -------------------------------------------------------------------------------- 1 | package com.sap.cloudfoundry.client.facade.adapters; 2 | 3 | import org.cloudfoundry.client.v3.Relationship; 4 | import org.cloudfoundry.client.v3.ToOneRelationship; 5 | import org.cloudfoundry.client.v3.servicebrokers.ServiceBrokerRelationships; 6 | import org.cloudfoundry.client.v3.servicebrokers.ServiceBrokerResource; 7 | import org.junit.jupiter.api.Test; 8 | 9 | import com.sap.cloudfoundry.client.facade.domain.CloudServiceBroker; 10 | import com.sap.cloudfoundry.client.facade.domain.ImmutableCloudServiceBroker; 11 | 12 | class RawCloudServiceBrokerTest { 13 | 14 | private static final String NAME = "auditlog-broker"; 15 | private static final String URL = "https://auditlog-broker.example.com"; 16 | private static final String SPACE_GUID_STRING = "ef93547f-74c3-4bad-ba69-b7dc4f212622"; 17 | 18 | @Test 19 | void testDerive() { 20 | RawCloudEntityTest.testDerive(buildExpectedServiceBroker(), buildRawServiceBroker()); 21 | } 22 | 23 | private static CloudServiceBroker buildExpectedServiceBroker() { 24 | return ImmutableCloudServiceBroker.builder() 25 | .metadata(RawCloudEntityTest.EXPECTED_METADATA_PARSED_FROM_V3_RESOURCE) 26 | .name(NAME) 27 | .spaceGuid(SPACE_GUID_STRING) 28 | .url(URL) 29 | .build(); 30 | } 31 | 32 | private static RawCloudServiceBroker buildRawServiceBroker() { 33 | return ImmutableRawCloudServiceBroker.builder() 34 | .serviceBroker(buildTestServiceBroker()) 35 | .build(); 36 | } 37 | 38 | private static ServiceBrokerResource buildTestServiceBroker() { 39 | return ServiceBrokerResource.builder() 40 | .id(RawCloudEntityTest.GUID_STRING) 41 | .createdAt(RawCloudEntityTest.CREATED_AT_STRING) 42 | .updatedAt(RawCloudEntityTest.UPDATED_AT_STRING) 43 | .name(NAME) 44 | .url(URL) 45 | .relationships(ServiceBrokerRelationships.builder() 46 | .space(ToOneRelationship.builder() 47 | .data(Relationship.builder() 48 | .id(SPACE_GUID_STRING) 49 | .build()) 50 | .build()) 51 | .build()) 52 | .build(); 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/com/sap/cloudfoundry/client/facade/adapters/RawCloudEntity.java: -------------------------------------------------------------------------------- 1 | package com.sap.cloudfoundry.client.facade.adapters; 2 | 3 | import java.text.MessageFormat; 4 | import java.time.LocalDateTime; 5 | import java.time.ZonedDateTime; 6 | import java.time.format.DateTimeFormatter; 7 | import java.time.format.DateTimeParseException; 8 | import java.util.Collection; 9 | import java.util.UUID; 10 | import java.util.stream.Collectors; 11 | 12 | import org.cloudfoundry.client.v3.Resource; 13 | import org.slf4j.Logger; 14 | import org.slf4j.LoggerFactory; 15 | 16 | import com.sap.cloudfoundry.client.facade.domain.CloudMetadata; 17 | import com.sap.cloudfoundry.client.facade.domain.Derivable; 18 | import com.sap.cloudfoundry.client.facade.domain.ImmutableCloudMetadata; 19 | 20 | public abstract class RawCloudEntity implements Derivable { 21 | 22 | private static final Logger LOGGER = LoggerFactory.getLogger(RawCloudEntity.class); 23 | 24 | protected RawCloudEntity() { 25 | // Recommended by Sonar. 26 | } 27 | 28 | public static CloudMetadata parseResourceMetadata(Resource resource) { 29 | return ImmutableCloudMetadata.builder() 30 | .guid(parseNullableGuid(resource.getId())) 31 | .createdAt(parseNullableDate(resource.getCreatedAt())) 32 | .updatedAt(parseNullableDate(resource.getUpdatedAt())) 33 | .build(); 34 | } 35 | 36 | protected static UUID parseNullableGuid(String guid) { 37 | return guid == null ? null : parseGuid(guid); 38 | } 39 | 40 | protected static UUID parseGuid(String guid) { 41 | try { 42 | return UUID.fromString(guid); 43 | } catch (IllegalArgumentException e) { 44 | LOGGER.warn(MessageFormat.format("Could not parse GUID string: \"{0}\"", guid), e); 45 | return null; 46 | } 47 | } 48 | 49 | protected static LocalDateTime parseNullableDate(String date) { 50 | return date == null ? null : parseDate(date); 51 | } 52 | 53 | protected static LocalDateTime parseDate(String dateString) { 54 | try { 55 | return ZonedDateTime.parse(dateString, DateTimeFormatter.ISO_DATE_TIME) 56 | .toLocalDateTime(); 57 | } catch (DateTimeParseException e) { 58 | LOGGER.warn(MessageFormat.format("Could not parse date string: \"{0}\"", dateString), e); 59 | return null; 60 | } 61 | } 62 | 63 | protected static > E parseEnum(Enum value, Class targetEnum) { 64 | String name = value.name() 65 | .toUpperCase(); 66 | return Enum.valueOf(targetEnum, name); 67 | } 68 | 69 | protected static D deriveFromNullable(Derivable derivable) { 70 | return derivable == null ? null : derivable.derive(); 71 | } 72 | 73 | protected static Collection derive(Collection> derivables) { 74 | return derivables.stream() 75 | .map(Derivable::derive) 76 | .collect(Collectors.toList()); 77 | } 78 | 79 | } 80 | -------------------------------------------------------------------------------- /src/main/java/com/sap/cloudfoundry/client/facade/adapters/RawCloudBuild.java: -------------------------------------------------------------------------------- 1 | package com.sap.cloudfoundry.client.facade.adapters; 2 | 3 | import java.util.Optional; 4 | 5 | import org.cloudfoundry.client.v3.Relationship; 6 | import org.cloudfoundry.client.v3.builds.Build; 7 | import org.cloudfoundry.client.v3.builds.BuildState; 8 | import org.cloudfoundry.client.v3.builds.CreatedBy; 9 | import org.cloudfoundry.client.v3.builds.Droplet; 10 | import org.immutables.value.Value; 11 | 12 | import com.sap.cloudfoundry.client.facade.domain.CloudBuild; 13 | import com.sap.cloudfoundry.client.facade.domain.DropletInfo; 14 | import com.sap.cloudfoundry.client.facade.domain.ImmutableCloudBuild; 15 | import com.sap.cloudfoundry.client.facade.domain.ImmutableCloudBuild.ImmutableCreatedBy; 16 | import com.sap.cloudfoundry.client.facade.domain.ImmutableCloudBuild.ImmutablePackageInfo; 17 | import com.sap.cloudfoundry.client.facade.domain.ImmutableDropletInfo; 18 | 19 | @Value.Immutable 20 | public abstract class RawCloudBuild extends RawCloudEntity { 21 | 22 | private static CloudBuild.CreatedBy parseCreatedBy(Build buildResource) { 23 | CreatedBy createdBy = buildResource.getCreatedBy(); 24 | return ImmutableCreatedBy.builder() 25 | .guid(parseNullableGuid(createdBy.getId())) 26 | .name(createdBy.getName()) 27 | .build(); 28 | } 29 | 30 | private static CloudBuild.PackageInfo parsePackageInfo(Build buildResource) { 31 | Relationship packageRelationship = buildResource.getInputPackage(); 32 | String packageId = packageRelationship.getId(); 33 | return ImmutablePackageInfo.of(parseNullableGuid(packageId)); 34 | } 35 | 36 | private static DropletInfo parseDropletInfo(Build buildResource) { 37 | Droplet droplet = buildResource.getDroplet(); 38 | return Optional.ofNullable(droplet) 39 | .map(Droplet::getId) 40 | .map(RawCloudEntity::parseNullableGuid) 41 | .map(dropletGuid -> ImmutableDropletInfo.builder() 42 | .guid(dropletGuid) 43 | .build()) 44 | .orElse(null); 45 | } 46 | 47 | private static CloudBuild.State parseState(BuildState state) { 48 | return parseEnum(state, CloudBuild.State.class); 49 | } 50 | 51 | @Value.Parameter 52 | public abstract Build getResource(); 53 | 54 | @Override 55 | public CloudBuild derive() { 56 | Build resource = getResource(); 57 | return ImmutableCloudBuild.builder() 58 | .metadata(parseResourceMetadata(resource)) 59 | .createdBy(parseCreatedBy(resource)) 60 | .packageInfo(parsePackageInfo(resource)) 61 | .dropletInfo(parseDropletInfo(resource)) 62 | .state(parseState(resource.getState())) 63 | .error(resource.getError()) 64 | .build(); 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/com/sap/cloudfoundry/client/facade/adapters/RawCloudPackage.java: -------------------------------------------------------------------------------- 1 | package com.sap.cloudfoundry.client.facade.adapters; 2 | 3 | import org.cloudfoundry.client.v3.Checksum; 4 | import org.cloudfoundry.client.v3.packages.BitsData; 5 | import org.cloudfoundry.client.v3.packages.DockerData; 6 | import org.cloudfoundry.client.v3.packages.Package; 7 | import org.cloudfoundry.client.v3.packages.PackageType; 8 | import org.immutables.value.Value; 9 | 10 | import com.sap.cloudfoundry.client.facade.domain.CloudPackage; 11 | import com.sap.cloudfoundry.client.facade.domain.ImmutableBitsData; 12 | import com.sap.cloudfoundry.client.facade.domain.ImmutableCloudPackage; 13 | import com.sap.cloudfoundry.client.facade.domain.ImmutableDockerData; 14 | import com.sap.cloudfoundry.client.facade.domain.Status; 15 | 16 | @Value.Immutable 17 | public abstract class RawCloudPackage extends RawCloudEntity { 18 | 19 | @Value.Parameter 20 | public abstract Package getResource(); 21 | 22 | @Override 23 | public CloudPackage derive() { 24 | Package resource = getResource(); 25 | return ImmutableCloudPackage.builder() 26 | .metadata(parseResourceMetadata(resource)) 27 | .status(parseStatus(resource)) 28 | .data(parseData(resource)) 29 | .type(parseType(resource)) 30 | .build(); 31 | } 32 | 33 | private static Status parseStatus(Package resource) { 34 | return parseEnum(resource.getState(), Status.class); 35 | } 36 | 37 | private static CloudPackage.PackageData parseData(Package resource) { 38 | if (resource.getType() == PackageType.BITS) { 39 | return parseBitsData((BitsData) resource.getData()); 40 | } 41 | return parseDockerData((DockerData) resource.getData()); 42 | } 43 | 44 | private static CloudPackage.PackageData parseBitsData(BitsData data) { 45 | return ImmutableBitsData.builder() 46 | .checksum(parseBitsChecksum(data.getChecksum())) 47 | .error(data.getError()) 48 | .build(); 49 | } 50 | 51 | private static com.sap.cloudfoundry.client.facade.domain.BitsData.Checksum parseBitsChecksum(Checksum checksum) { 52 | if (checksum == null) { 53 | return null; 54 | } 55 | return ImmutableBitsData.ImmutableChecksum.builder() 56 | .algorithm(checksum.getType() 57 | .toString()) 58 | .value(checksum.getValue()) 59 | .build(); 60 | } 61 | 62 | private static CloudPackage.PackageData parseDockerData(DockerData data) { 63 | return ImmutableDockerData.builder() 64 | .image(data.getImage()) 65 | .username(data.getUsername()) 66 | .password(data.getPassword()) 67 | .build(); 68 | } 69 | 70 | private static CloudPackage.Type parseType(Package resource) { 71 | return parseEnum(resource.getType(), CloudPackage.Type.class); 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /src/main/java/com/sap/cloudfoundry/client/facade/adapters/RawCloudApplication.java: -------------------------------------------------------------------------------- 1 | package com.sap.cloudfoundry.client.facade.adapters; 2 | 3 | import java.util.Collections; 4 | import java.util.Map; 5 | 6 | import com.sap.cloudfoundry.client.facade.domain.CloudApplication; 7 | import com.sap.cloudfoundry.client.facade.domain.CloudSpace; 8 | import com.sap.cloudfoundry.client.facade.domain.Derivable; 9 | import com.sap.cloudfoundry.client.facade.domain.ImmutableCloudApplication; 10 | import com.sap.cloudfoundry.client.facade.domain.ImmutableLifecycle; 11 | import com.sap.cloudfoundry.client.facade.domain.Lifecycle; 12 | import com.sap.cloudfoundry.client.facade.domain.LifecycleType; 13 | import org.cloudfoundry.client.v3.BuildpackData; 14 | import org.cloudfoundry.client.v3.CnbData; 15 | import org.cloudfoundry.client.v3.LifecycleData; 16 | import org.cloudfoundry.client.v3.applications.Application; 17 | import org.cloudfoundry.client.v3.applications.ApplicationState; 18 | import org.immutables.value.Value; 19 | 20 | @Value.Immutable 21 | public abstract class RawCloudApplication extends RawCloudEntity { 22 | 23 | public static final String BUILDPACKS = "buildpacks"; 24 | public static final String STACK = "stack"; 25 | 26 | public abstract Application getApplication(); 27 | 28 | public abstract Derivable getSpace(); 29 | 30 | @Override 31 | public CloudApplication derive() { 32 | Application app = getApplication(); 33 | return ImmutableCloudApplication.builder() 34 | .metadata(parseResourceMetadata(app)) 35 | .v3Metadata(app.getMetadata()) 36 | .name(app.getName()) 37 | .state(parseState(app.getState())) 38 | .lifecycle(parseLifecycle(app.getLifecycle())) 39 | .space(getSpace().derive()) 40 | .build(); 41 | } 42 | 43 | private static CloudApplication.State parseState(ApplicationState state) { 44 | return CloudApplication.State.valueOf(state.getValue()); 45 | } 46 | 47 | private static Lifecycle parseLifecycle(org.cloudfoundry.client.v3.Lifecycle lifecycle) { 48 | Map data = extractLifecycleData(lifecycle.getData()); 49 | 50 | return ImmutableLifecycle.builder() 51 | .type(LifecycleType.valueOf(lifecycle.getType() 52 | .toString() 53 | .toUpperCase())) 54 | .data(data) 55 | .build(); 56 | } 57 | 58 | private static Map extractLifecycleData(LifecycleData lifecycleData) { 59 | if (lifecycleData instanceof BuildpackData buildpackData) { 60 | return Map.of( 61 | BUILDPACKS, buildpackData.getBuildpacks(), 62 | STACK, buildpackData.getStack()); 63 | } else if (lifecycleData instanceof CnbData cnbData) { 64 | return Map.of( 65 | BUILDPACKS, cnbData.getBuildpacks(), 66 | STACK, cnbData.getStack()); 67 | } else { 68 | return Collections.emptyMap(); 69 | } 70 | } 71 | 72 | } 73 | -------------------------------------------------------------------------------- /src/test/java/com/sap/cloudfoundry/client/facade/adapters/RawCloudTaskTest.java: -------------------------------------------------------------------------------- 1 | package com.sap.cloudfoundry.client.facade.adapters; 2 | 3 | import org.cloudfoundry.client.v3.tasks.Result; 4 | import org.cloudfoundry.client.v3.tasks.Task; 5 | import org.cloudfoundry.client.v3.tasks.TaskResource; 6 | import org.cloudfoundry.client.v3.tasks.TaskState; 7 | import org.junit.jupiter.api.Test; 8 | 9 | import com.sap.cloudfoundry.client.facade.domain.CloudTask; 10 | import com.sap.cloudfoundry.client.facade.domain.ImmutableCloudTask; 11 | 12 | class RawCloudTaskTest { 13 | 14 | private static final String NAME = "echo"; 15 | private static final String COMMAND = "echo \"Hello!\""; 16 | private static final int DISK_IN_MB = 128; 17 | private static final int MEMORY_IN_MB = 256; 18 | private static final String FAILURE_REASON = "blabla"; 19 | private static final String DROPLET_ID = "8ff24465-823b-4a55-85ee-a680f2c743cd"; 20 | private static final int SEQUENCE_ID = 3; 21 | private static final TaskState STATE = TaskState.FAILED; 22 | 23 | private static final CloudTask.State EXPECTED_STATE = CloudTask.State.FAILED; 24 | 25 | @Test 26 | void testDerive() { 27 | RawCloudEntityTest.testDerive(buildExpectedTask(), buildRawTask()); 28 | } 29 | 30 | private static CloudTask buildExpectedTask() { 31 | return ImmutableCloudTask.builder() 32 | .metadata(RawCloudEntityTest.EXPECTED_METADATA_PARSED_FROM_V3_RESOURCE) 33 | .name(NAME) 34 | .command(COMMAND) 35 | .limits(ImmutableCloudTask.ImmutableLimits.builder() 36 | .disk(DISK_IN_MB) 37 | .memory(MEMORY_IN_MB) 38 | .build()) 39 | .result(ImmutableCloudTask.ImmutableResult.builder() 40 | .failureReason(FAILURE_REASON) 41 | .build()) 42 | .state(EXPECTED_STATE) 43 | .build(); 44 | } 45 | 46 | private static RawCloudTask buildRawTask() { 47 | return ImmutableRawCloudTask.of(buildTestResource()); 48 | } 49 | 50 | private static Task buildTestResource() { 51 | return TaskResource.builder() 52 | .id(RawCloudEntityTest.GUID_STRING) 53 | .createdAt(RawCloudEntityTest.CREATED_AT_STRING) 54 | .updatedAt(RawCloudEntityTest.UPDATED_AT_STRING) 55 | .name(NAME) 56 | .command(COMMAND) 57 | .diskInMb(DISK_IN_MB) 58 | .memoryInMb(MEMORY_IN_MB) 59 | .state(STATE) 60 | .result(buildTestResult()) 61 | .dropletId(DROPLET_ID) 62 | .sequenceId(SEQUENCE_ID) 63 | .build(); 64 | } 65 | 66 | private static Result buildTestResult() { 67 | return Result.builder() 68 | .failureReason(FAILURE_REASON) 69 | .build(); 70 | } 71 | 72 | } 73 | -------------------------------------------------------------------------------- /src/main/java/com/sap/cloudfoundry/client/facade/domain/ServiceOperation.java: -------------------------------------------------------------------------------- 1 | package com.sap.cloudfoundry.client.facade.domain; 2 | 3 | import java.text.MessageFormat; 4 | import java.util.Objects; 5 | 6 | import org.cloudfoundry.client.v3.LastOperation; 7 | 8 | public class ServiceOperation { 9 | 10 | public enum Type { 11 | 12 | CREATE, UPDATE, DELETE; 13 | 14 | @Override 15 | public String toString() { 16 | return name().toLowerCase(); 17 | } 18 | 19 | public static Type fromString(String value) { 20 | for (Type type : Type.values()) { 21 | if (type.toString() 22 | .equals(value)) { 23 | return type; 24 | } 25 | } 26 | throw new IllegalArgumentException(MessageFormat.format("Illegal service operation type: {0}", value)); 27 | } 28 | 29 | } 30 | 31 | public enum State { 32 | 33 | SUCCEEDED("succeeded"), FAILED("failed"), IN_PROGRESS("in progress"), INITIAL("initial"); 34 | 35 | private final String name; 36 | 37 | State(String name) { 38 | this.name = name; 39 | } 40 | 41 | @Override 42 | public String toString() { 43 | return name; 44 | } 45 | 46 | public static State fromString(String value) { 47 | for (State state : State.values()) { 48 | if (state.toString() 49 | .equals(value)) { 50 | return state; 51 | } 52 | } 53 | throw new IllegalArgumentException(MessageFormat.format("Illegal service operation state: {0}", value)); 54 | } 55 | 56 | } 57 | 58 | private Type type; 59 | private String description; 60 | private State state; 61 | 62 | ServiceOperation() { 63 | // Required by Jackson. 64 | } 65 | 66 | public ServiceOperation(Type type, String description, State state) { 67 | this.type = type; 68 | this.description = description; 69 | this.state = state; 70 | } 71 | 72 | public Type getType() { 73 | return type; 74 | } 75 | 76 | public String getDescription() { 77 | return description; 78 | } 79 | 80 | public State getState() { 81 | return state; 82 | } 83 | 84 | public static ServiceOperation fromLastOperation(LastOperation lastOperation) { 85 | if (lastOperation == null || lastOperation.getType() == null || lastOperation.getState() == null) { 86 | return null; 87 | } 88 | Type type = Type.fromString(lastOperation.getType()); 89 | State state = State.fromString(lastOperation.getState()); 90 | String description = lastOperation.getDescription(); 91 | return new ServiceOperation(type, description, state); 92 | } 93 | 94 | @Override 95 | public boolean equals(Object o) { 96 | if (this == o) 97 | return true; 98 | if (o == null || getClass() != o.getClass()) 99 | return false; 100 | ServiceOperation that = (ServiceOperation) o; 101 | return type == that.type && state == that.state; 102 | } 103 | 104 | @Override 105 | public int hashCode() { 106 | return Objects.hash(type, state); 107 | } 108 | 109 | @Override 110 | public String toString() { 111 | return String.format("%s %s", type, state); 112 | } 113 | 114 | } 115 | -------------------------------------------------------------------------------- /src/main/java/com/sap/cloudfoundry/client/facade/rest/CloudControllerResponseErrorHandler.java: -------------------------------------------------------------------------------- 1 | package com.sap.cloudfoundry.client.facade.rest; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import com.sap.cloudfoundry.client.facade.CloudOperationException; 5 | import com.sap.cloudfoundry.client.facade.util.CloudUtil; 6 | import org.springframework.http.HttpStatus; 7 | import org.springframework.http.client.ClientHttpResponse; 8 | import org.springframework.web.client.DefaultResponseErrorHandler; 9 | import org.springframework.web.client.RestClientException; 10 | 11 | import java.io.IOException; 12 | import java.util.List; 13 | import java.util.Map; 14 | import java.util.stream.Collectors; 15 | 16 | public class CloudControllerResponseErrorHandler extends DefaultResponseErrorHandler { 17 | 18 | private static CloudOperationException getException(ClientHttpResponse response) throws IOException { 19 | HttpStatus statusCode = HttpStatus.valueOf(response.getStatusCode() 20 | .value()); 21 | String statusText = response.getStatusText(); 22 | 23 | ObjectMapper mapper = new ObjectMapper(); // can reuse, share globally 24 | 25 | if (response.getBody() != null) { 26 | try { 27 | @SuppressWarnings("unchecked") Map responseBody = mapper.readValue(response.getBody(), Map.class); 28 | String description = getTrimmedDescription(responseBody); 29 | return new CloudOperationException(statusCode, statusText, description); 30 | } catch (IOException e) { 31 | // Fall through. Handled below. 32 | } 33 | } 34 | return new CloudOperationException(statusCode, statusText); 35 | } 36 | 37 | private static String getTrimmedDescription(Map responseBody) { 38 | String description = getDescription(responseBody); 39 | return description == null ? null : description.trim(); 40 | } 41 | 42 | private static String getDescription(Map responseBody) { 43 | String description = getV2Description(responseBody); 44 | return description == null ? getV3Description(responseBody) : description; 45 | } 46 | 47 | private static String getV2Description(Map responseBody) { 48 | return CloudUtil.parse(String.class, responseBody.get("description")); 49 | } 50 | 51 | @SuppressWarnings("unchecked") 52 | private static String getV3Description(Map responseBody) { 53 | List> errors = (List>) responseBody.get("errors"); 54 | return errors == null ? null : concatenateErrorMessages(errors); 55 | } 56 | 57 | private static String concatenateErrorMessages(List> errors) { 58 | return errors.stream() 59 | .map(error -> (String) error.get("detail")) 60 | .collect(Collectors.joining("\n")); 61 | } 62 | 63 | @Override 64 | public void handleError(ClientHttpResponse response) throws IOException { 65 | HttpStatus statusCode = HttpStatus.valueOf(response.getStatusCode() 66 | .value()); 67 | switch (statusCode.series()) { 68 | case CLIENT_ERROR: 69 | case SERVER_ERROR: 70 | throw getException(response); 71 | default: 72 | throw new RestClientException("Unknown status code [" + statusCode + "]"); 73 | } 74 | } 75 | 76 | } 77 | -------------------------------------------------------------------------------- /src/test/java/com/sap/cloudfoundry/client/facade/adapters/RawCloudAsyncJobTest.java: -------------------------------------------------------------------------------- 1 | package com.sap.cloudfoundry.client.facade.adapters; 2 | 3 | import java.util.List; 4 | 5 | import org.cloudfoundry.client.v3.Error; 6 | import org.cloudfoundry.client.v3.jobs.GetJobResponse; 7 | import org.cloudfoundry.client.v3.jobs.Job; 8 | import org.cloudfoundry.client.v3.jobs.JobState; 9 | import org.cloudfoundry.client.v3.jobs.Warning; 10 | import org.junit.jupiter.api.Test; 11 | 12 | import com.sap.cloudfoundry.client.facade.domain.CloudAsyncJob; 13 | import com.sap.cloudfoundry.client.facade.domain.ImmutableCloudAsyncJob; 14 | 15 | class RawCloudAsyncJobTest { 16 | 17 | private static final String OPERATION = "service_broker.delete"; 18 | private static final String WARNING_1 = "warning1"; 19 | private static final String WARNING_2 = "warning2"; 20 | private static final String WARNINGS = "warning1,warning2"; 21 | private static final int ERROR_CODE_1 = 10008; 22 | private static final int ERROR_CODE_2 = 1000; 23 | private static final String ERROR_TITLE_1 = "CF-UnprocessableEntity"; 24 | private static final String ERROR_TITLE_2 = "CF-InvalidAuthToken"; 25 | private static final String ERROR_DETAIL_1 = "something went wrong"; 26 | private static final String ERROR_DETAIL_2 = "not valid token"; 27 | private static final String ERRORS = "10008 CF-UnprocessableEntity something went wrong,1000 CF-InvalidAuthToken not valid token"; 28 | 29 | @Test 30 | void testDerive() { 31 | RawCloudEntityTest.testDerive(buildExpectedTask(), buildRawTask()); 32 | } 33 | 34 | private CloudAsyncJob buildExpectedTask() { 35 | return ImmutableCloudAsyncJob.builder() 36 | .metadata(RawCloudEntityTest.EXPECTED_METADATA_PARSED_FROM_V3_RESOURCE) 37 | .state(JobState.FAILED) 38 | .operation(OPERATION) 39 | .warnings(WARNINGS) 40 | .errors(ERRORS) 41 | .build(); 42 | } 43 | 44 | private RawCloudAsyncJob buildRawTask() { 45 | return ImmutableRawCloudAsyncJob.of(buildTestResource()); 46 | } 47 | 48 | private Job buildTestResource() { 49 | return GetJobResponse.builder() 50 | .id(RawCloudEntityTest.GUID_STRING) 51 | .createdAt(RawCloudEntityTest.CREATED_AT_STRING) 52 | .updatedAt(RawCloudEntityTest.UPDATED_AT_STRING) 53 | .state(JobState.FAILED) 54 | .operation(OPERATION) 55 | .warnings(buildWarnings()) 56 | .errors(buildErrors()) 57 | .build(); 58 | } 59 | 60 | private List buildWarnings() { 61 | return List.of(Warning.builder() 62 | .detail(WARNING_1) 63 | .build(), 64 | Warning.builder() 65 | .detail(WARNING_2) 66 | .build()); 67 | } 68 | 69 | private List buildErrors() { 70 | return List.of(Error.builder() 71 | .code(ERROR_CODE_1) 72 | .title(ERROR_TITLE_1) 73 | .detail(ERROR_DETAIL_1) 74 | .build(), 75 | Error.builder() 76 | .code(ERROR_CODE_2) 77 | .title(ERROR_TITLE_2) 78 | .detail(ERROR_DETAIL_2) 79 | .build()); 80 | } 81 | 82 | } 83 | -------------------------------------------------------------------------------- /src/main/java/com/sap/cloudfoundry/client/facade/rest/CloudControllerRestClientFactory.java: -------------------------------------------------------------------------------- 1 | package com.sap.cloudfoundry.client.facade.rest; 2 | 3 | import com.sap.cloudfoundry.client.facade.CloudCredentials; 4 | import com.sap.cloudfoundry.client.facade.adapters.CloudFoundryClientFactory; 5 | import com.sap.cloudfoundry.client.facade.adapters.ImmutableCloudFoundryClientFactory; 6 | import com.sap.cloudfoundry.client.facade.domain.CloudSpace; 7 | import com.sap.cloudfoundry.client.facade.oauth2.OAuthClient; 8 | import com.sap.cloudfoundry.client.facade.util.RestUtil; 9 | import org.cloudfoundry.client.CloudFoundryClient; 10 | import org.cloudfoundry.reactor.ConnectionContext; 11 | import org.immutables.value.Value; 12 | import org.springframework.util.StringUtils; 13 | 14 | import java.net.URL; 15 | import java.time.Duration; 16 | import java.util.Collections; 17 | import java.util.Map; 18 | import java.util.Optional; 19 | 20 | @Value.Immutable 21 | public abstract class CloudControllerRestClientFactory { 22 | private final RestUtil restUtil = new RestUtil(); 23 | 24 | public abstract Optional getSslHandshakeTimeout(); 25 | 26 | public abstract Optional getConnectTimeout(); 27 | 28 | public abstract Optional getConnectionPoolSize(); 29 | 30 | public abstract Optional getThreadPoolSize(); 31 | 32 | public abstract Optional getResponseTimeout(); 33 | 34 | @Value.Default 35 | public boolean shouldTrustSelfSignedCertificates() { 36 | return false; 37 | } 38 | 39 | @Value.Derived 40 | public CloudFoundryClientFactory getCloudFoundryClientFactory() { 41 | ImmutableCloudFoundryClientFactory.Builder builder = ImmutableCloudFoundryClientFactory.builder(); 42 | getSslHandshakeTimeout().ifPresent(builder::sslHandshakeTimeout); 43 | getConnectTimeout().ifPresent(builder::connectTimeout); 44 | getConnectionPoolSize().ifPresent(builder::connectionPoolSize); 45 | getThreadPoolSize().ifPresent(builder::threadPoolSize); 46 | getResponseTimeout().ifPresent(builder::responseTimeout); 47 | return builder.build(); 48 | } 49 | 50 | public CloudControllerRestClient createClient(URL controllerUrl, CloudCredentials credentials, String organizationName, 51 | String spaceName, OAuthClient oAuthClient, Map requestTags) { 52 | oAuthClient.init(credentials); 53 | CloudSpaceClient spaceGetter = getCloudFoundryClientFactory().createSpaceClient(controllerUrl, oAuthClient, requestTags); 54 | CloudSpace target = spaceGetter.getSpace(organizationName, spaceName); 55 | return createClient(controllerUrl, credentials, target, oAuthClient, requestTags); 56 | } 57 | 58 | public CloudControllerRestClient createClient(URL controllerUrl, CloudCredentials credentials, CloudSpace target) { 59 | return createClient(controllerUrl, credentials, target, createOAuthClient(controllerUrl, credentials.getOrigin()), 60 | Collections.emptyMap()); 61 | } 62 | 63 | public CloudControllerRestClient createClient(URL controllerUrl, CloudCredentials credentials, CloudSpace target, 64 | OAuthClient oAuthClient, Map requestTags) { 65 | oAuthClient.init(credentials); 66 | CloudFoundryClient delegate = getCloudFoundryClientFactory().createClient(controllerUrl, oAuthClient, requestTags); 67 | return new CloudControllerRestClientImpl(delegate, target); 68 | } 69 | 70 | private OAuthClient createOAuthClient(URL controllerUrl, String origin) { 71 | return restUtil.createOAuthClientByControllerUrl(controllerUrl, shouldTrustSelfSignedCertificates()); 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /src/main/java/com/sap/cloudfoundry/client/facade/oauth2/TokenFactory.java: -------------------------------------------------------------------------------- 1 | package com.sap.cloudfoundry.client.facade.oauth2; 2 | 3 | import java.nio.charset.StandardCharsets; 4 | import java.text.MessageFormat; 5 | import java.time.Instant; 6 | import java.util.Base64; 7 | import java.util.Base64.Decoder; 8 | import java.util.Collections; 9 | import java.util.HashSet; 10 | import java.util.List; 11 | import java.util.Map; 12 | 13 | import org.springframework.http.HttpStatus; 14 | import org.springframework.security.oauth2.core.OAuth2AccessToken; 15 | import org.springframework.web.server.ResponseStatusException; 16 | 17 | import com.sap.cloudfoundry.client.facade.util.JsonUtil; 18 | 19 | public class TokenFactory { 20 | 21 | private static final int JWT_TOKEN_PARTS_COUNT = 3; 22 | 23 | // Scopes: 24 | public static final String SCOPE_CC_READ = "cloud_controller.read"; 25 | public static final String SCOPE_CC_WRITE = "cloud_controller.write"; 26 | public static final String SCOPE_CC_ADMIN = "cloud_controller.admin"; 27 | 28 | // Token Body elements: 29 | public static final String SCOPE = "scope"; 30 | public static final String EXPIRES_AT_KEY = "exp"; 31 | public static final String ISSUED_AT_KEY = "iat"; 32 | public static final String USER_NAME = "user_name"; 33 | public static final String USER_ID = "user_id"; 34 | public static final String CLIENT_ID = "client_id"; 35 | 36 | public OAuth2AccessTokenWithAdditionalInfo createToken(String tokenString) { 37 | Map tokenInfo = parseToken(tokenString); 38 | return createToken(tokenString, tokenInfo); 39 | } 40 | 41 | @SuppressWarnings("unchecked") 42 | public OAuth2AccessTokenWithAdditionalInfo createToken(String tokenString, Map tokenInfo) { 43 | List scope = (List) tokenInfo.get(SCOPE); 44 | Number expiresAt = (Number) tokenInfo.get(EXPIRES_AT_KEY); 45 | Number instantiatedAt = (Number) tokenInfo.get(ISSUED_AT_KEY); 46 | if (scope == null || expiresAt == null || instantiatedAt == null) { 47 | throw new IllegalStateException(MessageFormat.format("One or more of the following elements are missing from the token: \"{0}\"", 48 | List.of(SCOPE, EXPIRES_AT_KEY, ISSUED_AT_KEY))); 49 | } 50 | return new OAuth2AccessTokenWithAdditionalInfo(createOAuth2AccessToken(tokenString, scope, expiresAt, instantiatedAt), tokenInfo); 51 | } 52 | 53 | private OAuth2AccessToken createOAuth2AccessToken(String tokenString, List scope, Number expiresAt, Number instantiatedAt) { 54 | try { 55 | return new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, 56 | tokenString, 57 | Instant.ofEpochSecond(instantiatedAt.longValue()), 58 | Instant.ofEpochSecond(expiresAt.longValue()), 59 | new HashSet<>(scope)); 60 | } catch (IllegalArgumentException e) { 61 | throw new ResponseStatusException(HttpStatus.UNAUTHORIZED, e.getMessage(), e); 62 | } 63 | } 64 | 65 | private Map parseToken(String tokenString) { 66 | String[] headerBodySignature = tokenString.split("\\."); 67 | if (headerBodySignature.length != JWT_TOKEN_PARTS_COUNT) { 68 | return Collections.emptyMap(); 69 | } 70 | String body = decode(headerBodySignature[1]); 71 | return JsonUtil.convertJsonToMap(body); 72 | } 73 | 74 | private String decode(String string) { 75 | Decoder decoder = Base64.getUrlDecoder(); 76 | return new String(decoder.decode(string), StandardCharsets.UTF_8); 77 | } 78 | 79 | public OAuth2AccessTokenWithAdditionalInfo createToken(Oauth2AccessTokenResponse oauth2AccessTokenResponse) { 80 | return createToken(oauth2AccessTokenResponse.getAccessToken()); 81 | } 82 | 83 | } 84 | -------------------------------------------------------------------------------- /src/main/java/com/sap/cloudfoundry/client/facade/util/CloudUtil.java: -------------------------------------------------------------------------------- 1 | package com.sap.cloudfoundry.client.facade.util; 2 | 3 | import java.text.MessageFormat; 4 | import java.text.ParseException; 5 | import java.text.SimpleDateFormat; 6 | import java.time.Duration; 7 | import java.util.Date; 8 | import java.util.Locale; 9 | import java.util.function.Supplier; 10 | 11 | import com.sap.cloudfoundry.client.facade.Messages; 12 | 13 | import org.slf4j.Logger; 14 | import org.slf4j.LoggerFactory; 15 | 16 | /** 17 | * Some helper utilities used by the Cloud Foundry Java client. 18 | * 19 | */ 20 | public class CloudUtil { 21 | 22 | private static final Double DEFAULT_DOUBLE = 0.0; 23 | private static final Integer DEFAULT_INTEGER = 0; 24 | private static final Long DEFAULT_LONG = 0L; 25 | private static final int RETRIES = 3; 26 | private static final Duration RETRY_INTERVAL = Duration.ofSeconds(3); 27 | private static final Logger LOGGER = LoggerFactory.getLogger(CloudUtil.class); 28 | 29 | private CloudUtil() { 30 | } 31 | 32 | @SuppressWarnings("unchecked") 33 | public static T parse(Class clazz, Object object) { 34 | T defaultValue = null; 35 | try { 36 | if (clazz == Date.class) { 37 | String stringValue = parse(String.class, object); 38 | return clazz.cast(new SimpleDateFormat("EEE MMM d HH:mm:ss Z yyyy", Locale.US).parse(stringValue)); 39 | } 40 | 41 | if (clazz == Integer.class) { 42 | defaultValue = (T) DEFAULT_INTEGER; 43 | } else if (clazz == Long.class) { 44 | defaultValue = (T) DEFAULT_LONG; 45 | } else if (clazz == Double.class) { 46 | defaultValue = (T) DEFAULT_DOUBLE; 47 | } 48 | 49 | if (object == null) { 50 | return defaultValue; 51 | } 52 | 53 | // special handling for int and long since smaller numbers become ints 54 | // but may be requested as long and vice versa 55 | if (clazz == Integer.class) { 56 | if (object instanceof Number) { 57 | return clazz.cast(((Number) object).intValue()); 58 | } else if (object instanceof String) { 59 | return clazz.cast(Integer.valueOf(((String) object))); 60 | } 61 | } 62 | if (clazz == Long.class) { 63 | if (object instanceof Number) { 64 | return clazz.cast(((Number) object).longValue()); 65 | } else if (object instanceof String) { 66 | return clazz.cast(Long.valueOf(((String) object))); 67 | } 68 | } 69 | if (clazz == Double.class) { 70 | if (object instanceof Number) { 71 | return clazz.cast(((Number) object).doubleValue()); 72 | } else if (object instanceof String) { 73 | return clazz.cast(Double.valueOf(((String) object))); 74 | } 75 | } 76 | 77 | return clazz.cast(object); 78 | } catch (ClassCastException | ParseException e) { 79 | // ignore 80 | } 81 | return defaultValue; 82 | } 83 | 84 | public static T executeWithRetry(Supplier operation) { 85 | for (int i = 1; i < RETRIES; i++) { 86 | try { 87 | return operation.get(); 88 | } catch (Exception e) { 89 | LOGGER.warn(MessageFormat.format(Messages.RETRYING_OPERATION, e.getMessage()), e); 90 | sleep(RETRY_INTERVAL); 91 | } 92 | } 93 | return operation.get(); 94 | } 95 | 96 | public static void sleep(Duration duration) { 97 | try { 98 | Thread.sleep(duration.toMillis()); 99 | } catch (InterruptedException e) { 100 | Thread.currentThread().interrupt(); 101 | throw new IllegalStateException("Interrupted!", e); 102 | } 103 | } 104 | 105 | } 106 | -------------------------------------------------------------------------------- /src/test/java/com/sap/cloudfoundry/client/facade/adapters/RawCloudServiceBindingTest.java: -------------------------------------------------------------------------------- 1 | package com.sap.cloudfoundry.client.facade.adapters; 2 | 3 | import java.time.LocalDateTime; 4 | import java.util.UUID; 5 | 6 | import org.cloudfoundry.client.v3.LastOperation; 7 | import org.cloudfoundry.client.v3.servicebindings.ServiceBindingRelationships; 8 | import org.cloudfoundry.client.v3.servicebindings.ServiceBindingResource; 9 | import org.cloudfoundry.client.v3.servicebindings.ServiceBindingType; 10 | import org.junit.jupiter.api.Test; 11 | 12 | import com.sap.cloudfoundry.client.facade.domain.CloudServiceBinding; 13 | import com.sap.cloudfoundry.client.facade.domain.ImmutableCloudServiceBinding; 14 | import com.sap.cloudfoundry.client.facade.domain.ServiceCredentialBindingOperation; 15 | 16 | class RawCloudServiceBindingTest { 17 | 18 | private static final String APPLICATION_GUID_STRING = "3725650a-8725-4401-a949-c68f83d54a86"; 19 | private static final String SERVICE_INSTANCE_GUID_STRING = "3725650a-8725-4401-a949-c68f83d54a86"; 20 | private static final UUID APPLICATION_GUID = UUID.fromString(APPLICATION_GUID_STRING); 21 | private static final UUID SERVICE_INSTANCE_GUID = UUID.fromString(SERVICE_INSTANCE_GUID_STRING); 22 | private static final LastOperation LAST_OPERATION = buildLastOperation(); 23 | 24 | @Test 25 | void testDerive() { 26 | RawCloudEntityTest.testDerive(buildExpectedServiceBinding(), buildRawServiceBinding()); 27 | } 28 | 29 | private static CloudServiceBinding buildExpectedServiceBinding() { 30 | return ImmutableCloudServiceBinding.builder() 31 | .metadata(RawCloudEntityTest.EXPECTED_METADATA_V3) 32 | .applicationGuid(APPLICATION_GUID) 33 | .serviceInstanceGuid(SERVICE_INSTANCE_GUID) 34 | .serviceBindingOperation(ServiceCredentialBindingOperation.from(LAST_OPERATION)) 35 | .build(); 36 | } 37 | 38 | private static RawCloudServiceBinding buildRawServiceBinding() { 39 | return ImmutableRawCloudServiceBinding.builder() 40 | .serviceBinding(buildTestResource()) 41 | 42 | .build(); 43 | } 44 | 45 | private static ServiceBindingResource buildTestResource() { 46 | return ServiceBindingResource.builder() 47 | .id(RawCloudEntityTest.GUID_STRING) 48 | .createdAt(RawCloudEntityTest.CREATED_AT_STRING) 49 | .updatedAt(RawCloudEntityTest.UPDATED_AT_STRING) 50 | .type(ServiceBindingType.APPLICATION) 51 | .lastOperation(LAST_OPERATION) 52 | .relationships(ServiceBindingRelationships.builder() 53 | .serviceInstance(RawCloudEntityTest.buildToOneRelationship(SERVICE_INSTANCE_GUID_STRING)) 54 | .application(RawCloudEntityTest.buildToOneRelationship(APPLICATION_GUID_STRING)) 55 | .build()) 56 | .build(); 57 | } 58 | 59 | private static LastOperation buildLastOperation() { 60 | return LastOperation.builder() 61 | .state(ServiceCredentialBindingOperation.State.SUCCEEDED.toString()) 62 | .type(ServiceCredentialBindingOperation.Type.CREATE.toString()) 63 | .createdAt(LocalDateTime.now() 64 | .toString()) 65 | .updatedAt(LocalDateTime.now() 66 | .toString()) 67 | .description("Service binding created") 68 | .build(); 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/com/sap/cloudfoundry/client/facade/domain/ServiceCredentialBindingOperation.java: -------------------------------------------------------------------------------- 1 | package com.sap.cloudfoundry.client.facade.domain; 2 | 3 | import java.text.MessageFormat; 4 | import java.time.LocalDateTime; 5 | import java.time.format.DateTimeFormatter; 6 | import java.util.Arrays; 7 | 8 | import org.cloudfoundry.client.v3.LastOperation; 9 | import org.immutables.value.Value; 10 | 11 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 12 | import com.fasterxml.jackson.databind.annotation.JsonSerialize; 13 | import com.sap.cloudfoundry.client.facade.Nullable; 14 | 15 | @Value.Immutable 16 | @JsonSerialize(as = ImmutableServiceCredentialBindingOperation.class) 17 | @JsonDeserialize(as = ImmutableServiceCredentialBindingOperation.class) 18 | public abstract class ServiceCredentialBindingOperation { 19 | 20 | public abstract Type getType(); 21 | 22 | public abstract State getState(); 23 | 24 | @Nullable 25 | public abstract String getDescription(); 26 | 27 | @Nullable 28 | public abstract LocalDateTime getCreatedAt(); 29 | 30 | @Nullable 31 | public abstract LocalDateTime getUpdatedAt(); 32 | 33 | public static ServiceCredentialBindingOperation from(LastOperation lastOperation) { 34 | String lastOperationType = lastOperation.getType(); 35 | String lastOperationState = lastOperation.getState(); 36 | String lastOperationDescription = lastOperation.getDescription(); 37 | String lastOperationCreatedAt = lastOperation.getCreatedAt(); 38 | String lastOperationUpdatedAt = lastOperation.getUpdatedAt(); 39 | return ImmutableServiceCredentialBindingOperation.builder() 40 | .type(ServiceCredentialBindingOperation.Type.fromString(lastOperationType)) 41 | .state(ServiceCredentialBindingOperation.State.fromString(lastOperationState)) 42 | .description(lastOperationDescription) 43 | .createdAt(LocalDateTime.parse(lastOperationCreatedAt, 44 | DateTimeFormatter.ISO_DATE_TIME)) 45 | .updatedAt(LocalDateTime.parse(lastOperationUpdatedAt, 46 | DateTimeFormatter.ISO_DATE_TIME)) 47 | .build(); 48 | } 49 | 50 | public enum Type { 51 | CREATE, DELETE; 52 | 53 | public static Type fromString(String value) { 54 | return Arrays.stream(values()) 55 | .filter(type -> type.toString() 56 | .equals(value)) 57 | .findFirst() 58 | .orElseThrow(() -> new IllegalArgumentException(MessageFormat.format("Illegal service binding operation type: \"{0}\"", 59 | value))); 60 | } 61 | 62 | @Override 63 | public String toString() { 64 | return name().toLowerCase(); 65 | } 66 | } 67 | 68 | public enum State { 69 | INITIAL("initial"), IN_PROGRESS("in progress"), SUCCEEDED("succeeded"), FAILED("failed"); 70 | 71 | private final String name; 72 | 73 | State(String name) { 74 | this.name = name; 75 | } 76 | 77 | @Override 78 | public String toString() { 79 | return name; 80 | } 81 | 82 | public static State fromString(String value) { 83 | return Arrays.stream(values()) 84 | .filter(state -> state.toString() 85 | .equals(value)) 86 | .findFirst() 87 | .orElseThrow(() -> new IllegalArgumentException(MessageFormat.format("Illegal service binding state: \"{0}\"", 88 | value))); 89 | } 90 | } 91 | 92 | } 93 | -------------------------------------------------------------------------------- /src/test/java/com/sap/cloudfoundry/client/facade/util/JsonUtilTest.java: -------------------------------------------------------------------------------- 1 | package com.sap.cloudfoundry.client.facade.util; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertEquals; 4 | import static org.junit.jupiter.api.Assertions.assertNull; 5 | import static org.junit.jupiter.api.Assertions.assertTrue; 6 | 7 | import java.util.Arrays; 8 | import java.util.Collections; 9 | import java.util.List; 10 | import java.util.Map; 11 | import java.util.TreeMap; 12 | 13 | import org.junit.jupiter.api.Test; 14 | 15 | class JsonUtilTest { 16 | 17 | @Test 18 | void testUsingPrettyPrint() { 19 | testWithCustomPrinting(); 20 | } 21 | 22 | @Test 23 | void testWithNoPrettyPrint() { 24 | testWithCustomPrinting(); 25 | } 26 | 27 | private void testWithCustomPrinting() { 28 | Map fooMap = createTestProperties(); 29 | 30 | String json = JsonUtil.convertToJson(fooMap, true); 31 | validateJsonMap(json); 32 | } 33 | 34 | @Test 35 | void testUsingTheMethodWithNoPrettyPrint() { 36 | Map fooMap = createTestProperties(); 37 | 38 | String json = JsonUtil.convertToJson(fooMap); 39 | 40 | validateJsonMap(json); 41 | } 42 | 43 | private void validateJsonMap(String json) { 44 | System.out.println(json); 45 | 46 | Map resultFoo = JsonUtil.convertJsonToMap(json); 47 | 48 | assertTestProperties(resultFoo); 49 | } 50 | 51 | @Test 52 | void testConvertListToJsonAndBack() { 53 | List testingList = Arrays.asList("foo", "bar", "baz"); 54 | String jsonList = JsonUtil.convertToJson(testingList); 55 | 56 | System.out.println(jsonList); 57 | 58 | List toList = JsonUtil.convertJsonToList(jsonList); 59 | 60 | assertEquals(3, toList.size()); 61 | assertEquals(Arrays.asList("foo", "bar", "baz"), toList); 62 | } 63 | 64 | @Test 65 | void testWithNullMap() { 66 | 67 | Map foo = Collections.emptyMap(); 68 | String fooJson = JsonUtil.convertToJson(foo); 69 | String barJson = "null"; 70 | 71 | Map fooMap = JsonUtil.convertJsonToMap(fooJson); 72 | Map barMap = JsonUtil.convertJsonToMap(barJson); 73 | 74 | assertTrue(fooMap.isEmpty()); 75 | assertNull(barMap); 76 | } 77 | 78 | private Map createTestProperties() { 79 | Map testProperties1 = new TreeMap<>(); 80 | testProperties1.put("host", "localhost"); 81 | testProperties1.put("port", 30030); 82 | testProperties1.put("long-value", (long) Integer.MAX_VALUE * 10); 83 | testProperties1.put("double-value", 1.5); 84 | Map testProperties2 = new TreeMap<>(); 85 | testProperties2.put("port", 50000); 86 | testProperties2.put("long-value", (long) Integer.MAX_VALUE * 10); 87 | testProperties2.put("double-value", 1.5); 88 | testProperties1.put("test", testProperties2); 89 | testProperties1.put("list", Arrays.asList("foo", "bar", "baz")); 90 | return testProperties1; 91 | } 92 | 93 | @SuppressWarnings("unchecked") 94 | private void assertTestProperties(Map actualProperties) { 95 | assertTrue(actualProperties.get("host") instanceof String); 96 | assertTrue(actualProperties.get("port") instanceof Integer); 97 | assertTrue(actualProperties.get("long-value") instanceof Long); 98 | assertTrue(actualProperties.get("double-value") instanceof Double); 99 | assertTrue(((Map) actualProperties.get("test")).get("port") instanceof Integer); 100 | assertTrue(((Map) actualProperties.get("test")).get("long-value") instanceof Long); 101 | assertTrue(((Map) actualProperties.get("test")).get("double-value") instanceof Double); 102 | List listFromMap = (List) actualProperties.get("list"); 103 | assertEquals(3, listFromMap.size()); 104 | assertEquals(Arrays.asList("foo", "bar", "baz"), listFromMap); 105 | } 106 | 107 | } 108 | -------------------------------------------------------------------------------- /src/main/java/com/sap/cloudfoundry/client/facade/util/JsonUtil.java: -------------------------------------------------------------------------------- 1 | package com.sap.cloudfoundry.client.facade.util; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | import java.nio.charset.StandardCharsets; 6 | import java.util.ArrayList; 7 | import java.util.HashMap; 8 | import java.util.List; 9 | import java.util.Map; 10 | 11 | import org.apache.commons.logging.Log; 12 | import org.apache.commons.logging.LogFactory; 13 | import org.springframework.http.MediaType; 14 | 15 | import com.fasterxml.jackson.core.JsonParseException; 16 | import com.fasterxml.jackson.core.type.TypeReference; 17 | import com.fasterxml.jackson.databind.JsonMappingException; 18 | import com.fasterxml.jackson.databind.ObjectMapper; 19 | import com.fasterxml.jackson.databind.ObjectWriter; 20 | 21 | /** 22 | * Some JSON helper utilities used by the Cloud Foundry Java client. 23 | * 24 | */ 25 | public class JsonUtil { 26 | 27 | public static final MediaType JSON_MEDIA_TYPE = new MediaType(MediaType.APPLICATION_JSON.getType(), 28 | MediaType.APPLICATION_JSON.getSubtype(), 29 | StandardCharsets.UTF_8); 30 | 31 | protected static final Log logger = LogFactory.getLog(JsonUtil.class); 32 | 33 | private static final ObjectMapper mapper = new ObjectMapper(); 34 | 35 | public static List convertJsonToList(String json) { 36 | List retList = new ArrayList<>(); 37 | if (json != null) { 38 | try { 39 | retList = mapper.readValue(json, new TypeReference>() { 40 | }); 41 | } catch (IOException e) { 42 | logger.warn("Error while reading Java List from JSON response: " + json, e); 43 | } 44 | } 45 | return retList; 46 | } 47 | 48 | public static Map convertJsonToMap(String json) { 49 | Map retMap = new HashMap<>(); 50 | if (json != null) { 51 | try { 52 | retMap = mapper.readValue(json, new TypeReference>() { 53 | }); 54 | } catch (IOException e) { 55 | logger.warn("Error while reading Java Map from JSON response: " + json, e); 56 | } 57 | } 58 | return retMap; 59 | } 60 | 61 | public static String convertToJson(Object value) { 62 | return convertToJson(value, false); 63 | } 64 | 65 | public static String convertToJson(Object value, boolean shouldPrettyPrint) { 66 | if (shouldPrettyPrint) { 67 | return convertToJsonStringUsingWriter(mapper.writerWithDefaultPrettyPrinter(), value); 68 | } 69 | 70 | return convertToJsonStringUsingWriter(mapper.writer(), value); 71 | } 72 | 73 | private static String convertToJsonStringUsingWriter(ObjectWriter writer, Object value) { 74 | if (value == null || writer.canSerialize(value.getClass())) { 75 | try { 76 | return writer.writeValueAsString(value); 77 | } catch (IOException e) { 78 | logger.warn("Error while serializing " + value + " to JSON", e); 79 | return null; 80 | } 81 | 82 | } else { 83 | throw new IllegalArgumentException("Value of type " + value.getClass() 84 | .getName() 85 | + " can not be serialized to JSON."); 86 | } 87 | } 88 | 89 | @SuppressWarnings("unchecked") 90 | public static List> convertToJsonList(InputStream jsonInputStream) { 91 | try { 92 | return mapper.readValue(jsonInputStream, List.class); 93 | } catch (JsonParseException | JsonMappingException e) { 94 | logger.error("Unable to parse JSON from InputStream", e); 95 | throw new IllegalArgumentException("Unable to parse JSON from InputStream", e); 96 | } catch (IOException e) { 97 | logger.error("Unable to process InputStream", e); 98 | throw new IllegalArgumentException("Unable to parse JSON from InputStream", e); 99 | } 100 | } 101 | 102 | } 103 | -------------------------------------------------------------------------------- /src/main/java/com/sap/cloudfoundry/client/facade/util/RestUtil.java: -------------------------------------------------------------------------------- 1 | package com.sap.cloudfoundry.client.facade.util; 2 | 3 | import java.net.MalformedURLException; 4 | import java.net.URL; 5 | import java.security.cert.X509Certificate; 6 | import java.text.MessageFormat; 7 | 8 | import javax.net.ssl.SSLException; 9 | import javax.net.ssl.X509TrustManager; 10 | 11 | import org.springframework.http.client.reactive.ClientHttpConnector; 12 | import org.springframework.http.client.reactive.ReactorClientHttpConnector; 13 | import org.springframework.web.reactive.function.client.ExchangeStrategies; 14 | import org.springframework.web.reactive.function.client.WebClient; 15 | 16 | import com.sap.cloudfoundry.client.facade.oauth2.OAuthClient; 17 | 18 | import io.netty.handler.ssl.SslContext; 19 | import io.netty.handler.ssl.SslContextBuilder; 20 | import reactor.netty.http.client.HttpClient; 21 | 22 | /** 23 | * Some helper utilities for creating classes used for the REST support. 24 | * 25 | */ 26 | public class RestUtil { 27 | 28 | private static final int MAX_IN_MEMORY_SIZE = 1024 * 1024; // 1MB 29 | 30 | public OAuthClient createOAuthClientByControllerUrl(URL controllerUrl, boolean shouldTrustSelfSignedCertificates) { 31 | WebClient webClient = createWebClient(shouldTrustSelfSignedCertificates); 32 | URL authorizationUrl = getAuthorizationUrl(controllerUrl, webClient); 33 | return new OAuthClient(authorizationUrl, webClient); 34 | } 35 | 36 | private URL getAuthorizationUrl(URL controllerUrl, WebClient webClient) { 37 | AuthorizationEndpointGetter authorizationEndpointGetter = new AuthorizationEndpointGetter(webClient); 38 | return getAuthorizationUrl(authorizationEndpointGetter.getAuthorizationEndpoint(controllerUrl.toString())); 39 | } 40 | 41 | private URL getAuthorizationUrl(String authorizationEndpoint) { 42 | try { 43 | return new URL(authorizationEndpoint); 44 | } catch (MalformedURLException e) { 45 | throw new IllegalArgumentException(MessageFormat.format("Error creating authorization endpoint URL for endpoint {0}.", 46 | authorizationEndpoint), 47 | e); 48 | } 49 | } 50 | 51 | public WebClient createWebClient(boolean trustSelfSignedCerts) { 52 | return WebClient.builder() 53 | .exchangeStrategies(ExchangeStrategies.builder() 54 | .codecs(configurer -> configurer.defaultCodecs() 55 | .maxInMemorySize(MAX_IN_MEMORY_SIZE)) 56 | .build()) 57 | .clientConnector(buildClientConnector(trustSelfSignedCerts)) 58 | .build(); 59 | } 60 | 61 | private ClientHttpConnector buildClientConnector(boolean trustSelfSignedCerts) { 62 | HttpClient httpClient = HttpClient.create() 63 | .followRedirect(true); 64 | if (trustSelfSignedCerts) { 65 | httpClient = httpClient.secure(sslContextSpec -> sslContextSpec.sslContext(buildSslContext())); 66 | } else { 67 | httpClient = httpClient.secure(); 68 | } 69 | return new ReactorClientHttpConnector(httpClient); 70 | } 71 | 72 | private SslContext buildSslContext() { 73 | try { 74 | return SslContextBuilder.forClient() 75 | .trustManager(createDummyTrustManager()) 76 | .build(); 77 | } catch (SSLException e) { 78 | throw new RuntimeException("An error occurred setting up the SSLContext", e); 79 | } 80 | } 81 | 82 | private X509TrustManager createDummyTrustManager() { 83 | return new X509TrustManager() { 84 | 85 | @Override 86 | public void checkClientTrusted(X509Certificate[] xcs, String string) { 87 | // NOSONAR 88 | } 89 | 90 | @Override 91 | public void checkServerTrusted(X509Certificate[] xcs, String string) { 92 | // NOSONAR 93 | } 94 | 95 | @Override 96 | public X509Certificate[] getAcceptedIssuers() { 97 | return new X509Certificate[] {}; 98 | } 99 | 100 | }; 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /src/test/java/com/sap/cloudfoundry/client/facade/ApplicationUtil.java: -------------------------------------------------------------------------------- 1 | package com.sap.cloudfoundry.client.facade; 2 | 3 | import static org.junit.jupiter.api.Assertions.fail; 4 | 5 | import java.nio.file.Path; 6 | import java.text.MessageFormat; 7 | import java.time.Duration; 8 | import java.util.List; 9 | import java.util.UUID; 10 | import java.util.concurrent.TimeUnit; 11 | 12 | import com.sap.cloudfoundry.client.facade.domain.CloudApplication; 13 | import com.sap.cloudfoundry.client.facade.domain.CloudBuild; 14 | import com.sap.cloudfoundry.client.facade.domain.CloudPackage; 15 | import com.sap.cloudfoundry.client.facade.domain.InstanceInfo; 16 | import com.sap.cloudfoundry.client.facade.domain.InstanceState; 17 | import com.sap.cloudfoundry.client.facade.domain.Status; 18 | 19 | public class ApplicationUtil { 20 | 21 | public static final Duration UPLOAD_TIMEOUT = Duration.ofMinutes(15); 22 | 23 | public static CloudPackage uploadApplication(CloudControllerClient client, String applicationName, Path pathToFile) 24 | throws InterruptedException { 25 | CloudPackage cloudPackage = client.asyncUploadApplicationWithExponentialBackoff(applicationName, pathToFile, 26 | UploadStatusCallback.NONE, UPLOAD_TIMEOUT); 27 | while (cloudPackage.getStatus() != Status.READY && !hasUploadFailed(cloudPackage.getStatus())) { 28 | Thread.sleep(TimeUnit.SECONDS.toMillis(1)); 29 | cloudPackage = client.getPackage(cloudPackage.getGuid()); 30 | } 31 | if (hasUploadFailed(cloudPackage.getStatus())) { 32 | fail(MessageFormat.format("Cloud package is in invalid state: \"{0}\"", cloudPackage.getStatus())); 33 | } 34 | return cloudPackage; 35 | } 36 | 37 | private static boolean hasUploadFailed(Status status) { 38 | return status == Status.EXPIRED || status == Status.FAILED; 39 | } 40 | 41 | public static void stageApplication(CloudControllerClient client, String applicationName, CloudPackage cloudPackage) 42 | throws InterruptedException { 43 | UUID applicationGuid = client.getApplicationGuid(applicationName); 44 | CloudBuild build = createBuildForPackage(client, cloudPackage); 45 | client.bindDropletToApp(build.getDropletInfo() 46 | .getGuid(), 47 | applicationGuid); 48 | } 49 | 50 | public static CloudBuild createBuildForPackage(CloudControllerClient client, CloudPackage cloudPackage) throws InterruptedException { 51 | CloudBuild build = client.createBuild(cloudPackage.getGuid()); 52 | while (build.getState() == CloudBuild.State.STAGING) { 53 | Thread.sleep(TimeUnit.SECONDS.toMillis(1)); 54 | build = client.getBuild(build.getGuid()); 55 | } 56 | if (build.getState() == CloudBuild.State.FAILED) { 57 | fail(MessageFormat.format("Cloud build is in invalid state: \"{0}\"", build.getState())); 58 | } 59 | return build; 60 | } 61 | 62 | public static void startApplication(CloudControllerClient client, String applicationName) throws InterruptedException { 63 | client.startApplication(applicationName); 64 | 65 | CloudApplication app = client.getApplication(applicationName); 66 | List appInstances = client.getApplicationInstances(app) 67 | .getInstances(); 68 | while (!hasAppInstancesStarted(appInstances) && !hasAppInstancesFailed(appInstances)) { 69 | Thread.sleep(TimeUnit.SECONDS.toMillis(1)); 70 | appInstances = client.getApplicationInstances(app) 71 | .getInstances(); 72 | } 73 | 74 | if (hasAppInstancesFailed(appInstances)) { 75 | fail(MessageFormat.format("Some or all application instances of application \"{0}\" failed to start", applicationName)); 76 | } 77 | } 78 | 79 | private static boolean hasAppInstancesStarted(List appInstances) { 80 | return appInstances.stream() 81 | .allMatch(appInstance -> appInstance.getState() 82 | .equals(InstanceState.RUNNING)); 83 | } 84 | 85 | private static boolean hasAppInstancesFailed(List appInstances) { 86 | return appInstances.stream() 87 | .anyMatch(appInstance -> hasAppInstanceStartFailed(appInstance.getState())); 88 | } 89 | 90 | private static boolean hasAppInstanceStartFailed(InstanceState instanceState) { 91 | return instanceState == InstanceState.DOWN || instanceState == InstanceState.CRASHED; 92 | } 93 | 94 | } 95 | -------------------------------------------------------------------------------- /src/test/java/com/sap/cloudfoundry/client/facade/adapters/RawCloudServiceKeyTest.java: -------------------------------------------------------------------------------- 1 | package com.sap.cloudfoundry.client.facade.adapters; 2 | 3 | import java.time.LocalDateTime; 4 | import java.util.HashMap; 5 | import java.util.Map; 6 | 7 | import org.cloudfoundry.client.v3.LastOperation; 8 | import org.cloudfoundry.client.v3.servicebindings.ServiceBindingRelationships; 9 | import org.cloudfoundry.client.v3.servicebindings.ServiceBindingResource; 10 | import org.cloudfoundry.client.v3.servicebindings.ServiceBindingType; 11 | import org.junit.jupiter.api.Test; 12 | 13 | import com.sap.cloudfoundry.client.facade.domain.CloudServiceInstance; 14 | import com.sap.cloudfoundry.client.facade.domain.CloudServiceKey; 15 | import com.sap.cloudfoundry.client.facade.domain.ImmutableCloudServiceInstance; 16 | import com.sap.cloudfoundry.client.facade.domain.ImmutableCloudServiceKey; 17 | import com.sap.cloudfoundry.client.facade.domain.ServiceCredentialBindingOperation; 18 | 19 | class RawCloudServiceKeyTest { 20 | 21 | private static final String SERVICE_NAME = "foo"; 22 | private static final String NAME = "bar"; 23 | private static final Map CREDENTIALS = buildTestCredentials(); 24 | private static final CloudServiceInstance SERVICE_INSTANCE = ImmutableCloudServiceInstance.builder() 25 | .name(SERVICE_NAME) 26 | .build(); 27 | private static final LastOperation LAST_OPERATION = buildLastOperation(); 28 | 29 | @Test 30 | void testDerive() { 31 | RawCloudEntityTest.testDerive(buildExpectedServiceKey(), buildRawServiceKey()); 32 | } 33 | 34 | private static CloudServiceKey buildExpectedServiceKey() { 35 | return ImmutableCloudServiceKey.builder() 36 | .metadata(RawCloudEntityTest.EXPECTED_METADATA_V3) 37 | .v3Metadata(RawCloudEntityTest.V3_METADATA) 38 | .name(NAME) 39 | .credentials(CREDENTIALS) 40 | .serviceInstance(SERVICE_INSTANCE) 41 | .serviceKeyOperation(ServiceCredentialBindingOperation.from(LAST_OPERATION)) 42 | .build(); 43 | } 44 | 45 | private static RawCloudServiceKey buildRawServiceKey() { 46 | return ImmutableRawCloudServiceKey.builder() 47 | .serviceBindingResource(buildTestResource()) 48 | .serviceInstance(SERVICE_INSTANCE) 49 | .credentials(CREDENTIALS) 50 | .build(); 51 | } 52 | 53 | private static ServiceBindingResource buildTestResource() { 54 | return ServiceBindingResource.builder() 55 | .id(RawCloudEntityTest.GUID_STRING) 56 | .createdAt(RawCloudEntityTest.CREATED_AT_STRING) 57 | .updatedAt(RawCloudEntityTest.UPDATED_AT_STRING) 58 | .metadata(RawCloudEntityTest.V3_METADATA) 59 | .name(NAME) 60 | .type(ServiceBindingType.KEY) 61 | .lastOperation(LAST_OPERATION) 62 | .relationships(ServiceBindingRelationships.builder() 63 | .serviceInstance(RawCloudEntityTest.buildToOneRelationship("")) 64 | .build()) 65 | .build(); 66 | } 67 | 68 | private static Map buildTestCredentials() { 69 | Map parameters = new HashMap<>(); 70 | parameters.put("foo", "bar"); 71 | parameters.put("baz", false); 72 | parameters.put("qux", 3.141); 73 | return parameters; 74 | } 75 | 76 | private static LastOperation buildLastOperation() { 77 | return LastOperation.builder() 78 | .state(ServiceCredentialBindingOperation.State.SUCCEEDED.toString()) 79 | .type(ServiceCredentialBindingOperation.Type.CREATE.toString()) 80 | .createdAt(LocalDateTime.now() 81 | .toString()) 82 | .updatedAt(LocalDateTime.now() 83 | .toString()) 84 | .description("Service key created") 85 | .build(); 86 | } 87 | 88 | } 89 | -------------------------------------------------------------------------------- /src/test/java/com/sap/cloudfoundry/client/facade/CloudControllerClientIntegrationTest.java: -------------------------------------------------------------------------------- 1 | package com.sap.cloudfoundry.client.facade; 2 | 3 | import java.net.MalformedURLException; 4 | import java.net.URI; 5 | import java.net.URL; 6 | import java.util.Collections; 7 | import java.util.HashMap; 8 | import java.util.Map; 9 | 10 | import com.sap.cloudfoundry.client.facade.adapters.ImmutableCloudFoundryClientFactory; 11 | import com.sap.cloudfoundry.client.facade.domain.CloudSpace; 12 | import com.sap.cloudfoundry.client.facade.domain.ImmutableLifecycle; 13 | import com.sap.cloudfoundry.client.facade.domain.Lifecycle; 14 | import com.sap.cloudfoundry.client.facade.domain.LifecycleType; 15 | import com.sap.cloudfoundry.client.facade.domain.Staging; 16 | import com.sap.cloudfoundry.client.facade.rest.CloudSpaceClient; 17 | import com.sap.cloudfoundry.client.facade.util.RestUtil; 18 | import org.cloudfoundry.client.CloudFoundryClient; 19 | import org.junit.jupiter.api.AfterEach; 20 | import org.junit.jupiter.api.BeforeAll; 21 | import org.junit.jupiter.api.BeforeEach; 22 | import org.junit.jupiter.api.TestInfo; 23 | 24 | import static org.junit.jupiter.api.Assertions.assertNotNull; 25 | 26 | abstract class CloudControllerClientIntegrationTest { 27 | 28 | private static final String DEFAULT_CLIENT_ID = "cf"; 29 | private static final String DEFAULT_CLIENT_SECRET = ""; 30 | protected static final String DEFAULT_STACK = "cflinuxfs4"; 31 | 32 | protected static CloudControllerClient client; 33 | protected static CloudFoundryClient delegate; 34 | protected static CloudSpace target; 35 | 36 | @BeforeAll 37 | static void login() throws MalformedURLException { 38 | assertAllRequiredVariablesAreDefined(); 39 | CloudCredentials credentials = getCloudCredentials(); 40 | URL apiUrl = URI.create(ITVariable.CF_API.getValue()) 41 | .toURL(); 42 | var clientFactory = ImmutableCloudFoundryClientFactory.builder() 43 | .build(); 44 | var oauthClient = new RestUtil().createOAuthClientByControllerUrl(apiUrl, true); 45 | oauthClient.init(credentials); 46 | CloudSpaceClient spaceClient = clientFactory.createSpaceClient(apiUrl, oauthClient, Collections.emptyMap()); 47 | target = spaceClient.getSpace(ITVariable.ORG.getValue(), ITVariable.SPACE.getValue()); 48 | client = new CloudControllerClientImpl(apiUrl, credentials, target, true); 49 | delegate = clientFactory.createClient(apiUrl, oauthClient, Collections.emptyMap()); 50 | } 51 | 52 | @BeforeEach 53 | public void setUp(TestInfo testInfo) { 54 | System.out.println("================================"); 55 | System.out.printf("Test started: %s%n", testInfo.getDisplayName()); 56 | } 57 | 58 | @AfterEach 59 | public void tearDown(TestInfo testInfo) { 60 | System.out.printf("Test finished: %s%n", testInfo.getDisplayName()); 61 | } 62 | 63 | protected Lifecycle createLifecycle(Staging staging) { 64 | if (staging.getDockerInfo() != null) { 65 | return ImmutableLifecycle.builder() 66 | .type(LifecycleType.DOCKER) 67 | .data(Map.of()) 68 | .build(); 69 | } 70 | 71 | var data = new HashMap(); 72 | data.put("buildpacks", staging.getBuildpacks()); 73 | data.put("stack", DEFAULT_STACK); 74 | 75 | // Default to BUILDPACK if lifecycleType is null 76 | LifecycleType lifecycleType = staging.getLifecycleType() != null 77 | ? staging.getLifecycleType() 78 | : LifecycleType.BUILDPACK; 79 | 80 | return ImmutableLifecycle.builder() 81 | .type(lifecycleType) 82 | .data(data) 83 | .build(); 84 | } 85 | 86 | private static void assertAllRequiredVariablesAreDefined() { 87 | for (ITVariable itVariable : ITVariable.values()) { 88 | if (!itVariable.isRequired()) { 89 | continue; 90 | } 91 | assertNotNull(itVariable.getValue(), String.format("Missing required value defined by env var %s or system property %s", 92 | itVariable.getEnvVariable(), itVariable.getProperty())); 93 | } 94 | } 95 | 96 | private static CloudCredentials getCloudCredentials() { 97 | if (ITVariable.USER_ORIGIN.getValue() == null) { 98 | return new CloudCredentials(ITVariable.USER_EMAIL.getValue(), ITVariable.USER_PASSWORD.getValue()); 99 | } 100 | return new CloudCredentials(ITVariable.USER_EMAIL.getValue(), 101 | ITVariable.USER_PASSWORD.getValue(), 102 | DEFAULT_CLIENT_ID, 103 | DEFAULT_CLIENT_SECRET, 104 | ITVariable.USER_ORIGIN.getValue()); 105 | } 106 | 107 | } 108 | -------------------------------------------------------------------------------- /src/main/java/com/sap/cloudfoundry/client/facade/adapters/RawCloudRoute.java: -------------------------------------------------------------------------------- 1 | package com.sap.cloudfoundry.client.facade.adapters; 2 | 3 | import java.util.List; 4 | import java.util.Objects; 5 | import java.util.UUID; 6 | import java.util.stream.Collectors; 7 | 8 | import org.cloudfoundry.client.v3.routes.Route; 9 | import org.immutables.value.Value; 10 | 11 | import com.sap.cloudfoundry.client.facade.Nullable; 12 | import com.sap.cloudfoundry.client.facade.domain.CloudRoute; 13 | import com.sap.cloudfoundry.client.facade.domain.ImmutableCloudDomain; 14 | import com.sap.cloudfoundry.client.facade.domain.ImmutableCloudMetadata; 15 | import com.sap.cloudfoundry.client.facade.domain.ImmutableCloudRoute; 16 | import com.sap.cloudfoundry.client.facade.domain.ImmutableRouteDestination; 17 | import com.sap.cloudfoundry.client.facade.domain.RouteDestination; 18 | 19 | @Value.Immutable 20 | public abstract class RawCloudRoute extends RawCloudEntity { 21 | 22 | @Value.Parameter 23 | public abstract Route getRoute(); 24 | 25 | @Nullable 26 | public abstract UUID getApplicationGuid(); 27 | 28 | @Override 29 | public CloudRoute derive() { 30 | Route route = getRoute(); 31 | List destinations = mapDestinations(); 32 | String domainGuid = route.getRelationships() 33 | .getDomain() 34 | .getData() 35 | .getId(); 36 | return ImmutableCloudRoute.builder() 37 | .metadata(parseResourceMetadata(route)) 38 | .appsUsingRoute(route.getDestinations() 39 | .size()) 40 | .host(route.getHost()) 41 | .port(route.getPort()) 42 | .domain(ImmutableCloudDomain.builder() 43 | .name(computeDomain(route)) 44 | .metadata(ImmutableCloudMetadata.of(UUID.fromString(domainGuid))) 45 | .build()) 46 | .path(route.getPath()) 47 | .url(route.getUrl()) 48 | .destinations(destinations) 49 | .requestedProtocol(computeRequestedProtocol(destinations)) 50 | .build(); 51 | } 52 | 53 | private static String computeDomain(Route route) { 54 | String domain = route.getUrl(); 55 | if (!route.getHost() 56 | .isEmpty()) { 57 | domain = domain.substring(route.getHost() 58 | .length() 59 | + 1); 60 | } 61 | if (!route.getPath() 62 | .isEmpty()) { 63 | domain = domain.substring(0, domain.indexOf('/')); 64 | } 65 | if (route.getPort() != null) { 66 | domain = domain.substring(0, domain.indexOf(':')); 67 | } 68 | return domain; 69 | } 70 | 71 | private List mapDestinations() { 72 | return getRoute().getDestinations() 73 | .stream() 74 | .map(destination -> ImmutableRouteDestination.builder() 75 | .metadata(ImmutableCloudMetadata.builder() 76 | .guid(UUID.fromString(destination.getDestinationId())) 77 | .build()) 78 | .applicationGuid(UUID.fromString(destination.getApplication() 79 | .getApplicationId())) 80 | .port(destination.getPort()) 81 | .weight(destination.getWeight()) 82 | .protocol(destination.getProtocol()) 83 | .build()) 84 | .collect(Collectors.toList()); 85 | } 86 | 87 | private String computeRequestedProtocol(List destinations) { 88 | UUID applicationGuid = getApplicationGuid(); 89 | if (applicationGuid == null) { 90 | return null; 91 | } 92 | return destinations.stream() 93 | .filter(destination -> Objects.equals(destination.getApplicationGuid(), applicationGuid)) 94 | .findFirst() 95 | .map(RouteDestination::getProtocol) 96 | .orElse(null); 97 | } 98 | 99 | } 100 | --------------------------------------------------------------------------------