Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ plugins {
kotlin("jvm") version "2.1.0"
kotlin("plugin.serialization") version "1.9.10"
id("com.apollographql.apollo3") version "3.8.2"
id("com.diffplug.spotless") version "7.0.2"
}

group = "com.ziro.engineering"
Expand Down Expand Up @@ -60,6 +61,17 @@ apollo {
}
}

spotless {
kotlin {
ktfmt().configure {
it.setBlockIndent(4)
it.setContinuationIndent(4)
it.setRemoveUnusedImports(true)
}
targetExclude("build/**")
}
}

tasks.test {
useJUnitPlatform()
}
Expand Down
16 changes: 9 additions & 7 deletions src/main/kotlin/github/GitHubClient.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,10 @@ import com.apollographql.apollo3.api.Optional
import com.ziro.engineering.github.graphql.sdk.GetBranchLogHistoryQuery
import com.ziro.engineering.github.graphql.sdk.GetFileFromBranchQuery
import com.ziro.engineering.github.graphql.sdk.RepositoryQuery
import kotlin.math.min
import kotlinx.coroutines.flow.single
import kotlinx.coroutines.runBlocking
import okhttp3.internal.closeQuietly
import kotlin.math.ceil
import kotlin.math.min

const val MAX_COMMITS_IN_PAGE = 100

