Skip to content

Commit

Permalink
feat(codebuild): Add API to list projects (#635)
Browse files Browse the repository at this point in the history
Add a new API to get all projects for specific account and region. This API
iterates over all pages and aggregates the results. The number of projects
per region per account is at most 5000. Each page contains 100 results, so
at most 50 calls will be made to fetch all projects. I was thinking about
returning the nextToken to caller and let caller do pagination. However, I
found out that none of list* APIs igor provides supports pagination. Therefore
I decide to follow the pattern to return all results in one batch. It should
be fine since the number of projects is less than 100 in most cases.

Co-authored-by: Clare Liguori <[email protected]>
  • Loading branch information
Kaixiang-AWS and clareliguori authored Feb 21, 2020
1 parent 1cc618e commit 7970171
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@
import com.amazonaws.services.codebuild.model.BatchGetBuildsRequest;
import com.amazonaws.services.codebuild.model.Build;
import com.amazonaws.services.codebuild.model.BuildArtifacts;
import com.amazonaws.services.codebuild.model.ListProjectsRequest;
import com.amazonaws.services.codebuild.model.ListProjectsResult;
import com.amazonaws.services.codebuild.model.ProjectSortByType;
import com.amazonaws.services.codebuild.model.StartBuildRequest;
import com.amazonaws.services.codebuild.model.StopBuildRequest;
import com.amazonaws.services.securitytoken.AWSSecurityTokenServiceClient;
Expand Down Expand Up @@ -56,6 +59,25 @@ public AwsCodeBuildAccount(String accountId, String assumeRole, String region) {
.build();
}

// The number of projects has an upper limit of 5000 per region per account
// P99 latency could be high if user has more than 1000 projects
public List<String> getProjects() {
List<String> projects = new ArrayList<>();
String nextToken = null;

do {
ListProjectsResult result =
client.listProjects(
new ListProjectsRequest()
.withSortBy(ProjectSortByType.NAME)
.withNextToken(nextToken));
projects.addAll(result.getProjects());
nextToken = result.getNextToken();
} while (nextToken != null);

return projects;
}

public Build startBuild(StartBuildRequest request) {
return client.startBuild(request).getBuild();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@ List<String> getAccounts() {
return awsCodeBuildAccountRepository.getAccountNames();
}

@RequestMapping(value = "/projects/{account}", method = RequestMethod.GET)
List<String> getProjects(@PathVariable String account) {
return awsCodeBuildAccountRepository.getAccount(account).getProjects();
}

@RequestMapping(
value = "/builds/start/{account}",
method = RequestMethod.POST,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ import com.amazonaws.services.codebuild.model.BatchGetBuildsRequest
import com.amazonaws.services.codebuild.model.BatchGetBuildsResult
import com.amazonaws.services.codebuild.model.Build
import com.amazonaws.services.codebuild.model.BuildArtifacts
import com.amazonaws.services.codebuild.model.ListProjectsRequest
import com.amazonaws.services.codebuild.model.ListProjectsResult
import com.amazonaws.services.codebuild.model.ProjectSortByType
import com.amazonaws.services.codebuild.model.StartBuildRequest
import com.amazonaws.services.codebuild.model.StartBuildResult
import spock.lang.Specification
Expand Down Expand Up @@ -145,6 +148,30 @@ class AwsCodeBuildAccountSpec extends Specification {
result.get(1).getName() == "s3://another-bucket/another/path/file.zip"
}

def "getProjects should return all projects in the account"() {
given:
def firstPage = (1..100).collect{ it.toString() }
def secondPage = (101..150).collect{ it.toString() }

when:
def result = awsCodeBuildAccount.getProjects()

then:
1 * client.listProjects(new ListProjectsRequest().withSortBy(ProjectSortByType.NAME)) >> new ListProjectsResult().withProjects(firstPage).withNextToken("nextToken")
1 * client.listProjects(new ListProjectsRequest().withSortBy(ProjectSortByType.NAME).withNextToken("nextToken")) >> new ListProjectsResult().withProjects(secondPage)
result.size() == 150
result == (1..150).collect{ it.toString() }
}

def "getProjects should return empty when no project found"() {
when:
def result = awsCodeBuildAccount.getProjects()

then:
1 * client.listProjects(new ListProjectsRequest().withSortBy(ProjectSortByType.NAME)) >> new ListProjectsResult().withProjects([])
result == []
}

private static StartBuildRequest getStartBuildInput(String projectName) {
return new StartBuildRequest()
.withProjectName(projectName);
Expand Down

0 comments on commit 7970171

Please sign in to comment.