Skip to content

Commit

Permalink
Fix pagination issue when listing service accounts for confluent cloud (
Browse files Browse the repository at this point in the history
#531)

* fix pagination issue when listing service accounts for confluent cloud

* cleanup
  • Loading branch information
purbon committed Aug 22, 2022
1 parent b896cf7 commit 1868102
Show file tree
Hide file tree
Showing 3 changed files with 100 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
}
Expand Down
16 changes: 12 additions & 4 deletions src/main/java/com/purbon/kafka/topology/api/mds/Response.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,19 @@ public class Response {
private final int statusCode;
private final HttpHeaders headers;

public Response(HttpHeaders headers, int statusCode, Map<String, Object> 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<String> 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() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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");
Expand Down Expand Up @@ -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<ServiceAccount> accounts = apiClient.listServiceAccounts();
assertThat(accounts).hasSize(2);
}
}

0 comments on commit 1868102

Please sign in to comment.