Expand All @@ -20,7 +19,8 @@ private const val GITHUB_GRAPHQL_URL = "https://api.github.com/graphql"
class GitHubClient : AutoCloseable {

private val apolloClient: ApolloClient =
ApolloClient.Builder().serverUrl(GITHUB_GRAPHQL_URL)
ApolloClient.Builder()
.serverUrl(GITHUB_GRAPHQL_URL)
.addHttpHeader("Authorization", "Bearer ${System.getenv("GITHUB_API_TOKEN")}")
.build()

Expand Down Expand Up @@ -55,12 +55,14 @@ class GitHubClient : AutoCloseable {

while (commitsLeft > 0 && hasNextPage) {
val numCommitsInPage = min(MAX_COMMITS_IN_PAGE, commitsLeft)
val query = GetBranchLogHistoryQuery(repoOwner, repoName, branch, numCommitsInPage, cursor)
val query =
GetBranchLogHistoryQuery(repoOwner, repoName, branch, numCommitsInPage, cursor)
val response = apolloClient.query(query).toFlow().single()

commits.addAll(response.data?.repository?.ref?.target?.onCommit?.history?.edges?.mapNotNull {
it?.node?.message
} ?: emptyList())
commits.addAll(
response.data?.repository?.ref?.target?.onCommit?.history?.edges?.mapNotNull {
it?.node?.message
} ?: emptyList())

response.data?.repository?.ref?.target?.onCommit?.history?.pageInfo?.let {
cursor = Optional.presentIfNotNull(it.endCursor)
Expand Down
1 change: 0 additions & 1 deletion src/main/kotlin/github/GitHubClientSmokeTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,3 @@ fun main() {
}
}
}

2 changes: 1 addition & 1 deletion src/main/kotlin/zenhub/Pipeline.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ package zenhub
enum class Pipeline(val id: String) {
MERGE_READY("Z2lkOi8vcmFwdG9yL1BpcGVsaW5lLzEwMTIwODA"),
UAT_READY("Z2lkOi8vcmFwdG9yL1BpcGVsaW5lLzEwMTIxNzI")
}
}
116 changes: 72 additions & 44 deletions src/main/kotlin/zenhub/ZenHubClient.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,16 @@ import com.apollographql.apollo3.ApolloClient
import com.apollographql.apollo3.api.Optional
import com.ziro.engineering.zenhub.graphql.sdk.*
import com.ziro.engineering.zenhub.graphql.sdk.type.*
import java.time.Instant
import kotlinx.coroutines.flow.single
import kotlinx.coroutines.runBlocking
import okhttp3.internal.closeQuietly
import java.time.Instant

/**
* Default GitHub Repository ID - references the SMACS repository.
*/
/** Default GitHub Repository ID - references the SMACS repository. */
const val DEFAULT_GITHUB_REPOSITORY_ID: Int = 15617306
const val DEFAULT_GIT_REPOSITORY_ID: String = "Z2lkOi8vcmFwdG9yL1JlcG9zaXRvcnkvMjEwNTg"

/**
* Default Workspace ID - references the "Engineering Team" workspace.
*/
/** Default Workspace ID - references the "Engineering Team" workspace. */
private const val DEFAULT_WORKSPACE_ID = "59c54eb49d9e774e473597f1"
private const val ZENHUB_GRAPHQL_URL = "https://api.zenhub.com/public/graphql"

Expand All @@ -27,14 +23,21 @@ class ZenHubClient(
private val zenhubWorkspaceId: String = DEFAULT_WORKSPACE_ID
) : AutoCloseable {

private val apolloClient: ApolloClient = ApolloClient.Builder().serverUrl(ZENHUB_GRAPHQL_URL)
.addHttpHeader("Authorization", "Bearer ${System.getenv("ZENHUB_GRAPHQL_TOKEN")}").build()
private val apolloClient: ApolloClient =
ApolloClient.Builder()
.serverUrl(ZENHUB_GRAPHQL_URL)
.addHttpHeader("Authorization", "Bearer ${System.getenv("ZENHUB_GRAPHQL_TOKEN")}")
.build()

fun searchClosedIssuesBetween(startTime: Instant, endTime: Instant): List<SearchClosedIssuesQuery.Node> {
fun searchClosedIssuesBetween(
startTime: Instant,
endTime: Instant
): List<SearchClosedIssuesQuery.Node> {
val results = ArrayList<SearchClosedIssuesQuery.Node>()
val issueOnlyFilter = IssueSearchFiltersInput(
displayType = Optional.present(DisplayFilter.issues),
)
val issueOnlyFilter =
IssueSearchFiltersInput(
displayType = Optional.present(DisplayFilter.issues),
)
var earliestClosedDate: Instant
var cursor: String? = null

Expand All @@ -49,11 +52,14 @@ class ZenHubClient(
}

fun getCurrentSprint(): GetSprintsByStateQuery.Node? = runBlocking {
val results = getSprintByState(
SprintFiltersInput(Optional.present(SprintStateInput(SprintState.OPEN)), Optional.absent()),
1,
SprintOrderInput(Optional.present(OrderDirection.ASC), Optional.present(SprintOrderField.END_AT))
)
val results =
getSprintByState(
SprintFiltersInput(
Optional.present(SprintStateInput(SprintState.OPEN)), Optional.absent()),
1,
SprintOrderInput(
Optional.present(OrderDirection.ASC),
Optional.present(SprintOrderField.END_AT)))
if (results.isNullOrEmpty()) {
null
} else {
Expand All @@ -62,19 +68,26 @@ class ZenHubClient(
}

fun getPreviousSprint(): GetSprintsByStateQuery.Node? = runBlocking {
val results = getSprintByState(
SprintFiltersInput(Optional.present(SprintStateInput(SprintState.CLOSED)), Optional.absent()),
1,
SprintOrderInput(Optional.present(OrderDirection.DESC), Optional.present(SprintOrderField.END_AT))
)
val results =
getSprintByState(
SprintFiltersInput(
Optional.present(SprintStateInput(SprintState.CLOSED)), Optional.absent()),
1,
SprintOrderInput(
Optional.present(OrderDirection.DESC),
Optional.present(SprintOrderField.END_AT)))
if (results.isNullOrEmpty()) {
null
} else {
results[0]
}
}

fun issueByInfo(githubRepoId: Int, gitRepoId: String, issueNumber: Int): IssueByInfoQuery.IssueByInfo? = runBlocking {
fun issueByInfo(
githubRepoId: Int,
gitRepoId: String,
issueNumber: Int
): IssueByInfoQuery.IssueByInfo? = runBlocking {
val query = IssueByInfoQuery(githubRepoId, gitRepoId, issueNumber)
apolloClient.query(query).toFlow().single().data?.issueByInfo
}
Expand All @@ -99,28 +112,35 @@ class ZenHubClient(

fun getIssuesByPipeline(pipeline: Pipeline): List<GetIssuesByPipelineQuery.Node> = runBlocking {
val query = GetIssuesByPipelineQuery(pipeline.id)
apolloClient.query(query).toFlow().single().data?.searchIssuesByPipeline?.nodes ?: emptyList()
apolloClient.query(query).toFlow().single().data?.searchIssuesByPipeline?.nodes
?: emptyList()
}

fun getReleases(githubRepoId: Int): List<GetReleasesQuery.Node> = runBlocking {
val query = GetReleasesQuery(githubRepoId)
apolloClient.query(query).toFlow().single().data?.repositoriesByGhId?.get(0)?.releases?.nodes
?: emptyList()
apolloClient
.query(query)
.toFlow()
.single()
.data
?.repositoriesByGhId
?.get(0)
?.releases
?.nodes ?: emptyList()
}

fun getSprints(workspaceId: String): List<GetSprintsQuery.Node> = runBlocking {
val query = GetSprintsQuery(workspaceId)
apolloClient.query(query).toFlow().single().data?.workspace?.sprints?.nodes ?: emptyList()
}

/**
* Cannot move an issue to closed because closed is not a pipeline.
*/
fun moveIssueToPipeline(issueId: String, pipeline: Pipeline): MoveIssueMutation.MoveIssue? = runBlocking {
val input = MoveIssueInput(Optional.absent(), pipeline.id, issueId, Optional.present(0))
val mutation = MoveIssueMutation(input, DEFAULT_WORKSPACE_ID)
apolloClient.mutation(mutation).toFlow().single().data?.moveIssue
}
/** Cannot move an issue to closed because closed is not a pipeline. */
fun moveIssueToPipeline(issueId: String, pipeline: Pipeline): MoveIssueMutation.MoveIssue? =
runBlocking {
val input = MoveIssueInput(Optional.absent(), pipeline.id, issueId, Optional.present(0))
val mutation = MoveIssueMutation(input, DEFAULT_WORKSPACE_ID)
apolloClient.mutation(mutation).toFlow().single().data?.moveIssue
}

fun closeIssues(issueIds: List<String>): CloseIssuesMutation.CloseIssues? = runBlocking {
val mutation = CloseIssuesMutation(issueIds)
Expand All @@ -136,12 +156,18 @@ class ZenHubClient(
firstSprints: Int,
orderSprintsBy: SprintOrderInput
): List<GetSprintsByStateQuery.Node>? = runBlocking {
val query = GetSprintsByStateQuery(zenhubWorkspaceId, sprintFilters, firstSprints, orderSprintsBy)
val query =
GetSprintsByStateQuery(zenhubWorkspaceId, sprintFilters, firstSprints, orderSprintsBy)
apolloClient.query(query).toFlow().single().data?.workspace?.sprints?.nodes
}

private fun searchClosedIssues(filters: IssueSearchFiltersInput, after: String?): SearchClosedIssuesQuery.SearchClosedIssues? = runBlocking {
val query = SearchClosedIssuesQuery(zenhubWorkspaceId, filters, Optional.present(100), Optional.presentIfNotNull(after))
private fun searchClosedIssues(
filters: IssueSearchFiltersInput,
after: String?
): SearchClosedIssuesQuery.SearchClosedIssues? = runBlocking {
val query =
SearchClosedIssuesQuery(
zenhubWorkspaceId, filters, Optional.present(100), Optional.presentIfNotNull(after))
apolloClient.query(query).toFlow().single().data?.searchClosedIssues
}

Expand All @@ -155,15 +181,17 @@ class ZenHubClient(
return results
}

val indexOfEarliestIssue = results.indexOfFirst { issue ->
Instant.parse(issue.closedAt.toString()).isBefore(startDate)
}
val indexOfEarliestIssue =
results.indexOfFirst { issue ->
Instant.parse(issue.closedAt.toString()).isBefore(startDate)
}

var indexOfLatestIssue = -1
if (Instant.parse(results[0].closedAt.toString()).isAfter(endDate)) {
indexOfLatestIssue = results.indexOfLast { issue ->
Instant.parse(issue.closedAt.toString()).isAfter(endDate)
}
indexOfLatestIssue =
results.indexOfLast { issue ->
Instant.parse(issue.closedAt.toString()).isAfter(endDate)
}
}

return results.subList(indexOfLatestIssue + 1, indexOfEarliestIssue)
Expand Down
9 changes: 4 additions & 5 deletions src/main/kotlin/zenhub/ZenHubClientSmokeTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,14 @@ import java.time.Instant

fun main() {
ZenHubClient().use { client ->
val issue = client.issueByInfo(DEFAULT_GITHUB_REPOSITORY_ID, DEFAULT_GIT_REPOSITORY_ID, 15675)
val issue =
client.issueByInfo(DEFAULT_GITHUB_REPOSITORY_ID, DEFAULT_GIT_REPOSITORY_ID, 15675)
if (issue != null) {
println("id: ${issue.id}")
}
val fourteenDays = Duration.ofDays(28)
val response = client.searchClosedIssuesBetween(
Instant.now().minus(fourteenDays),
Instant.now()
)
val response =
client.searchClosedIssuesBetween(Instant.now().minus(fourteenDays), Instant.now())
println("Results Size: ${response.size}")

response.forEach { node -> println(node) }
Expand Down
14 changes: 9 additions & 5 deletions src/test/kotlin/github/GitHubClientTest.kt
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package github

import org.junit.jupiter.api.Test
import kotlin.test.assertEquals
import org.junit.jupiter.api.Test

class GitHubClientTest {
private val gitHubClient = GitHubClient()
Expand All @@ -14,7 +14,8 @@ class GitHubClientTest {

@Test
fun whenGetCommitsWithLess1PageThenCorrectAmountIsReturned() {
val result = gitHubClient.getCommits(branch = "develop", numCommits = MAX_COMMITS_IN_PAGE / 10)
val result =
gitHubClient.getCommits(branch = "develop", numCommits = MAX_COMMITS_IN_PAGE / 10)
assertEquals(MAX_COMMITS_IN_PAGE / 10, result.size)
}

Expand All @@ -26,19 +27,22 @@ class GitHubClientTest {

@Test
fun whenGetCommitsWithMoreThan1PageThenCorrectAmountIsReturned() {
val result = gitHubClient.getCommits(branch = "develop", numCommits = MAX_COMMITS_IN_PAGE * 2)
val result =
gitHubClient.getCommits(branch = "develop", numCommits = MAX_COMMITS_IN_PAGE * 2)
assertEquals(MAX_COMMITS_IN_PAGE * 2, result.size)
}

@Test
fun whenGetCommitsWith1PageAndLeftoverThenCorrectAmountIsReturned() {
val result = gitHubClient.getCommits(branch = "develop", numCommits = MAX_COMMITS_IN_PAGE * 2 - 1)
val result =
gitHubClient.getCommits(branch = "develop", numCommits = MAX_COMMITS_IN_PAGE * 2 - 1)
assertEquals(MAX_COMMITS_IN_PAGE * 2 - 1, result.size)
}

@Test
fun whenGetCommitsOnBadBranchThenThereAreNoCommits() {
val result = gitHubClient.getCommits(branch = "bad-branch", numCommits = MAX_COMMITS_IN_PAGE)
val result =
gitHubClient.getCommits(branch = "bad-branch", numCommits = MAX_COMMITS_IN_PAGE)
assertEquals(0, result.size)
}
}
6 changes: 3 additions & 3 deletions src/test/kotlin/zenhub/ZenHubClientTest.kt
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
package zenhub

import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertAll
import kotlin.test.assertEquals
import kotlin.test.assertNotNull
import kotlin.test.assertTrue
import org.junit.jupiter.api.Test

class ZenHubClientTest {
private val zenHubClient = ZenHubClient()

@Test
fun whenIssueByInfoThenCorrectIssueIsReturned() {
val issue = zenHubClient.issueByInfo(DEFAULT_GITHUB_REPOSITORY_ID, DEFAULT_GIT_REPOSITORY_ID, 18004)
val issue =
zenHubClient.issueByInfo(DEFAULT_GITHUB_REPOSITORY_ID, DEFAULT_GIT_REPOSITORY_ID, 18004)
assertEquals(18004, issue?.number)
}

Expand Down