From 1868102808caea75f50410cf5320613a5454667d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pere=20Urb=C3=B3n?= Date: Mon, 22 Aug 2022 15:57:07 +0200 Subject: [PATCH] Fix pagination issue when listing service accounts for confluent cloud (#531) * fix pagination issue when listing service accounts for confluent cloud * cleanup --- .../kafka/topology/api/ccloud/CCloudApi.java | 12 ++- .../kafka/topology/api/mds/Response.java | 16 +++- .../topology/api/ccloud/CCloudApiTest.java | 81 ++++++++++++++++++- 3 files changed, 100 insertions(+), 9 deletions(-) diff --git a/src/main/java/com/purbon/kafka/topology/api/ccloud/CCloudApi.java b/src/main/java/com/purbon/kafka/topology/api/ccloud/CCloudApi.java index 1efd44563..0cad01271 100644 --- a/src/main/java/com/purbon/kafka/topology/api/ccloud/CCloudApi.java +++ b/src/main/java/com/purbon/kafka/topology/api/ccloud/CCloudApi.java @@ -29,9 +29,9 @@ public class CCloudApi { private static final Logger LOGGER = LogManager.getLogger(CCloudApi.class); - private static final String V1_IAM_SERVICE_ACCOUNTS_URL = "/service_accounts"; - private static final String V2_IAM_SERVICE_ACCOUNTS_URL = "/iam/v2/service-accounts"; - private static final String V3_KAFKA_CLUSTER_URL = "/kafka/v3/clusters/"; + static final String V1_IAM_SERVICE_ACCOUNTS_URL = "/service_accounts"; + static final String V2_IAM_SERVICE_ACCOUNTS_URL = "/iam/v2/service-accounts"; + static final String V3_KAFKA_CLUSTER_URL = "/kafka/v3/clusters/"; private JulieHttpClient clusterHttpClient; private JulieHttpClient ccloudApiHttpClient; @@ -157,7 +157,11 @@ private ServiceAccountV1Response getServiceAccountsV1(String url) throws IOExcep private ListServiceAccountResponse getListServiceAccounts(String url, int page_size) throws IOException { - Response r = ccloudApiHttpClient.doGet(String.format("%s?page_size=%d", url, page_size)); + String requestUrl = url; + if (!url.contains("page_token")) { + requestUrl = String.format("%s?page_size=%d", url, page_size); + } + Response r = ccloudApiHttpClient.doGet(requestUrl); return (ListServiceAccountResponse) JSON.toObject(r.getResponseAsString(), ListServiceAccountResponse.class); } diff --git a/src/main/java/com/purbon/kafka/topology/api/mds/Response.java b/src/main/java/com/purbon/kafka/topology/api/mds/Response.java index e349940cd..e8818e212 100644 --- a/src/main/java/com/purbon/kafka/topology/api/mds/Response.java +++ b/src/main/java/com/purbon/kafka/topology/api/mds/Response.java @@ -19,11 +19,19 @@ public class Response { private final int statusCode; private final HttpHeaders headers; + public Response(HttpHeaders headers, int statusCode, Map map, String response) { + this.headers = headers; + this.statusCode = statusCode; + this.map = map; + this.response = response; + } + + public Response(HttpHeaders headers, int statusCode, String response) { + this(headers, statusCode, new HashMap<>(), response); + } + public Response(HttpResponse response) { - this.headers = response.headers(); - this.statusCode = response.statusCode(); - this.response = response.body(); - this.map = new HashMap<>(); + this(response.headers(), response.statusCode(), response.body()); } public Integer getStatus() { diff --git a/src/test/java/com/purbon/kafka/topology/api/ccloud/CCloudApiTest.java b/src/test/java/com/purbon/kafka/topology/api/ccloud/CCloudApiTest.java index 31c55aebe..ec4d0c3eb 100644 --- a/src/test/java/com/purbon/kafka/topology/api/ccloud/CCloudApiTest.java +++ b/src/test/java/com/purbon/kafka/topology/api/ccloud/CCloudApiTest.java @@ -2,19 +2,25 @@ import static com.purbon.kafka.topology.CommandLineInterface.BROKERS_OPTION; import static com.purbon.kafka.topology.Constants.*; +import static com.purbon.kafka.topology.api.ccloud.CCloudApi.V2_IAM_SERVICE_ACCOUNTS_URL; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import com.purbon.kafka.topology.Configuration; +import com.purbon.kafka.topology.api.mds.Response; import com.purbon.kafka.topology.clients.JulieHttpClient; +import com.purbon.kafka.topology.model.cluster.ServiceAccount; import com.purbon.kafka.topology.roles.TopologyAclBinding; import java.io.IOException; +import java.net.http.HttpResponse; import java.util.HashMap; import java.util.Map; import java.util.Optional; import java.util.Properties; +import java.util.Set; + import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -40,14 +46,15 @@ public void before() throws IOException { props.put(CCLOUD_CLUSTER_API_SECRET, "apiSecret"); props.put(CCLOUD_CLOUD_API_KEY, "apiKey"); props.put(CCLOUD_CLOUD_API_SECRET, "apiSecret"); + props.put(CCLOUD_SA_ACCOUNT_QUERY_PAGE_SIZE, 1); config = new Configuration(cliOps, props); - when(httpClient.baseUrl()).thenReturn("http://not.valid:9999"); apiClient = new CCloudApi(httpClient, Optional.of(httpClient), config); } @Test public void testAclCreateMethod() throws IOException { + when(httpClient.baseUrl()).thenReturn("http://not.valid:9999"); TopologyAclBinding binding = new TopologyAclBinding("TOPIC", "foo", "*", "ALL", "User:foo", "LITERAL"); @@ -119,4 +126,76 @@ public void testDeleteServiceAccount() throws IOException { var url = "/iam/v2/service-accounts/User:foo"; verify(httpClient, times(1)).doDelete(url); } + + @Test + public void listServiceAccountsShouldAcceptPage() throws IOException { + + String body01 = "{\n" + + " \"api_version\": \"iam/v2\",\n" + + " \"kind\": \"ServiceAccountList\",\n" + + " \"metadata\": {\n" + + " \"first\": \"https://api.confluent.cloud/iam/v2/service-accounts\",\n" + + " \"last\": \"https://api.confluent.cloud/iam/v2/service-accounts?page_token=bcAOehAY8F16YD84Z1wT\",\n" + + " \"prev\": \"https://api.confluent.cloud/iam/v2/service-accounts?page_token=YIXRY97wWYmwzrax4dld\",\n" + + " \"next\": \"https://api.confluent.cloud/iam/v2/service-accounts?page_token=UvmDWOB1iwfAIBPj6EYb\",\n" + + " \"total_size\": 123\n" + + " },\n" + + " \"data\": [\n" + + " {\n" + + " \"api_version\": \"iam/v2\",\n" + + " \"kind\": \"ServiceAccount\",\n" + + " \"id\": \"dlz-f3a90de\",\n" + + " \"metadata\": {\n" + + " \"self\": \"https://api.confluent.cloud/iam/v2/service-accounts/sa-12345\",\n" + + " \"resource_name\": \"crn://confluent.cloud/service-account=sa-12345\",\n" + + " \"created_at\": \"2006-01-02T15:04:05-07:00\",\n" + + " \"updated_at\": \"2006-01-02T15:04:05-07:00\",\n" + + " \"deleted_at\": \"2006-01-02T15:04:05-07:00\"\n" + + " },\n" + + " \"display_name\": \"DeLorean_auto_repair\",\n" + + " \"description\": \"Doc's repair bot for the DeLorean\"\n" + + " }\n" + + " ]\n" + + "}"; + + Response response01 = new Response(null, 200, body01); + + when(httpClient.doGet(String.format("%s?page_size=%d", V2_IAM_SERVICE_ACCOUNTS_URL, 1))) + .thenReturn(response01); + + String body02 = "{\n" + + " \"api_version\": \"iam/v2\",\n" + + " \"kind\": \"ServiceAccountList\",\n" + + " \"metadata\": {\n" + + " \"first\": \"https://api.confluent.cloud/iam/v2/service-accounts\",\n" + + " \"last\": \"https://api.confluent.cloud/iam/v2/service-accounts?page_token=bcAOehAY8F16YD84Z1wT\",\n" + + " \"prev\": \"https://api.confluent.cloud/iam/v2/service-accounts?page_token=YIXRY97wWYmwzrax4dld\",\n" + + " \"total_size\": 123\n" + + " },\n" + + " \"data\": [\n" + + " {\n" + + " \"api_version\": \"iam/v2\",\n" + + " \"kind\": \"ServiceAccount\",\n" + + " \"id\": \"abc-f3a90de\",\n" + + " \"metadata\": {\n" + + " \"self\": \"https://api.confluent.cloud/iam/v2/service-accounts/sa-12345\",\n" + + " \"resource_name\": \"crn://confluent.cloud/service-account=sa-12345\",\n" + + " \"created_at\": \"2006-01-02T15:04:05-07:00\",\n" + + " \"updated_at\": \"2006-01-02T15:04:05-07:00\",\n" + + " \"deleted_at\": \"2006-01-02T15:04:05-07:00\"\n" + + " },\n" + + " \"display_name\": \"MacFly\",\n" + + " \"description\": \"Doc's repair bot for the MacFly\"\n" + + " }\n" + + " ]\n" + + "}"; + Response response02 = new Response(null, 200, body02); + + + when(httpClient.doGet("/iam/v2/service-accounts?page_token=UvmDWOB1iwfAIBPj6EYb")) + .thenReturn(response02); + + Set accounts = apiClient.listServiceAccounts(); + assertThat(accounts).hasSize(2); + } }