/*
* Copyright 2013-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.cloudfoundry.operations;
import org.cloudfoundry.Nullable;
import org.cloudfoundry.client.CloudFoundryClient;
import org.cloudfoundry.client.v2.organizations.ListOrganizationsRequest;
import org.cloudfoundry.client.v2.organizations.OrganizationResource;
import org.cloudfoundry.client.v2.spaces.ListSpacesRequest;
import org.cloudfoundry.client.v2.spaces.SpaceResource;
import org.cloudfoundry.doppler.DopplerClient;
import org.cloudfoundry.operations.advanced.Advanced;
import org.cloudfoundry.operations.advanced.DefaultAdvanced;
import org.cloudfoundry.operations.applications.Applications;
import org.cloudfoundry.operations.applications.DefaultApplications;
import org.cloudfoundry.operations.buildpacks.Buildpacks;
import org.cloudfoundry.operations.buildpacks.DefaultBuildpacks;
import org.cloudfoundry.operations.domains.DefaultDomains;
import org.cloudfoundry.operations.domains.Domains;
import org.cloudfoundry.operations.organizationadmin.DefaultOrganizationAdmin;
import org.cloudfoundry.operations.organizationadmin.OrganizationAdmin;
import org.cloudfoundry.operations.organizations.DefaultOrganizations;
import org.cloudfoundry.operations.organizations.Organizations;
import org.cloudfoundry.operations.routes.DefaultRoutes;
import org.cloudfoundry.operations.routes.Routes;
import org.cloudfoundry.operations.serviceadmin.DefaultServiceAdmin;
import org.cloudfoundry.operations.serviceadmin.ServiceAdmin;
import org.cloudfoundry.operations.services.DefaultServices;
import org.cloudfoundry.operations.services.Services;
import org.cloudfoundry.operations.spaceadmin.DefaultSpaceAdmin;
import org.cloudfoundry.operations.spaceadmin.SpaceAdmin;
import org.cloudfoundry.operations.spaces.DefaultSpaces;
import org.cloudfoundry.operations.spaces.Spaces;
import org.cloudfoundry.operations.stacks.DefaultStacks;
import org.cloudfoundry.operations.stacks.Stacks;
import org.cloudfoundry.routing.RoutingClient;
import org.cloudfoundry.uaa.UaaClient;
import org.cloudfoundry.util.ExceptionUtils;
import org.cloudfoundry.util.PaginationUtils;
import org.cloudfoundry.util.ResourceUtils;
import org.immutables.value.Value;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.util.NoSuchElementException;
import java.util.Optional;
/**
* The default implementation of the {@link CloudFoundryOperations} interface
*/
@Value.Immutable
abstract class _DefaultCloudFoundryOperations implements CloudFoundryOperations {
@Override
@Value.Derived
public Advanced advanced() {
return new DefaultAdvanced(getUaaClientPublisher());
}
@Override
@Value.Derived
public Applications applications() {
return new DefaultApplications(getCloudFoundryClientPublisher(), getDopplerClientPublisher(), getSpaceId());
}
@Override
@Value.Derived
public Buildpacks buildpacks() {
return new DefaultBuildpacks(getCloudFoundryClientPublisher());
}
@Override
@Value.Derived
public Domains domains() {
return new DefaultDomains(getCloudFoundryClientPublisher(), getRoutingClientPublisher());
}
@Override
@Value.Derived
public OrganizationAdmin organizationAdmin() {
return new DefaultOrganizationAdmin(getCloudFoundryClientPublisher());
}
@Override
@Value.Derived
public Organizations organizations() {
return new DefaultOrganizations(getCloudFoundryClientPublisher(), getUsername());
}
@Override
@Value.Derived
public Routes routes() {
return new DefaultRoutes(getCloudFoundryClientPublisher(), getOrganizationId(), getSpaceId());
}
@Override
@Value.Derived
public ServiceAdmin serviceAdmin() {
return new DefaultServiceAdmin(getCloudFoundryClientPublisher(), getSpaceId());
}
@Override
@Value.Derived
public Services services() {
return new DefaultServices(getCloudFoundryClientPublisher(), getOrganizationId(), getSpaceId());
}
@Override
@Value.Derived
public SpaceAdmin spaceAdmin() {
return new DefaultSpaceAdmin(getCloudFoundryClientPublisher(), getOrganizationId());
}
@Override
@Value.Derived
public Spaces spaces() {
return new DefaultSpaces(getCloudFoundryClientPublisher(), getOrganizationId(), getUsername());
}
@Override
@Value.Derived
public Stacks stacks() {
return new DefaultStacks(getCloudFoundryClientPublisher());
}
/**
* The {@link CloudFoundryClient} to use for operations functionality
*/
@Nullable
abstract CloudFoundryClient getCloudFoundryClient();
@Value.Derived
Mono<CloudFoundryClient> getCloudFoundryClientPublisher() {
return Optional.ofNullable(getCloudFoundryClient())
.map(Mono::just)
.orElse(Mono.error(new IllegalStateException("CloudFoundryClient must be set")));
}
/**
* The {@link DopplerClient} to use for operations functionality
*/
@Nullable
abstract DopplerClient getDopplerClient();
@Value.Derived
Mono<DopplerClient> getDopplerClientPublisher() {
return Optional.ofNullable(getDopplerClient())
.map(Mono::just)
.orElse(Mono.error(new IllegalStateException("DopplerClient must be set")));
}
/**
* The organization to target
*/
@Nullable
abstract String getOrganization();
@Value.Derived
Mono<String> getOrganizationId() {
String organization = getOrganization();
if (hasText(organization)) {
return getOrganization(getCloudFoundryClientPublisher(), organization)
.map(ResourceUtils::getId)
.cache();
} else {
return Mono.error(new IllegalStateException("No organization targeted"));
}
}
/**
* The {@link RoutingClient} to use for operations functionality
*/
@Nullable
abstract RoutingClient getRoutingClient();
@Value.Derived
Mono<RoutingClient> getRoutingClientPublisher() {
return Optional.ofNullable(getRoutingClient())
.map(Mono::just)
.orElse(Mono.error(new IllegalStateException("RoutingClient must be set")));
}
/**
* The space to target
*/
@Nullable
abstract String getSpace();
@Value.Derived
Mono<String> getSpaceId() {
String space = getSpace();
if (hasText(getSpace())) {
return getOrganizationId()
.then(organizationId -> getSpace(getCloudFoundryClientPublisher(), organizationId, space))
.map(ResourceUtils::getId)
.cache();
} else {
return Mono.error(new IllegalStateException("No space targeted"));
}
}
/**
* The {@link UaaClient} to use for operations functionality
*/
@Nullable
abstract UaaClient getUaaClient();
@Value.Derived
Mono<UaaClient> getUaaClientPublisher() {
return Optional.ofNullable(getUaaClient())
.map(Mono::just)
.orElse(Mono.error(new IllegalStateException("UaaClient must be set")));
}
@Value.Derived
Mono<String> getUsername() {
return getUaaClientPublisher()
.then(UaaClient::getUsername);
}
private static Mono<OrganizationResource> getOrganization(Mono<CloudFoundryClient> cloudFoundryClient, String organization) {
return requestOrganizations(cloudFoundryClient, organization)
.single()
.onErrorResume(NoSuchElementException.class, t -> ExceptionUtils.illegalArgument("Organization %s does not exist", organization));
}
private static Mono<SpaceResource> getSpace(Mono<CloudFoundryClient> cloudFoundryClient, String organizationId, String space) {
return requestSpaces(cloudFoundryClient, organizationId, space)
.single()
.onErrorResume(NoSuchElementException.class, t -> ExceptionUtils.illegalArgument("Space %s does not exist", space));
}
private static boolean hasLength(CharSequence str) {
return (str != null && str.length() > 0);
}
private static boolean hasText(CharSequence str) {
if (!hasLength(str)) {
return false;
}
int strLen = str.length();
for (int i = 0; i < strLen; i++) {
if (!Character.isWhitespace(str.charAt(i))) {
return true;
}
}
return false;
}
private static Flux<OrganizationResource> requestOrganizations(Mono<CloudFoundryClient> cloudFoundryClientPublisher, String organization) {
return cloudFoundryClientPublisher
.flatMapMany(cloudFoundryClient -> PaginationUtils
.requestClientV2Resources(page -> cloudFoundryClient.organizations()
.list(ListOrganizationsRequest.builder()
.name(organization)
.page(page)
.build())));
}
private static Flux<SpaceResource> requestSpaces(Mono<CloudFoundryClient> cloudFoundryClientPublisher, String organizationId, String space) {
return cloudFoundryClientPublisher
.flatMapMany(cloudFoundryClient -> PaginationUtils
.requestClientV2Resources(page -> cloudFoundryClient.spaces()
.list(ListSpacesRequest.builder()
.organizationId(organizationId)
.name(space)
.page(page)
.build())));
}
}