From eef22059bf49318965f953cfda55df35a5de5251 Mon Sep 17 00:00:00 2001 From: "Merchant, Saifuddin A" Date: Fri, 12 Dec 2025 15:03:10 -0700 Subject: [PATCH 1/6] Implement apply organization quota with integration test --- .../ReactorOrganizationQuotasV3.java | 14 ++++ .../ReactorOrganizationQuotasV3Test.java | 80 +++++++++++++++++-- .../organizations}/GET_response.json | 0 .../organizations}/GET_{id}_response.json | 0 .../organizations}/PATCH_{id}_request.json | 0 .../organizations}/PATCH_{id}_response.json | 0 .../organizations}/POST_request.json | 0 .../organizations}/POST_response.json | 0 .../relationships/POST_{id}_request.json | 10 +++ .../relationships/POST_{id}_response.json | 18 +++++ .../organizations/OrganizationQuotasV3.java | 9 +++ .../_ApplyOrganizationQuotaRequest.java | 28 +++++++ .../_ApplyOrganizationQuotaResponse.java | 29 +++++++ .../ApplyOrganizationQuotaRequestTest.java | 61 ++++++++++++++ .../client/v3/OrganizationQuotasTest.java | 52 +++++++++++- 15 files changed, 293 insertions(+), 8 deletions(-) rename cloudfoundry-client-reactor/src/test/resources/fixtures/client/v3/{organization_quotas => quotas/organizations}/GET_response.json (100%) rename cloudfoundry-client-reactor/src/test/resources/fixtures/client/v3/{organization_quotas => quotas/organizations}/GET_{id}_response.json (100%) rename cloudfoundry-client-reactor/src/test/resources/fixtures/client/v3/{organization_quotas => quotas/organizations}/PATCH_{id}_request.json (100%) rename cloudfoundry-client-reactor/src/test/resources/fixtures/client/v3/{organization_quotas => quotas/organizations}/PATCH_{id}_response.json (100%) rename cloudfoundry-client-reactor/src/test/resources/fixtures/client/v3/{organization_quotas => quotas/organizations}/POST_request.json (100%) rename cloudfoundry-client-reactor/src/test/resources/fixtures/client/v3/{organization_quotas => quotas/organizations}/POST_response.json (100%) create mode 100644 cloudfoundry-client-reactor/src/test/resources/fixtures/client/v3/quotas/organizations/relationships/POST_{id}_request.json create mode 100644 cloudfoundry-client-reactor/src/test/resources/fixtures/client/v3/quotas/organizations/relationships/POST_{id}_response.json create mode 100644 cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/quotas/organizations/_ApplyOrganizationQuotaRequest.java create mode 100644 cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/quotas/organizations/_ApplyOrganizationQuotaResponse.java create mode 100644 cloudfoundry-client/src/test/java/org/cloudfoundry/client/v3/quotas/organizations/ApplyOrganizationQuotaRequestTest.java diff --git a/cloudfoundry-client-reactor/src/main/java/org/cloudfoundry/reactor/client/v3/quotas/organizations/ReactorOrganizationQuotasV3.java b/cloudfoundry-client-reactor/src/main/java/org/cloudfoundry/reactor/client/v3/quotas/organizations/ReactorOrganizationQuotasV3.java index 2981f0b0f22..3f6c49d8eca 100644 --- a/cloudfoundry-client-reactor/src/main/java/org/cloudfoundry/reactor/client/v3/quotas/organizations/ReactorOrganizationQuotasV3.java +++ b/cloudfoundry-client-reactor/src/main/java/org/cloudfoundry/reactor/client/v3/quotas/organizations/ReactorOrganizationQuotasV3.java @@ -94,4 +94,18 @@ public Mono delete(DeleteOrganizationQuotaRequest request) { "organization_quotas", request.getOrganizationQuotaId())) .checkpoint(); } + + @Override + public Mono apply(ApplyOrganizationQuotaRequest request) { + return post( + request, + ApplyOrganizationQuotaResponse.class, + builder -> + builder.pathSegment( + "organization_quotas", + request.getOrganizationQuotaId(), + "relationships", + "organizations")) + .checkpoint(); + } } diff --git a/cloudfoundry-client-reactor/src/test/java/org/cloudfoundry/reactor/client/v3/quotas/organizations/ReactorOrganizationQuotasV3Test.java b/cloudfoundry-client-reactor/src/test/java/org/cloudfoundry/reactor/client/v3/quotas/organizations/ReactorOrganizationQuotasV3Test.java index 9d2485153e7..332aca569e8 100644 --- a/cloudfoundry-client-reactor/src/test/java/org/cloudfoundry/reactor/client/v3/quotas/organizations/ReactorOrganizationQuotasV3Test.java +++ b/cloudfoundry-client-reactor/src/test/java/org/cloudfoundry/reactor/client/v3/quotas/organizations/ReactorOrganizationQuotasV3Test.java @@ -22,11 +22,14 @@ import java.time.Duration; import java.util.Collections; +import java.util.Map; import org.cloudfoundry.client.v3.Link; import org.cloudfoundry.client.v3.Pagination; import org.cloudfoundry.client.v3.Relationship; import org.cloudfoundry.client.v3.ToManyRelationship; -import org.cloudfoundry.client.v3.quotas.*; +import org.cloudfoundry.client.v3.quotas.Apps; +import org.cloudfoundry.client.v3.quotas.Routes; +import org.cloudfoundry.client.v3.quotas.Services; import org.cloudfoundry.client.v3.quotas.organizations.*; import org.cloudfoundry.reactor.InteractionContext; import org.cloudfoundry.reactor.TestRequest; @@ -51,13 +54,13 @@ void create() { .method(POST) .path("/organization_quotas") .payload( - "fixtures/client/v3/organization_quotas/POST_request.json") + "fixtures/client/v3/quotas/organizations/POST_request.json") .build()) .response( TestResponse.builder() .status(OK) .payload( - "fixtures/client/v3/organization_quotas/POST_response.json") + "fixtures/client/v3/quotas/organizations/POST_response.json") .build()) .build()); @@ -115,7 +118,7 @@ void get() { TestResponse.builder() .status(OK) .payload( - "fixtures/client/v3/organization_quotas/GET_{id}_response.json") + "fixtures/client/v3/quotas/organizations/GET_{id}_response.json") .build()) .build()); @@ -146,7 +149,7 @@ void list() { TestResponse.builder() .status(OK) .payload( - "fixtures/client/v3/organization_quotas/GET_response.json") + "fixtures/client/v3/quotas/organizations/GET_response.json") .build()) .build()); @@ -193,13 +196,13 @@ void update() { .path( "/organization_quotas/24637893-3b77-489d-bb79-8466f0d88b52") .payload( - "fixtures/client/v3/organization_quotas/PATCH_{id}_request.json") + "fixtures/client/v3/quotas/organizations/PATCH_{id}_request.json") .build()) .response( TestResponse.builder() .status(OK) .payload( - "fixtures/client/v3/organization_quotas/PATCH_{id}_response.json") + "fixtures/client/v3/quotas/organizations/PATCH_{id}_response.json") .build()) .build()); @@ -217,6 +220,47 @@ void update() { .verify(Duration.ofSeconds(5)); } + @Test + void apply() { + mockRequest( + InteractionContext.builder() + .request( + TestRequest.builder() + .method(POST) + .path( + "/organization_quotas/24637893-3b77-489d-bb79-8466f0d88b52/relationships/organizations") + .payload( + "fixtures/client/v3/quotas/organizations/relationships/POST_{id}_request.json") + .build()) + .response( + TestResponse.builder() + .status(OK) + .payload( + "fixtures/client/v3/quotas/organizations/relationships/POST_{id}_response.json") + .build()) + .build()); + + Relationship org1 = Relationship.builder().id("org-guid1").build(); + Relationship org2 = Relationship.builder().id("org-guid2").build(); + + ToManyRelationship organizationRelationships = + ToManyRelationship.builder().data(org1, org2).build(); + + this.organizationQuotasV3 + .apply( + ApplyOrganizationQuotaRequest.builder() + .organizationQuotaId("24637893-3b77-489d-bb79-8466f0d88b52") + .organizationRelationships(organizationRelationships) + .build()) + .as(StepVerifier::create) + .expectNext( + ApplyOrganizationQuotaResponse.builder() + .from(expectedApplyOrganizationQuotaResponse()) + .build()) + .expectComplete() + .verify(Duration.ofSeconds(5)); + } + @NotNull private static OrganizationQuotaResource expectedOrganizationQuotaResource1() { return buildOrganizationQuotaResource( @@ -225,6 +269,28 @@ private static OrganizationQuotaResource expectedOrganizationQuotaResource1() { "9b370018-c38e-44c9-86d6-155c76801104"); } + @NotNull + private static ApplyOrganizationQuotaResponse expectedApplyOrganizationQuotaResponse() { + + Relationship org1 = Relationship.builder().id("org-guid1").build(); + Relationship org2 = Relationship.builder().id("org-guid2").build(); + Relationship existingOrg = Relationship.builder().id("previous-org-guid").build(); + + ToManyRelationship organizationRelationships = + ToManyRelationship.builder().data(org1, org2, existingOrg).build(); + Link selfLink = + Link.builder() + .href( + "https://api.example.org/v3/organization_quotas/24637893-3b77-489d-bb79-8466f0d88b52/relationships/organizations") + .build(); + Map links = Collections.singletonMap("self", selfLink); + + return ApplyOrganizationQuotaResponse.builder() + .organizationRelationships(organizationRelationships) + .links(links) + .build(); + } + private static OrganizationQuotaResource expectedOrganizationQuotaResource2() { return buildOrganizationQuotaResource( "bb49bf20-ad98-4729-93ae-38fbc564b630", diff --git a/cloudfoundry-client-reactor/src/test/resources/fixtures/client/v3/organization_quotas/GET_response.json b/cloudfoundry-client-reactor/src/test/resources/fixtures/client/v3/quotas/organizations/GET_response.json similarity index 100% rename from cloudfoundry-client-reactor/src/test/resources/fixtures/client/v3/organization_quotas/GET_response.json rename to cloudfoundry-client-reactor/src/test/resources/fixtures/client/v3/quotas/organizations/GET_response.json diff --git a/cloudfoundry-client-reactor/src/test/resources/fixtures/client/v3/organization_quotas/GET_{id}_response.json b/cloudfoundry-client-reactor/src/test/resources/fixtures/client/v3/quotas/organizations/GET_{id}_response.json similarity index 100% rename from cloudfoundry-client-reactor/src/test/resources/fixtures/client/v3/organization_quotas/GET_{id}_response.json rename to cloudfoundry-client-reactor/src/test/resources/fixtures/client/v3/quotas/organizations/GET_{id}_response.json diff --git a/cloudfoundry-client-reactor/src/test/resources/fixtures/client/v3/organization_quotas/PATCH_{id}_request.json b/cloudfoundry-client-reactor/src/test/resources/fixtures/client/v3/quotas/organizations/PATCH_{id}_request.json similarity index 100% rename from cloudfoundry-client-reactor/src/test/resources/fixtures/client/v3/organization_quotas/PATCH_{id}_request.json rename to cloudfoundry-client-reactor/src/test/resources/fixtures/client/v3/quotas/organizations/PATCH_{id}_request.json diff --git a/cloudfoundry-client-reactor/src/test/resources/fixtures/client/v3/organization_quotas/PATCH_{id}_response.json b/cloudfoundry-client-reactor/src/test/resources/fixtures/client/v3/quotas/organizations/PATCH_{id}_response.json similarity index 100% rename from cloudfoundry-client-reactor/src/test/resources/fixtures/client/v3/organization_quotas/PATCH_{id}_response.json rename to cloudfoundry-client-reactor/src/test/resources/fixtures/client/v3/quotas/organizations/PATCH_{id}_response.json diff --git a/cloudfoundry-client-reactor/src/test/resources/fixtures/client/v3/organization_quotas/POST_request.json b/cloudfoundry-client-reactor/src/test/resources/fixtures/client/v3/quotas/organizations/POST_request.json similarity index 100% rename from cloudfoundry-client-reactor/src/test/resources/fixtures/client/v3/organization_quotas/POST_request.json rename to cloudfoundry-client-reactor/src/test/resources/fixtures/client/v3/quotas/organizations/POST_request.json diff --git a/cloudfoundry-client-reactor/src/test/resources/fixtures/client/v3/organization_quotas/POST_response.json b/cloudfoundry-client-reactor/src/test/resources/fixtures/client/v3/quotas/organizations/POST_response.json similarity index 100% rename from cloudfoundry-client-reactor/src/test/resources/fixtures/client/v3/organization_quotas/POST_response.json rename to cloudfoundry-client-reactor/src/test/resources/fixtures/client/v3/quotas/organizations/POST_response.json diff --git a/cloudfoundry-client-reactor/src/test/resources/fixtures/client/v3/quotas/organizations/relationships/POST_{id}_request.json b/cloudfoundry-client-reactor/src/test/resources/fixtures/client/v3/quotas/organizations/relationships/POST_{id}_request.json new file mode 100644 index 00000000000..093842883d5 --- /dev/null +++ b/cloudfoundry-client-reactor/src/test/resources/fixtures/client/v3/quotas/organizations/relationships/POST_{id}_request.json @@ -0,0 +1,10 @@ +{ + "data": [ + { + "guid": "org-guid1" + }, + { + "guid": "org-guid2" + } + ] +} \ No newline at end of file diff --git a/cloudfoundry-client-reactor/src/test/resources/fixtures/client/v3/quotas/organizations/relationships/POST_{id}_response.json b/cloudfoundry-client-reactor/src/test/resources/fixtures/client/v3/quotas/organizations/relationships/POST_{id}_response.json new file mode 100644 index 00000000000..e1abe6da192 --- /dev/null +++ b/cloudfoundry-client-reactor/src/test/resources/fixtures/client/v3/quotas/organizations/relationships/POST_{id}_response.json @@ -0,0 +1,18 @@ +{ + "data": [ + { + "guid": "org-guid1" + }, + { + "guid": "org-guid2" + }, + { + "guid": "previous-org-guid" + } + ], + "links": { + "self": { + "href": "https://api.example.org/v3/organization_quotas/24637893-3b77-489d-bb79-8466f0d88b52/relationships/organizations" + } + } +} \ No newline at end of file diff --git a/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/quotas/organizations/OrganizationQuotasV3.java b/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/quotas/organizations/OrganizationQuotasV3.java index cb6f9a42238..44412f7cfde 100644 --- a/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/quotas/organizations/OrganizationQuotasV3.java +++ b/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/quotas/organizations/OrganizationQuotasV3.java @@ -66,4 +66,13 @@ public interface OrganizationQuotasV3 { * @return the response from the Delete Organization Quota request */ Mono delete(DeleteOrganizationQuotaRequest request); + + /** + * Makes the Apply an Organization Quota to an Organization + * request + * + * @param request the Apply an Organization Quota to an Organization request + * @return the response from the Apply an Organization Quota to an Organization request + */ + Mono apply(ApplyOrganizationQuotaRequest request); } diff --git a/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/quotas/organizations/_ApplyOrganizationQuotaRequest.java b/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/quotas/organizations/_ApplyOrganizationQuotaRequest.java new file mode 100644 index 00000000000..aa83eee67bc --- /dev/null +++ b/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/quotas/organizations/_ApplyOrganizationQuotaRequest.java @@ -0,0 +1,28 @@ +package org.cloudfoundry.client.v3.quotas.organizations; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonUnwrapped; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import org.cloudfoundry.client.v3.ToManyRelationship; +import org.immutables.value.Value; + +/** + * The request payload to apply an Organization Quota to an Organization + */ +@JsonSerialize +@Value.Immutable +abstract class _ApplyOrganizationQuotaRequest { + + /** + * The Organization Quota id + */ + @JsonIgnore + abstract String getOrganizationQuotaId(); + + /** + * A relationship to the organizations where the quota is applied + * Use of JsonUnwrapped to inline the organization relationships as per the API spec + */ + @JsonUnwrapped + abstract ToManyRelationship organizationRelationships(); +} diff --git a/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/quotas/organizations/_ApplyOrganizationQuotaResponse.java b/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/quotas/organizations/_ApplyOrganizationQuotaResponse.java new file mode 100644 index 00000000000..21850c34f14 --- /dev/null +++ b/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/quotas/organizations/_ApplyOrganizationQuotaResponse.java @@ -0,0 +1,29 @@ +package org.cloudfoundry.client.v3.quotas.organizations; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonUnwrapped; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import org.cloudfoundry.AllowNulls; +import org.cloudfoundry.client.v3.Link; +import org.cloudfoundry.client.v3.ToManyRelationship; +import org.immutables.value.Value; + +import java.util.Map; + +/** + * The response payload for applying an Organization Quota to an Organization + */ +@JsonDeserialize +@Value.Immutable +abstract class _ApplyOrganizationQuotaResponse { + + @JsonUnwrapped + abstract ToManyRelationship organizationRelationships(); + + /** + * Links to related resources and actions for the resource + */ + @AllowNulls + @JsonProperty("links") + public abstract Map getLinks(); +} diff --git a/cloudfoundry-client/src/test/java/org/cloudfoundry/client/v3/quotas/organizations/ApplyOrganizationQuotaRequestTest.java b/cloudfoundry-client/src/test/java/org/cloudfoundry/client/v3/quotas/organizations/ApplyOrganizationQuotaRequestTest.java new file mode 100644 index 00000000000..dd01bc3eb60 --- /dev/null +++ b/cloudfoundry-client/src/test/java/org/cloudfoundry/client/v3/quotas/organizations/ApplyOrganizationQuotaRequestTest.java @@ -0,0 +1,61 @@ +/* + * Copyright 2013-2025 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.client.v3.quotas.organizations; + +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.cloudfoundry.client.v3.Relationship; +import org.cloudfoundry.client.v3.ToManyRelationship; +import org.junit.jupiter.api.Test; + +final class ApplyOrganizationQuotaRequestTest { + + @Test + void noOrganizationRelationships() { + assertThrows( + IllegalStateException.class, + () -> + ApplyOrganizationQuotaRequest.builder() + .organizationQuotaId("quota-id") + .build()); + } + + @Test + void noOrganizationQuotaId() { + Relationship organizationRelationship = Relationship.builder().id("test-quota").build(); + ToManyRelationship organizationRelationships = + ToManyRelationship.builder().data(organizationRelationship).build(); + + assertThrows( + IllegalStateException.class, + () -> + ApplyOrganizationQuotaRequest.builder() + .organizationRelationships(organizationRelationships) + .build()); + } + + @Test + void valid() { + Relationship organizationRelationship = Relationship.builder().id("test-quota").build(); + ToManyRelationship organizationRelationships = + ToManyRelationship.builder().data(organizationRelationship).build(); + ApplyOrganizationQuotaRequest.builder() + .organizationQuotaId("quota-id") + .organizationRelationships(organizationRelationships) + .build(); + } +} diff --git a/integration-test/src/test/java/org/cloudfoundry/client/v3/OrganizationQuotasTest.java b/integration-test/src/test/java/org/cloudfoundry/client/v3/OrganizationQuotasTest.java index 15b0239c96e..e719fcc1b2b 100644 --- a/integration-test/src/test/java/org/cloudfoundry/client/v3/OrganizationQuotasTest.java +++ b/integration-test/src/test/java/org/cloudfoundry/client/v3/OrganizationQuotasTest.java @@ -19,11 +19,16 @@ import static org.assertj.core.api.Assertions.assertThat; import java.time.Duration; +import java.util.List; import org.cloudfoundry.AbstractIntegrationTest; import org.cloudfoundry.CloudFoundryVersion; import org.cloudfoundry.IfCloudFoundryVersion; import org.cloudfoundry.client.CloudFoundryClient; -import org.cloudfoundry.client.v3.quotas.*; +import org.cloudfoundry.client.v3.organizations.CreateOrganizationRequest; +import org.cloudfoundry.client.v3.organizations.Organization; +import org.cloudfoundry.client.v3.quotas.Apps; +import org.cloudfoundry.client.v3.quotas.Routes; +import org.cloudfoundry.client.v3.quotas.Services; import org.cloudfoundry.client.v3.quotas.organizations.*; import org.cloudfoundry.util.JobUtils; import org.cloudfoundry.util.PaginationUtils; @@ -197,6 +202,43 @@ public void update() { .verify(Duration.ofMinutes(5)); } + @Test + public void apply() { + String orgName = this.nameFactory.getOrganizationName(); + String organizationId = createOrganization(this.cloudFoundryClient, orgName).getId(); + Relationship organizationRelationship1 = Relationship.builder().id(organizationId).build(); + ToManyRelationship organizationRelationships = + ToManyRelationship.builder().data(organizationRelationship1).build(); + + String organizationQuotaName = this.nameFactory.getQuotaDefinitionName(); + + createOrganizationQuotaId(this.cloudFoundryClient, organizationQuotaName) + .flatMap( + organizationQuotaId -> { + ApplyOrganizationQuotaRequest applyOrganizationQuotaRequest = + ApplyOrganizationQuotaRequest.builder() + .organizationQuotaId(organizationQuotaId) + .organizationRelationships(organizationRelationships) + .build(); + return this.cloudFoundryClient + .organizationQuotasV3() + .apply(applyOrganizationQuotaRequest); + }) + .as(StepVerifier::create) + .consumeNextWith( + applyOrganizationQuotaResponse -> { + List organizationRelationshipsData = + applyOrganizationQuotaResponse + .organizationRelationships() + .getData(); + assertThat(organizationRelationshipsData.size()).isEqualTo(1); + assertThat(organizationRelationshipsData.get(0).getId()) + .isEqualTo(organizationId); + }) + .expectComplete() + .verify(Duration.ofMinutes(5)); + } + private static Mono createOrganizationQuotaId( CloudFoundryClient cloudFoundryClient, String organizationQuotaName) { return createOrganizationQuota(cloudFoundryClient, organizationQuotaName) @@ -225,4 +267,12 @@ private static Flux requestListOrganizationQuotas( .page(page) .build())); } + + private static Organization createOrganization( + CloudFoundryClient cloudFoundryClient, String orgName) { + return cloudFoundryClient + .organizationsV3() + .create(CreateOrganizationRequest.builder().name(orgName).build()) + .block(Duration.ofMinutes(5)); + } } From 75a2b794bd2311b2a3a5abe39d2369dfec60d2e5 Mon Sep 17 00:00:00 2001 From: "Merchant, Saifuddin A" Date: Sun, 14 Dec 2025 15:50:29 -0700 Subject: [PATCH 2/6] Implement apply and remove space quota --- .../quotas/spaces/ReactorSpaceQuotasV3.java | 29 +++++ .../ReactorOrganizationQuotasV3Test.java | 14 +-- .../spaces/ReactorSpaceQuotasV3Test.java | 118 +++++++++++++++--- .../spaces}/GET_response.json | 0 .../spaces}/GET_{id}_response.json | 0 .../spaces}/PATCH_{id}_request.json | 0 .../spaces}/PATCH_{id}_response.json | 0 .../spaces}/POST_request.json | 0 .../spaces}/POST_response.json | 0 .../relationships/POST_{id}_request.json | 10 ++ .../relationships/POST_{id}_response.json | 18 +++ .../v3/quotas/spaces/SpaceQuotasV3.java | 18 +++ .../spaces/_ApplySpaceQuotaRequest.java | 28 +++++ .../spaces/_ApplySpaceQuotaResponse.java | 29 +++++ .../spaces/_RemoveSpaceQuotaRequest.java | 40 ++++++ .../ApplyOrganizationQuotaRequestTest.java | 6 +- .../spaces/ApplySpaceQuotaRequestTest.java | 58 +++++++++ .../spaces/RemoveSpaceQuotaRequestTest.java | 43 +++++++ pom.xml | 40 +++--- 19 files changed, 404 insertions(+), 47 deletions(-) rename cloudfoundry-client-reactor/src/test/resources/fixtures/client/v3/{space_quotas => quotas/spaces}/GET_response.json (100%) rename cloudfoundry-client-reactor/src/test/resources/fixtures/client/v3/{space_quotas => quotas/spaces}/GET_{id}_response.json (100%) rename cloudfoundry-client-reactor/src/test/resources/fixtures/client/v3/{space_quotas => quotas/spaces}/PATCH_{id}_request.json (100%) rename cloudfoundry-client-reactor/src/test/resources/fixtures/client/v3/{space_quotas => quotas/spaces}/PATCH_{id}_response.json (100%) rename cloudfoundry-client-reactor/src/test/resources/fixtures/client/v3/{space_quotas => quotas/spaces}/POST_request.json (100%) rename cloudfoundry-client-reactor/src/test/resources/fixtures/client/v3/{space_quotas => quotas/spaces}/POST_response.json (100%) create mode 100644 cloudfoundry-client-reactor/src/test/resources/fixtures/client/v3/quotas/spaces/relationships/POST_{id}_request.json create mode 100644 cloudfoundry-client-reactor/src/test/resources/fixtures/client/v3/quotas/spaces/relationships/POST_{id}_response.json create mode 100644 cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/quotas/spaces/_ApplySpaceQuotaRequest.java create mode 100644 cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/quotas/spaces/_ApplySpaceQuotaResponse.java create mode 100644 cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/quotas/spaces/_RemoveSpaceQuotaRequest.java create mode 100644 cloudfoundry-client/src/test/java/org/cloudfoundry/client/v3/quotas/spaces/ApplySpaceQuotaRequestTest.java create mode 100644 cloudfoundry-client/src/test/java/org/cloudfoundry/client/v3/quotas/spaces/RemoveSpaceQuotaRequestTest.java diff --git a/cloudfoundry-client-reactor/src/main/java/org/cloudfoundry/reactor/client/v3/quotas/spaces/ReactorSpaceQuotasV3.java b/cloudfoundry-client-reactor/src/main/java/org/cloudfoundry/reactor/client/v3/quotas/spaces/ReactorSpaceQuotasV3.java index 93e8182a000..1a6b5125204 100644 --- a/cloudfoundry-client-reactor/src/main/java/org/cloudfoundry/reactor/client/v3/quotas/spaces/ReactorSpaceQuotasV3.java +++ b/cloudfoundry-client-reactor/src/main/java/org/cloudfoundry/reactor/client/v3/quotas/spaces/ReactorSpaceQuotasV3.java @@ -87,4 +87,33 @@ public Mono delete(DeleteSpaceQuotaRequest request) { builder -> builder.pathSegment("space_quotas", request.getSpaceQuotaId())) .checkpoint(); } + + @Override + public Mono apply(ApplySpaceQuotaRequest request) { + return post( + request, + ApplySpaceQuotaResponse.class, + builder -> + builder.pathSegment( + "space_quotas", + request.getSpaceQuotaId(), + "relationships", + "spaces")) + .checkpoint(); + } + + @Override + public Mono remove(RemoveSpaceQuotaRequest request) { + return delete( + request, + Void.class, + builder -> + builder.pathSegment( + "space_quotas", + request.getSpaceQuotaId(), + "relationships", + "spaces", + request.getSpaceId())) + .checkpoint(); + } } diff --git a/cloudfoundry-client-reactor/src/test/java/org/cloudfoundry/reactor/client/v3/quotas/organizations/ReactorOrganizationQuotasV3Test.java b/cloudfoundry-client-reactor/src/test/java/org/cloudfoundry/reactor/client/v3/quotas/organizations/ReactorOrganizationQuotasV3Test.java index 332aca569e8..8488b3271a2 100644 --- a/cloudfoundry-client-reactor/src/test/java/org/cloudfoundry/reactor/client/v3/quotas/organizations/ReactorOrganizationQuotasV3Test.java +++ b/cloudfoundry-client-reactor/src/test/java/org/cloudfoundry/reactor/client/v3/quotas/organizations/ReactorOrganizationQuotasV3Test.java @@ -269,6 +269,13 @@ private static OrganizationQuotaResource expectedOrganizationQuotaResource1() { "9b370018-c38e-44c9-86d6-155c76801104"); } + private static OrganizationQuotaResource expectedOrganizationQuotaResource2() { + return buildOrganizationQuotaResource( + "bb49bf20-ad98-4729-93ae-38fbc564b630", + "my-quota-2", + "144251f2-a202-4ffe-ab47-9046c4077e99"); + } + @NotNull private static ApplyOrganizationQuotaResponse expectedApplyOrganizationQuotaResponse() { @@ -291,13 +298,6 @@ private static ApplyOrganizationQuotaResponse expectedApplyOrganizationQuotaResp .build(); } - private static OrganizationQuotaResource expectedOrganizationQuotaResource2() { - return buildOrganizationQuotaResource( - "bb49bf20-ad98-4729-93ae-38fbc564b630", - "my-quota-2", - "144251f2-a202-4ffe-ab47-9046c4077e99"); - } - @NotNull private static OrganizationQuotaResource buildOrganizationQuotaResource( String id, String name, String relatedOrganizationId) { diff --git a/cloudfoundry-client-reactor/src/test/java/org/cloudfoundry/reactor/client/v3/quotas/spaces/ReactorSpaceQuotasV3Test.java b/cloudfoundry-client-reactor/src/test/java/org/cloudfoundry/reactor/client/v3/quotas/spaces/ReactorSpaceQuotasV3Test.java index 34231e94269..7923efbb313 100644 --- a/cloudfoundry-client-reactor/src/test/java/org/cloudfoundry/reactor/client/v3/quotas/spaces/ReactorSpaceQuotasV3Test.java +++ b/cloudfoundry-client-reactor/src/test/java/org/cloudfoundry/reactor/client/v3/quotas/spaces/ReactorSpaceQuotasV3Test.java @@ -16,21 +16,16 @@ package org.cloudfoundry.reactor.client.v3.quotas.spaces; -import static io.netty.handler.codec.http.HttpMethod.DELETE; -import static io.netty.handler.codec.http.HttpMethod.GET; -import static io.netty.handler.codec.http.HttpMethod.PATCH; -import static io.netty.handler.codec.http.HttpMethod.POST; -import static io.netty.handler.codec.http.HttpResponseStatus.ACCEPTED; -import static io.netty.handler.codec.http.HttpResponseStatus.OK; +import static io.netty.handler.codec.http.HttpMethod.*; +import static io.netty.handler.codec.http.HttpResponseStatus.*; import java.time.Duration; import java.util.Collections; -import org.cloudfoundry.client.v3.Link; -import org.cloudfoundry.client.v3.Pagination; -import org.cloudfoundry.client.v3.Relationship; -import org.cloudfoundry.client.v3.ToManyRelationship; -import org.cloudfoundry.client.v3.ToOneRelationship; -import org.cloudfoundry.client.v3.quotas.*; +import java.util.Map; +import org.cloudfoundry.client.v3.*; +import org.cloudfoundry.client.v3.quotas.Apps; +import org.cloudfoundry.client.v3.quotas.Routes; +import org.cloudfoundry.client.v3.quotas.Services; import org.cloudfoundry.client.v3.quotas.spaces.*; import org.cloudfoundry.reactor.InteractionContext; import org.cloudfoundry.reactor.TestRequest; @@ -57,13 +52,13 @@ void create() { .method(POST) .path("/space_quotas") .payload( - "fixtures/client/v3/space_quotas/POST_request.json") + "fixtures/client/v3/quotas/spaces/POST_request.json") .build()) .response( TestResponse.builder() .status(OK) .payload( - "fixtures/client/v3/space_quotas/POST_response.json") + "fixtures/client/v3/quotas/spaces/POST_response.json") .build()) .build()); @@ -143,7 +138,7 @@ void get() { TestResponse.builder() .status(OK) .payload( - "fixtures/client/v3/space_quotas/GET_{id}_response.json") + "fixtures/client/v3/quotas/spaces/GET_{id}_response.json") .build()) .build()); @@ -165,7 +160,7 @@ void list() { TestResponse.builder() .status(OK) .payload( - "fixtures/client/v3/space_quotas/GET_response.json") + "fixtures/client/v3/quotas/spaces/GET_response.json") .build()) .build()); @@ -211,13 +206,13 @@ void update() { .method(PATCH) .path("/space_quotas/" + EXPECTED_SPACE_QUOTA_ID_1) .payload( - "fixtures/client/v3/space_quotas/PATCH_{id}_request.json") + "fixtures/client/v3/quotas/spaces/PATCH_{id}_request.json") .build()) .response( TestResponse.builder() .status(OK) .payload( - "fixtures/client/v3/space_quotas/PATCH_{id}_response.json") + "fixtures/client/v3/quotas/spaces/PATCH_{id}_response.json") .build()) .build()); @@ -235,6 +230,71 @@ void update() { .verify(Duration.ofSeconds(5)); } + @Test + void apply() { + mockRequest( + InteractionContext.builder() + .request( + TestRequest.builder() + .method(POST) + .path( + "/space_quotas/24637893-3b77-489d-bb79-8466f0d88b52/relationships/spaces") + .payload( + "fixtures/client/v3/quotas/spaces/relationships/POST_{id}_request.json") + .build()) + .response( + TestResponse.builder() + .status(OK) + .payload( + "fixtures/client/v3/quotas/spaces/relationships/POST_{id}_response.json") + .build()) + .build()); + + Relationship space1 = Relationship.builder().id("space-guid1").build(); + Relationship space2 = Relationship.builder().id("space-guid2").build(); + + ToManyRelationship organizationRelationships = + ToManyRelationship.builder().data(space1, space2).build(); + + this.spaceQuotasV3 + .apply( + ApplySpaceQuotaRequest.builder() + .spaceQuotaId("24637893-3b77-489d-bb79-8466f0d88b52") + .spaceRelationships(organizationRelationships) + .build()) + .as(StepVerifier::create) + .expectNext( + ApplySpaceQuotaResponse.builder() + .from(expectedApplySpaceQuotaResponse()) + .build()) + .expectComplete() + .verify(Duration.ofSeconds(5)); + } + + @Test + void remove() { + mockRequest( + InteractionContext.builder() + .request( + TestRequest.builder() + .method(DELETE) + .path( + "/space_quotas/test-space-quota-id/relationships/spaces/test-space-guid") + .build()) + .response(TestResponse.builder().status(NO_CONTENT).build()) + .build()); + + this.spaceQuotasV3 + .remove( + RemoveSpaceQuotaRequest.builder() + .spaceQuotaId("test-space-quota-id") + .spaceId("test-space-guid") + .build()) + .as(StepVerifier::create) + .expectComplete() + .verify(Duration.ofSeconds(5)); + } + @NotNull private static SpaceQuotaResource expectedSpaceQuotaResource1() { return buildSpaceQuotaResource( @@ -252,6 +312,28 @@ private static SpaceQuotaResource expectedSpaceQuotaResource2() { null); } + @NotNull + private static ApplySpaceQuotaResponse expectedApplySpaceQuotaResponse() { + + Relationship space1 = Relationship.builder().id("space-guid1").build(); + Relationship space2 = Relationship.builder().id("space-guid2").build(); + Relationship existingSpace = Relationship.builder().id("previous-space-guid").build(); + + ToManyRelationship spaceRelationships = + ToManyRelationship.builder().data(space1, space2, existingSpace).build(); + Link selfLink = + Link.builder() + .href( + "https://api.example.org/v3/space_quotas/24637893-3b77-489d-bb79-8466f0d88b52/relationships/spaces") + .build(); + Map links = Collections.singletonMap("self", selfLink); + + return ApplySpaceQuotaResponse.builder() + .spaceRelationships(spaceRelationships) + .links(links) + .build(); + } + @NotNull private static SpaceQuotaResource buildSpaceQuotaResource( String id, String name, String relatedOrganizationId, String relatedSpaceId) { diff --git a/cloudfoundry-client-reactor/src/test/resources/fixtures/client/v3/space_quotas/GET_response.json b/cloudfoundry-client-reactor/src/test/resources/fixtures/client/v3/quotas/spaces/GET_response.json similarity index 100% rename from cloudfoundry-client-reactor/src/test/resources/fixtures/client/v3/space_quotas/GET_response.json rename to cloudfoundry-client-reactor/src/test/resources/fixtures/client/v3/quotas/spaces/GET_response.json diff --git a/cloudfoundry-client-reactor/src/test/resources/fixtures/client/v3/space_quotas/GET_{id}_response.json b/cloudfoundry-client-reactor/src/test/resources/fixtures/client/v3/quotas/spaces/GET_{id}_response.json similarity index 100% rename from cloudfoundry-client-reactor/src/test/resources/fixtures/client/v3/space_quotas/GET_{id}_response.json rename to cloudfoundry-client-reactor/src/test/resources/fixtures/client/v3/quotas/spaces/GET_{id}_response.json diff --git a/cloudfoundry-client-reactor/src/test/resources/fixtures/client/v3/space_quotas/PATCH_{id}_request.json b/cloudfoundry-client-reactor/src/test/resources/fixtures/client/v3/quotas/spaces/PATCH_{id}_request.json similarity index 100% rename from cloudfoundry-client-reactor/src/test/resources/fixtures/client/v3/space_quotas/PATCH_{id}_request.json rename to cloudfoundry-client-reactor/src/test/resources/fixtures/client/v3/quotas/spaces/PATCH_{id}_request.json diff --git a/cloudfoundry-client-reactor/src/test/resources/fixtures/client/v3/space_quotas/PATCH_{id}_response.json b/cloudfoundry-client-reactor/src/test/resources/fixtures/client/v3/quotas/spaces/PATCH_{id}_response.json similarity index 100% rename from cloudfoundry-client-reactor/src/test/resources/fixtures/client/v3/space_quotas/PATCH_{id}_response.json rename to cloudfoundry-client-reactor/src/test/resources/fixtures/client/v3/quotas/spaces/PATCH_{id}_response.json diff --git a/cloudfoundry-client-reactor/src/test/resources/fixtures/client/v3/space_quotas/POST_request.json b/cloudfoundry-client-reactor/src/test/resources/fixtures/client/v3/quotas/spaces/POST_request.json similarity index 100% rename from cloudfoundry-client-reactor/src/test/resources/fixtures/client/v3/space_quotas/POST_request.json rename to cloudfoundry-client-reactor/src/test/resources/fixtures/client/v3/quotas/spaces/POST_request.json diff --git a/cloudfoundry-client-reactor/src/test/resources/fixtures/client/v3/space_quotas/POST_response.json b/cloudfoundry-client-reactor/src/test/resources/fixtures/client/v3/quotas/spaces/POST_response.json similarity index 100% rename from cloudfoundry-client-reactor/src/test/resources/fixtures/client/v3/space_quotas/POST_response.json rename to cloudfoundry-client-reactor/src/test/resources/fixtures/client/v3/quotas/spaces/POST_response.json diff --git a/cloudfoundry-client-reactor/src/test/resources/fixtures/client/v3/quotas/spaces/relationships/POST_{id}_request.json b/cloudfoundry-client-reactor/src/test/resources/fixtures/client/v3/quotas/spaces/relationships/POST_{id}_request.json new file mode 100644 index 00000000000..9cf906732a3 --- /dev/null +++ b/cloudfoundry-client-reactor/src/test/resources/fixtures/client/v3/quotas/spaces/relationships/POST_{id}_request.json @@ -0,0 +1,10 @@ +{ + "data": [ + { + "guid": "space-guid1" + }, + { + "guid": "space-guid2" + } + ] +} \ No newline at end of file diff --git a/cloudfoundry-client-reactor/src/test/resources/fixtures/client/v3/quotas/spaces/relationships/POST_{id}_response.json b/cloudfoundry-client-reactor/src/test/resources/fixtures/client/v3/quotas/spaces/relationships/POST_{id}_response.json new file mode 100644 index 00000000000..7b316d68e36 --- /dev/null +++ b/cloudfoundry-client-reactor/src/test/resources/fixtures/client/v3/quotas/spaces/relationships/POST_{id}_response.json @@ -0,0 +1,18 @@ +{ + "data": [ + { + "guid": "space-guid1" + }, + { + "guid": "space-guid2" + }, + { + "guid": "previous-space-guid" + } + ], + "links": { + "self": { + "href": "https://api.example.org/v3/space_quotas/24637893-3b77-489d-bb79-8466f0d88b52/relationships/spaces" + } + } +} \ No newline at end of file diff --git a/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/quotas/spaces/SpaceQuotasV3.java b/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/quotas/spaces/SpaceQuotasV3.java index cf34330e8c6..86f19567791 100644 --- a/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/quotas/spaces/SpaceQuotasV3.java +++ b/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/quotas/spaces/SpaceQuotasV3.java @@ -66,4 +66,22 @@ public interface SpaceQuotasV3 { * @return the response from the Space Organization Quota request */ Mono delete(DeleteSpaceQuotaRequest request); + + /** + * Makes the Apply a Space Quota to a Space + * request + * + * @param request the Apply a Space Quota to a Space request + * @return the response from the Apply a Space Quota to a Space request + */ + Mono apply(ApplySpaceQuotaRequest request); + + /** + * Makes the Remove a Space Quota from a Space + * request + * + * @param request the Remove a Space Quota from a Space request + * @return the response from the Remove a Space Quota from a Space request + */ + Mono remove(RemoveSpaceQuotaRequest request); } diff --git a/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/quotas/spaces/_ApplySpaceQuotaRequest.java b/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/quotas/spaces/_ApplySpaceQuotaRequest.java new file mode 100644 index 00000000000..3ed51600eac --- /dev/null +++ b/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/quotas/spaces/_ApplySpaceQuotaRequest.java @@ -0,0 +1,28 @@ +package org.cloudfoundry.client.v3.quotas.spaces; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonUnwrapped; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import org.cloudfoundry.client.v3.ToManyRelationship; +import org.immutables.value.Value; + +/** + * The request payload to apply an Space Quota to a Space + */ +@JsonSerialize +@Value.Immutable +abstract class _ApplySpaceQuotaRequest { + + /** + * The Space Quota id + */ + @JsonIgnore + abstract String getSpaceQuotaId(); + + /** + * A relationship to the spaces where the quota is applied + * Use of JsonUnwrapped to inline the space relationships as per the API spec + */ + @JsonUnwrapped + abstract ToManyRelationship spaceRelationships(); +} diff --git a/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/quotas/spaces/_ApplySpaceQuotaResponse.java b/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/quotas/spaces/_ApplySpaceQuotaResponse.java new file mode 100644 index 00000000000..c14ca74046d --- /dev/null +++ b/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/quotas/spaces/_ApplySpaceQuotaResponse.java @@ -0,0 +1,29 @@ +package org.cloudfoundry.client.v3.quotas.spaces; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonUnwrapped; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import org.cloudfoundry.AllowNulls; +import org.cloudfoundry.client.v3.Link; +import org.cloudfoundry.client.v3.ToManyRelationship; +import org.immutables.value.Value; + +import java.util.Map; + +/** + * The response payload for applying a Space Quota to a Space + */ +@JsonDeserialize +@Value.Immutable +abstract class _ApplySpaceQuotaResponse { + + @JsonUnwrapped + abstract ToManyRelationship spaceRelationships(); + + /** + * Links to related resources and actions for the resource + */ + @AllowNulls + @JsonProperty("links") + public abstract Map getLinks(); +} diff --git a/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/quotas/spaces/_RemoveSpaceQuotaRequest.java b/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/quotas/spaces/_RemoveSpaceQuotaRequest.java new file mode 100644 index 00000000000..38f08bb5254 --- /dev/null +++ b/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/quotas/spaces/_RemoveSpaceQuotaRequest.java @@ -0,0 +1,40 @@ +/* + * Copyright 2013-2025 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.client.v3.quotas.spaces; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import org.immutables.value.Value; + +/** + * The request payload to Remove a space quota from a space + */ +@Value.Immutable +abstract class _RemoveSpaceQuotaRequest { + + /** + * The space quota id + */ + @JsonIgnore + abstract String getSpaceQuotaId(); + + /** + * The space id + */ + @JsonIgnore + abstract String getSpaceId(); + +} diff --git a/cloudfoundry-client/src/test/java/org/cloudfoundry/client/v3/quotas/organizations/ApplyOrganizationQuotaRequestTest.java b/cloudfoundry-client/src/test/java/org/cloudfoundry/client/v3/quotas/organizations/ApplyOrganizationQuotaRequestTest.java index dd01bc3eb60..6a572188c93 100644 --- a/cloudfoundry-client/src/test/java/org/cloudfoundry/client/v3/quotas/organizations/ApplyOrganizationQuotaRequestTest.java +++ b/cloudfoundry-client/src/test/java/org/cloudfoundry/client/v3/quotas/organizations/ApplyOrganizationQuotaRequestTest.java @@ -36,7 +36,8 @@ void noOrganizationRelationships() { @Test void noOrganizationQuotaId() { - Relationship organizationRelationship = Relationship.builder().id("test-quota").build(); + Relationship organizationRelationship = + Relationship.builder().id("organization-id").build(); ToManyRelationship organizationRelationships = ToManyRelationship.builder().data(organizationRelationship).build(); @@ -50,7 +51,8 @@ void noOrganizationQuotaId() { @Test void valid() { - Relationship organizationRelationship = Relationship.builder().id("test-quota").build(); + Relationship organizationRelationship = + Relationship.builder().id("organization-id").build(); ToManyRelationship organizationRelationships = ToManyRelationship.builder().data(organizationRelationship).build(); ApplyOrganizationQuotaRequest.builder() diff --git a/cloudfoundry-client/src/test/java/org/cloudfoundry/client/v3/quotas/spaces/ApplySpaceQuotaRequestTest.java b/cloudfoundry-client/src/test/java/org/cloudfoundry/client/v3/quotas/spaces/ApplySpaceQuotaRequestTest.java new file mode 100644 index 00000000000..63b8b8061d0 --- /dev/null +++ b/cloudfoundry-client/src/test/java/org/cloudfoundry/client/v3/quotas/spaces/ApplySpaceQuotaRequestTest.java @@ -0,0 +1,58 @@ +/* + * Copyright 2013-2025 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.client.v3.quotas.spaces; + +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.cloudfoundry.client.v3.Relationship; +import org.cloudfoundry.client.v3.ToManyRelationship; +import org.junit.jupiter.api.Test; + +final class ApplySpaceQuotaRequestTest { + + @Test + void noSpaceRelationships() { + assertThrows( + IllegalStateException.class, + () -> ApplySpaceQuotaRequest.builder().spaceQuotaId("quota-id").build()); + } + + @Test + void noSpaceQuotaId() { + Relationship spaceRelationship = Relationship.builder().id("space-id").build(); + ToManyRelationship spaceRelationships = + ToManyRelationship.builder().data(spaceRelationship).build(); + + assertThrows( + IllegalStateException.class, + () -> + ApplySpaceQuotaRequest.builder() + .spaceRelationships(spaceRelationships) + .build()); + } + + @Test + void valid() { + Relationship spaceRelationship = Relationship.builder().id("space-id").build(); + ToManyRelationship spaceRelationships = + ToManyRelationship.builder().data(spaceRelationship).build(); + ApplySpaceQuotaRequest.builder() + .spaceQuotaId("quota-id") + .spaceRelationships(spaceRelationships) + .build(); + } +} diff --git a/cloudfoundry-client/src/test/java/org/cloudfoundry/client/v3/quotas/spaces/RemoveSpaceQuotaRequestTest.java b/cloudfoundry-client/src/test/java/org/cloudfoundry/client/v3/quotas/spaces/RemoveSpaceQuotaRequestTest.java new file mode 100644 index 00000000000..64f8490b07f --- /dev/null +++ b/cloudfoundry-client/src/test/java/org/cloudfoundry/client/v3/quotas/spaces/RemoveSpaceQuotaRequestTest.java @@ -0,0 +1,43 @@ +/* + * Copyright 2013-2025 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.client.v3.quotas.spaces; + +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.Test; + +final class RemoveSpaceQuotaRequestTest { + + @Test + void noSpaceId() { + assertThrows( + IllegalStateException.class, + () -> RemoveSpaceQuotaRequest.builder().spaceQuotaId("quota-id").build()); + } + + @Test + void noSpaceQuotaId() { + assertThrows( + IllegalStateException.class, + () -> RemoveSpaceQuotaRequest.builder().spaceId("space-guid").build()); + } + + @Test + void valid() { + RemoveSpaceQuotaRequest.builder().spaceQuotaId("quota-id").spaceId("space-guid").build(); + } +} diff --git a/pom.xml b/pom.xml index e8bd2cb83a1..1368389e11e 100644 --- a/pom.xml +++ b/pom.xml @@ -247,26 +247,26 @@ - - org.apache.maven.plugins - maven-gpg-plugin - 3.2.7 - - - sign-artifacts - verify - - sign - - - - --pinentry-mode - loopback - - - - - + + + + + + + + + + + + + + + + + + + + com.diffplug.spotless spotless-maven-plugin From 5d5b8d5e93d52ebc629ebdb971af103355428198 Mon Sep 17 00:00:00 2001 From: "Merchant, Saifuddin A" Date: Tue, 16 Dec 2025 12:28:18 -0700 Subject: [PATCH 3/6] Implement integration test for apply and remove space quota --- .../client/v3/SpaceQuotasTest.java | 113 +++++++++++++++++- 1 file changed, 107 insertions(+), 6 deletions(-) diff --git a/integration-test/src/test/java/org/cloudfoundry/client/v3/SpaceQuotasTest.java b/integration-test/src/test/java/org/cloudfoundry/client/v3/SpaceQuotasTest.java index ede877a34c5..a36fb49570e 100644 --- a/integration-test/src/test/java/org/cloudfoundry/client/v3/SpaceQuotasTest.java +++ b/integration-test/src/test/java/org/cloudfoundry/client/v3/SpaceQuotasTest.java @@ -19,13 +19,18 @@ import static org.assertj.core.api.Assertions.assertThat; import java.time.Duration; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; import org.cloudfoundry.AbstractIntegrationTest; import org.cloudfoundry.CloudFoundryVersion; import org.cloudfoundry.IfCloudFoundryVersion; import org.cloudfoundry.client.CloudFoundryClient; import org.cloudfoundry.client.v3.organizations.CreateOrganizationRequest; import org.cloudfoundry.client.v3.organizations.Organization; -import org.cloudfoundry.client.v3.quotas.*; +import org.cloudfoundry.client.v3.quotas.Apps; +import org.cloudfoundry.client.v3.quotas.Routes; +import org.cloudfoundry.client.v3.quotas.Services; import org.cloudfoundry.client.v3.quotas.spaces.*; import org.cloudfoundry.client.v3.spaces.CreateSpaceRequest; import org.cloudfoundry.client.v3.spaces.Space; @@ -105,7 +110,7 @@ public void create() { public void createWithSpaceRelationship() { String spaceQuotaName = this.nameFactory.getQuotaDefinitionName(); SpaceQuotaRelationships spaceQuotaRelationships = - createSpaceQuotaRelationships(organizationId, spaceId); + createSpaceQuotaRelationships(organizationId, Collections.singletonList(spaceId)); Apps spaceQuotaAppLimits = Apps.builder() @@ -277,6 +282,72 @@ public void delete() { .verify(Duration.ofMinutes(5)); } + @Test + public void apply() { + String spaceQuotaName = this.nameFactory.getQuotaDefinitionName(); + + Relationship spaceRelationship1 = Relationship.builder().id(spaceId).build(); + ToManyRelationship spaceRelationships = + ToManyRelationship.builder().data(spaceRelationship1).build(); + + createSpaceQuotaId(this.cloudFoundryClient, spaceQuotaName, organizationId) + .flatMap( + spaceQuotaId -> { + ApplySpaceQuotaRequest applySpaceQuotaRequest = + ApplySpaceQuotaRequest.builder() + .spaceQuotaId(spaceQuotaId) + .spaceRelationships(spaceRelationships) + .build(); + return this.cloudFoundryClient + .spaceQuotasV3() + .apply(applySpaceQuotaRequest); + }) + .as(StepVerifier::create) + .consumeNextWith( + applySpaceQuotaResponse -> { + List spaceRelationshipsData = + applySpaceQuotaResponse.spaceRelationships().getData(); + assertThat(spaceRelationshipsData.size()).isEqualTo(1); + assertThat(spaceRelationshipsData.get(0).getId()).isEqualTo(spaceId); + }) + .expectComplete() + .verify(Duration.ofMinutes(5)); + } + + @Test + public void remove() { + String spaceQuotaName = this.nameFactory.getQuotaDefinitionName(); + + // create a space quota in org with "organizationId" that is pre-associated to the space + // with "spaceId" + createSpaceQuotaId( + this.cloudFoundryClient, + spaceQuotaName, + organizationId, + Collections.singletonList(spaceId)) + .flatMap( + spaceQuotaId -> { + RemoveSpaceQuotaRequest removeSpaceQuotaRequest = + RemoveSpaceQuotaRequest.builder() + .spaceQuotaId(spaceQuotaId) + .spaceId(spaceId) + .build(); + return this.cloudFoundryClient + .spaceQuotasV3() + .remove(removeSpaceQuotaRequest); + }) + .thenMany(requestListSpaceQuotas(this.cloudFoundryClient, spaceQuotaName)) + .as(StepVerifier::create) + .consumeNextWith( + spaceQuotaResource -> { + List spaceRelationshipsData = + spaceQuotaResource.getRelationships().getSpaces().getData(); + assertThat(spaceRelationshipsData.size()).isEqualTo(0); + }) + .expectComplete() + .verify(Duration.ofMinutes(5)); + } + private static Organization createOrganization( CloudFoundryClient cloudFoundryClient, String orgName) { return cloudFoundryClient @@ -314,15 +385,20 @@ private static SpaceQuotaRelationships createSpaceQuotaRelationships(String orgG @NotNull private static SpaceQuotaRelationships createSpaceQuotaRelationships( - String orgGuid, String spaceGuid) { + String orgGuid, List spaceGuids) { ToOneRelationship organizationRelationship = ToOneRelationship.builder() .data(Relationship.builder().id(orgGuid).build()) .build(); + + // iterate over spaceGuids to create Relationship objects + List spaceRelationshipsData = + spaceGuids.stream() + .map(spaceGuid -> Relationship.builder().id(spaceGuid).build()) + .collect(Collectors.toList()); + ToManyRelationship spaceRelationships = - ToManyRelationship.builder() - .data(Relationship.builder().id(spaceGuid).build()) - .build(); + ToManyRelationship.builder().data(spaceRelationshipsData).build(); return SpaceQuotaRelationships.builder() .organization(organizationRelationship) .spaces(spaceRelationships) @@ -347,6 +423,31 @@ private static Mono createSpaceQuota( .build()); } + private static Mono createSpaceQuotaId( + CloudFoundryClient cloudFoundryClient, + String spaceQuotaName, + String orgGuid, + List spaceGuids) { + return createSpaceQuota(cloudFoundryClient, spaceQuotaName, orgGuid, spaceGuids) + .map(CreateSpaceQuotaResponse::getId); + } + + private static Mono createSpaceQuota( + CloudFoundryClient cloudFoundryClient, + String spaceQuotaName, + String orgGuid, + List spaceGuids) { + SpaceQuotaRelationships spaceQuotaRelationships = + createSpaceQuotaRelationships(orgGuid, spaceGuids); + return cloudFoundryClient + .spaceQuotasV3() + .create( + CreateSpaceQuotaRequest.builder() + .name(spaceQuotaName) + .relationships(spaceQuotaRelationships) + .build()); + } + private static Flux requestListSpaceQuotas( CloudFoundryClient cloudFoundryClient, String spaceName) { return PaginationUtils.requestClientV3Resources( From 18a5b211f9afdbc42f089e5002f246514979958d Mon Sep 17 00:00:00 2001 From: "Merchant, Saifuddin A" Date: Tue, 16 Dec 2025 12:32:40 -0700 Subject: [PATCH 4/6] Implement integration test for apply and remove space quota --- pom.xml | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/pom.xml b/pom.xml index 1368389e11e..e8bd2cb83a1 100644 --- a/pom.xml +++ b/pom.xml @@ -247,26 +247,26 @@ - - - - - - - - - - - - - - - - - - - - + + org.apache.maven.plugins + maven-gpg-plugin + 3.2.7 + + + sign-artifacts + verify + + sign + + + + --pinentry-mode + loopback + + + + + com.diffplug.spotless spotless-maven-plugin From ff53b6482b457f3356e2b65f9e2e10955d79a1c5 Mon Sep 17 00:00:00 2001 From: "Merchant, Saifuddin A" Date: Wed, 17 Dec 2025 08:30:57 -0700 Subject: [PATCH 5/6] Update comments on request and response for Apply org and spaces --- .../quotas/organizations/_ApplyOrganizationQuotaRequest.java | 2 +- .../quotas/organizations/_ApplyOrganizationQuotaResponse.java | 4 ++++ .../client/v3/quotas/spaces/_ApplySpaceQuotaRequest.java | 2 +- .../client/v3/quotas/spaces/_ApplySpaceQuotaResponse.java | 4 ++++ 4 files changed, 10 insertions(+), 2 deletions(-) diff --git a/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/quotas/organizations/_ApplyOrganizationQuotaRequest.java b/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/quotas/organizations/_ApplyOrganizationQuotaRequest.java index aa83eee67bc..84ca9423cf3 100644 --- a/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/quotas/organizations/_ApplyOrganizationQuotaRequest.java +++ b/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/quotas/organizations/_ApplyOrganizationQuotaRequest.java @@ -20,7 +20,7 @@ abstract class _ApplyOrganizationQuotaRequest { abstract String getOrganizationQuotaId(); /** - * A relationship to the organizations where the quota is applied + * Relationships to the organizations where the quota is applied * Use of JsonUnwrapped to inline the organization relationships as per the API spec */ @JsonUnwrapped diff --git a/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/quotas/organizations/_ApplyOrganizationQuotaResponse.java b/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/quotas/organizations/_ApplyOrganizationQuotaResponse.java index 21850c34f14..eaf61f61a19 100644 --- a/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/quotas/organizations/_ApplyOrganizationQuotaResponse.java +++ b/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/quotas/organizations/_ApplyOrganizationQuotaResponse.java @@ -17,6 +17,10 @@ @Value.Immutable abstract class _ApplyOrganizationQuotaResponse { + /** + * Relationships to the organizations where the quota is applied + * Use of JsonUnwrapped to inline the organization relationships as per the API spec + */ @JsonUnwrapped abstract ToManyRelationship organizationRelationships(); diff --git a/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/quotas/spaces/_ApplySpaceQuotaRequest.java b/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/quotas/spaces/_ApplySpaceQuotaRequest.java index 3ed51600eac..ddb1bb0d0e6 100644 --- a/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/quotas/spaces/_ApplySpaceQuotaRequest.java +++ b/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/quotas/spaces/_ApplySpaceQuotaRequest.java @@ -20,7 +20,7 @@ abstract class _ApplySpaceQuotaRequest { abstract String getSpaceQuotaId(); /** - * A relationship to the spaces where the quota is applied + * Relationships to the spaces where the quota is applied * Use of JsonUnwrapped to inline the space relationships as per the API spec */ @JsonUnwrapped diff --git a/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/quotas/spaces/_ApplySpaceQuotaResponse.java b/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/quotas/spaces/_ApplySpaceQuotaResponse.java index c14ca74046d..fe721048e76 100644 --- a/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/quotas/spaces/_ApplySpaceQuotaResponse.java +++ b/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/quotas/spaces/_ApplySpaceQuotaResponse.java @@ -17,6 +17,10 @@ @Value.Immutable abstract class _ApplySpaceQuotaResponse { + /** + * Relationships to the spaces where the quota is applied + * Use of JsonUnwrapped to inline the space relationships as per the API spec + */ @JsonUnwrapped abstract ToManyRelationship spaceRelationships(); From f62bdb64d68408f9b730c8096387cf6b7c6bd639 Mon Sep 17 00:00:00 2001 From: "Merchant, Saifuddin A" Date: Mon, 22 Dec 2025 15:13:41 -0700 Subject: [PATCH 6/6] Minor consistency improvements in the documentation --- .../quotas/organizations/OrganizationQuotasV3.java | 2 +- .../client/v3/quotas/spaces/SpaceQuotasV3.java | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/quotas/organizations/OrganizationQuotasV3.java b/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/quotas/organizations/OrganizationQuotasV3.java index 44412f7cfde..624e7374b26 100644 --- a/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/quotas/organizations/OrganizationQuotasV3.java +++ b/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/quotas/organizations/OrganizationQuotasV3.java @@ -68,7 +68,7 @@ public interface OrganizationQuotasV3 { Mono delete(DeleteOrganizationQuotaRequest request); /** - * Makes the Apply an Organization Quota to an Organization + * Makes the Apply an Organization Quota to an Organization * request * * @param request the Apply an Organization Quota to an Organization request diff --git a/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/quotas/spaces/SpaceQuotasV3.java b/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/quotas/spaces/SpaceQuotasV3.java index 86f19567791..ae2b00143db 100644 --- a/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/quotas/spaces/SpaceQuotasV3.java +++ b/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/quotas/spaces/SpaceQuotasV3.java @@ -33,7 +33,7 @@ public interface SpaceQuotasV3 { Mono create(CreateSpaceQuotaRequest request); /** - * Makes the Get Space Quota + * Makes the Get Space Quota * request * * @param request the Get Space Quota request @@ -42,7 +42,7 @@ public interface SpaceQuotasV3 { Mono get(GetSpaceQuotaRequest request); /** - * Makes the List all Space Quota + * Makes the List all Space Quota * request * * @param request the List all Space Quotas request @@ -50,7 +50,7 @@ public interface SpaceQuotasV3 { */ Mono list(ListSpaceQuotasRequest request); - /** Makes the Update Space Quota + /** Makes the Update Space Quota * request * * @param request the Update Space Quota request @@ -59,7 +59,7 @@ public interface SpaceQuotasV3 { Mono update(UpdateSpaceQuotaRequest request); /** - * Makes the Delete Space Quota + * Makes the Delete Space Quota * request * * @param request the Delete Space Quota request @@ -68,7 +68,7 @@ public interface SpaceQuotasV3 { Mono delete(DeleteSpaceQuotaRequest request); /** - * Makes the Apply a Space Quota to a Space + * Makes the Apply a Space Quota to a Space * request * * @param request the Apply a Space Quota to a Space request @@ -77,7 +77,7 @@ public interface SpaceQuotasV3 { Mono apply(ApplySpaceQuotaRequest request); /** - * Makes the Remove a Space Quota from a Space + * Makes the Remove a Space Quota from a Space * request * * @param request the Remove a Space Quota from a Space request