Skip to content

Commit

Permalink
Adding API endpoint for getting current repo coverage
Browse files Browse the repository at this point in the history
  • Loading branch information
craigatk committed Mar 29, 2024
1 parent 02efa48 commit 89d24c4
Show file tree
Hide file tree
Showing 15 changed files with 339 additions and 89 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package projektor.server.api.repository

data class BranchSearch(val branchName: String? = null, val branchType: BranchType? = null)
2 changes: 1 addition & 1 deletion server/server-app/src/main/kotlin/projektor/AppModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ fun createAppModule(

single { OrganizationCoverageService(get(), get()) }

single { RepositoryCoverageService(get()) }
single { RepositoryCoverageService(get(), get(), get()) }
single { RepositoryPerformanceService(get()) }
single { RepositoryTestRunService(get()) }

Expand Down
25 changes: 3 additions & 22 deletions server/server-app/src/main/kotlin/projektor/Application.kt
Original file line number Diff line number Diff line change
Expand Up @@ -63,28 +63,8 @@ import projektor.quality.CodeQualityReportRepository
import projektor.repository.coverage.RepositoryCoverageService
import projektor.repository.performance.RepositoryPerformanceService
import projektor.repository.testrun.RepositoryTestRunService
import projektor.route.*
import projektor.route.attachments
import projektor.route.badge
import projektor.route.codeQuality
import projektor.route.config
import projektor.route.coverage
import projektor.route.failure
import projektor.route.health
import projektor.route.messages
import projektor.route.metadata
import projektor.route.organization
import projektor.route.performance
import projektor.route.previousRuns
import projektor.route.repository
import projektor.route.repositoryCoverage
import projektor.route.repositoryPerformance
import projektor.route.results
import projektor.route.testCases
import projektor.route.testRunSystemAttributes
import projektor.route.testRuns
import projektor.route.testSuites
import projektor.route.ui
import projektor.route.version
import projektor.schedule.Scheduler
import projektor.telemetry.OpenTelemetryRoute
import projektor.testcase.TestCaseService
Expand Down Expand Up @@ -216,6 +196,7 @@ fun Application.main(meterRegistry: MeterRegistry? = null) {
val codeQualityReportRepository: CodeQualityReportRepository by inject()

routing {
api(repositoryCoverageService)
attachments(attachmentService, authService)
badge(coverageBadgeService)
codeQuality(codeQualityReportRepository)
Expand All @@ -229,7 +210,7 @@ fun Application.main(meterRegistry: MeterRegistry? = null) {
performance(performanceResultsService)
previousRuns(previousTestRunService)
repository(repositoryTestRunService)
repositoryCoverage(coverageService, previousTestRunService, repositoryCoverageService)
repositoryCoverage(repositoryCoverageService)
repositoryPerformance(repositoryPerformanceService)
results(testResultsService, groupedTestResultsService, testResultsProcessingService, authService, metricRegistry, metricsService)
testCases(testCaseService)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import projektor.compare.PreviousTestRunService
import projektor.coverage.CoverageService
import projektor.notification.badge.SvgCoverageBadgeCreator
import projektor.server.api.PublicId
import projektor.server.api.repository.BranchSearch
import projektor.server.api.repository.BranchType

class CoverageBadgeService(
Expand All @@ -14,13 +15,13 @@ class CoverageBadgeService(
suspend fun createCoverageBadge(fullRepoName: String, projectName: String?): String? {

val mostRecentRunWithCoverage = previousTestRunService.findMostRecentRunWithCoverage(
BranchType.MAINLINE,
fullRepoName,
projectName
projectName,
BranchSearch(branchType = BranchType.MAINLINE)
) ?: previousTestRunService.findMostRecentRunWithCoverage(
BranchType.ALL,
fullRepoName,
projectName
projectName,
BranchSearch(branchType = BranchType.ALL)
)

val coveredPercentage = mostRecentRunWithCoverage?.publicId?.let { publicId ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@ import org.jooq.DSLContext
import projektor.database.generated.Tables.CODE_COVERAGE_RUN
import projektor.database.generated.Tables.GIT_METADATA
import projektor.database.generated.Tables.TEST_RUN
import projektor.repository.testrun.RepositoryTestRunDatabaseRepository.Companion.withBranchName
import projektor.repository.testrun.RepositoryTestRunDatabaseRepository.Companion.withBranchType
import projektor.repository.testrun.RepositoryTestRunDatabaseRepository.Companion.withProjectName
import projektor.server.api.PublicId
import projektor.server.api.repository.BranchType
import projektor.server.api.repository.BranchSearch
import java.time.LocalDateTime
import java.time.ZoneOffset

Expand All @@ -35,12 +37,7 @@ class PreviousTestRunDatabaseRepository(private val dslContext: DSLContext) : Pr
GIT_METADATA.IS_MAIN_BRANCH.eq(true)
.and(TEST_RUN.PUBLIC_ID.ne(publicId.id))
.and(GIT_METADATA.REPO_NAME.eq(currentRunInfo.repoName))
.let {
if (currentRunInfo.projectName == null)
it.and(GIT_METADATA.PROJECT_NAME.isNull)
else
it.and(GIT_METADATA.PROJECT_NAME.eq(currentRunInfo.projectName))
}
.and(withProjectName(currentRunInfo.projectName))
.and(TEST_RUN.CREATED_TIMESTAMP.lessThan(currentRunInfo.createdTimestamp))
)
.orderBy(TEST_RUN.CREATED_TIMESTAMP.desc().nullsLast())
Expand All @@ -53,21 +50,17 @@ class PreviousTestRunDatabaseRepository(private val dslContext: DSLContext) : Pr
}
}

override suspend fun findMostRecentRunWithCoverage(branchType: BranchType, repoName: String, projectName: String?): RecentTestRun? =
override suspend fun findMostRecentRunWithCoverage(repoName: String, projectName: String?, branch: BranchSearch?): RecentTestRun? =
withContext(Dispatchers.IO) {
val recentTestRun = dslContext.select(TEST_RUN.PUBLIC_ID, TEST_RUN.CREATED_TIMESTAMP)
.from(TEST_RUN)
.innerJoin(GIT_METADATA).on(TEST_RUN.ID.eq(GIT_METADATA.TEST_RUN_ID))
.innerJoin(CODE_COVERAGE_RUN).on(TEST_RUN.PUBLIC_ID.eq(CODE_COVERAGE_RUN.TEST_RUN_PUBLIC_ID))
.where(
GIT_METADATA.REPO_NAME.eq(repoName)
.and(withBranchType(branchType))
.let {
if (projectName == null)
it.and(GIT_METADATA.PROJECT_NAME.isNull)
else
it.and(GIT_METADATA.PROJECT_NAME.eq(projectName))
}
.and(withBranchType(branch?.branchType))
.and(withBranchName(branch?.branchName))
.and(withProjectName(projectName))
)
.orderBy(TEST_RUN.CREATED_TIMESTAMP.desc().nullsLast())
.limit(1)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package projektor.compare

import projektor.server.api.PublicId
import projektor.server.api.repository.BranchType
import projektor.server.api.repository.BranchSearch

interface PreviousTestRunRepository {
suspend fun findPreviousMainBranchRunWithCoverage(publicId: PublicId): PublicId?

suspend fun findMostRecentRunWithCoverage(branchType: BranchType, repoName: String, projectName: String?): RecentTestRun?
suspend fun findMostRecentRunWithCoverage(repoName: String, projectName: String?, branch: BranchSearch?): RecentTestRun?
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
package projektor.compare

import projektor.server.api.PublicId
import projektor.server.api.repository.BranchType
import projektor.server.api.repository.BranchSearch

class PreviousTestRunService(private val previousTestRunRepository: PreviousTestRunRepository) {
suspend fun findPreviousMainBranchRunWithCoverage(publicId: PublicId): PublicId? =
previousTestRunRepository.findPreviousMainBranchRunWithCoverage(publicId)

suspend fun findMostRecentRunWithCoverage(branchType: BranchType, repoName: String, projectName: String?): RecentTestRun? =
previousTestRunRepository.findMostRecentRunWithCoverage(branchType, repoName, projectName)
suspend fun findMostRecentRunWithCoverage(repoName: String, projectName: String?, branch: BranchSearch?): RecentTestRun? =
previousTestRunRepository.findMostRecentRunWithCoverage(repoName, projectName, branch)
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import projektor.database.generated.Tables.GIT_METADATA
import projektor.database.generated.Tables.TEST_RUN
import projektor.parser.coverage.model.CoverageReportStats
import projektor.repository.testrun.RepositoryTestRunDatabaseRepository.Companion.withBranchType
import projektor.repository.testrun.RepositoryTestRunDatabaseRepository.Companion.withProjectName
import projektor.server.api.repository.BranchType
import projektor.server.api.repository.coverage.RepositoryCoverageTimeline
import projektor.server.api.repository.coverage.RepositoryCoverageTimelineEntry
Expand Down Expand Up @@ -43,12 +44,7 @@ class RepositoryCoverageDatabaseRepository(private val dslContext: DSLContext) :
.where(
GIT_METADATA.REPO_NAME.eq(repoName)
.and(withBranchType(branchType))
.let {
if (projectName == null)
it.and(GIT_METADATA.PROJECT_NAME.isNull)
else
it.and(GIT_METADATA.PROJECT_NAME.eq(projectName))
}
.and(withProjectName(projectName))
.and(CODE_COVERAGE_STATS.SCOPE.eq("GROUP"))
)
.groupBy(TEST_RUN.PUBLIC_ID, TEST_RUN.CREATED_TIMESTAMP)
Expand All @@ -72,12 +68,7 @@ class RepositoryCoverageDatabaseRepository(private val dslContext: DSLContext) :
.innerJoin(CODE_COVERAGE_STATS).on(CODE_COVERAGE_STATS.CODE_COVERAGE_RUN_ID.eq(CODE_COVERAGE_RUN.ID))
.where(
GIT_METADATA.REPO_NAME.eq(repoName)
.let {
if (projectName == null)
it.and(GIT_METADATA.PROJECT_NAME.isNull)
else
it.and(GIT_METADATA.PROJECT_NAME.eq(projectName))
}
.and(withProjectName(projectName))
.and(CODE_COVERAGE_STATS.SCOPE.eq("GROUP"))
)
)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,40 @@
package projektor.repository.coverage

import projektor.compare.PreviousTestRunService
import projektor.coverage.CoverageService
import projektor.server.api.repository.BranchSearch
import projektor.server.api.repository.BranchType
import projektor.server.api.repository.coverage.RepositoryCoverageTimeline
import projektor.server.api.repository.coverage.RepositoryCurrentCoverage

class RepositoryCoverageService(private val repositoryCoverageRepository: RepositoryCoverageRepository) {
class RepositoryCoverageService(
private val coverageService: CoverageService,
private val previousTestRunService: PreviousTestRunService,
private val repositoryCoverageRepository: RepositoryCoverageRepository
) {
suspend fun fetchRepositoryCoverageTimeline(branchType: BranchType, repoName: String, projectName: String?): RepositoryCoverageTimeline? =
repositoryCoverageRepository.fetchRepositoryCoverageTimeline(branchType, repoName, projectName)

suspend fun fetchRepositoryCurrentCoverage(repoName: String, projectName: String?, branchSearch: BranchSearch?): RepositoryCurrentCoverage? {
val mostRecentRunWithCoverage = previousTestRunService.findMostRecentRunWithCoverage(
repoName,
projectName,
branchSearch
)

val coveredPercentage = mostRecentRunWithCoverage?.publicId?.let { publicId -> coverageService.getCoveredLinePercentage(publicId) }

return if (mostRecentRunWithCoverage != null && coveredPercentage != null) {
RepositoryCurrentCoverage(
id = mostRecentRunWithCoverage.publicId.id,
coveredPercentage = coveredPercentage,
createdTimestamp = mostRecentRunWithCoverage.createdTimestamp
)
} else {
null
}
}

suspend fun coverageExists(repoName: String, projectName: String?): Boolean =
repositoryCoverageRepository.coverageExists(repoName, projectName)
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import org.simpleflatmapper.jdbc.JdbcMapperFactory
import projektor.database.generated.Tables.GIT_METADATA
import projektor.database.generated.Tables.PERFORMANCE_RESULTS
import projektor.database.generated.Tables.TEST_RUN
import projektor.repository.testrun.RepositoryTestRunDatabaseRepository.Companion.withProjectName
import projektor.server.api.repository.performance.RepositoryPerformanceTestTimelineEntry
import kotlin.streams.toList

Expand All @@ -33,12 +34,7 @@ class RepositoryPerformanceDatabaseRepository(private val dslContext: DSLContext
.innerJoin(GIT_METADATA).on(TEST_RUN.ID.eq(GIT_METADATA.TEST_RUN_ID))
.where(
GIT_METADATA.REPO_NAME.eq(repoName)
.let {
if (projectName == null)
it.and(GIT_METADATA.PROJECT_NAME.isNull)
else
it.and(GIT_METADATA.PROJECT_NAME.eq(projectName))
}
.and(withProjectName(projectName))
)
.orderBy(TEST_RUN.CREATED_TIMESTAMP.asc())
.fetchResultSet()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,17 +119,27 @@ class RepositoryTestRunDatabaseRepository(private val dslContext: DSLContext) :

companion object {
fun runInCIFromRepo(repoName: String, projectName: String?): Condition =
GIT_METADATA.REPO_NAME.eq(repoName).let {
if (projectName == null)
it.and(GIT_METADATA.PROJECT_NAME.isNull)
else
it.and(GIT_METADATA.PROJECT_NAME.eq(projectName))
}.and(RESULTS_METADATA.CI.eq(true))
GIT_METADATA.REPO_NAME.eq(repoName)
.and(withProjectName(projectName))
.and(RESULTS_METADATA.CI.eq(true))

fun withBranchType(branchType: BranchType): Condition =
fun withBranchType(branchType: BranchType?): Condition =
when (branchType) {
BranchType.MAINLINE -> GIT_METADATA.IS_MAIN_BRANCH.isTrue
else -> noCondition()
}

fun withBranchName(branchName: String?): Condition =
if (branchName != null) {
GIT_METADATA.BRANCH_NAME.eq(branchName)
} else {
noCondition()
}

fun withProjectName(projectName: String?): Condition =
if (projectName == null)
GIT_METADATA.PROJECT_NAME.isNull
else
GIT_METADATA.PROJECT_NAME.eq(projectName)
}
}
31 changes: 31 additions & 0 deletions server/server-app/src/main/kotlin/projektor/route/ApiRoutes.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package projektor.route

import io.ktor.http.*
import io.ktor.server.application.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
import io.ktor.server.util.*
import projektor.repository.coverage.RepositoryCoverageService
import projektor.server.api.repository.BranchSearch

fun Route.api(
repositoryCoverageService: RepositoryCoverageService,
) {
get("/api/repo/{orgPart}/{repoPart}/coverage/current") {
val orgPart = call.parameters.getOrFail("orgPart")
val repoPart = call.parameters.getOrFail("repoPart")
val fullRepoName = "$orgPart/$repoPart"

val projectName = call.request.queryParameters["project"]
val branchName = call.request.queryParameters["branch"]

val repositoryCurrentCoverage = repositoryCoverageService.fetchRepositoryCurrentCoverage(
fullRepoName,
projectName,
BranchSearch(branchName = branchName)
)

repositoryCurrentCoverage?.let { call.respond(HttpStatusCode.OK, it) }
?: call.respond(HttpStatusCode.NoContent)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,12 @@ import io.ktor.server.response.respond
import io.ktor.server.routing.Route
import io.ktor.server.routing.get
import io.ktor.server.util.getOrFail
import projektor.compare.PreviousTestRunService
import projektor.coverage.CoverageService
import projektor.repository.coverage.RepositoryCoverageService
import projektor.server.api.coverage.CoverageExists
import projektor.server.api.repository.BranchSearch
import projektor.server.api.repository.BranchType
import projektor.server.api.repository.coverage.RepositoryCurrentCoverage

fun Route.repositoryCoverage(
coverageService: CoverageService,
previousTestRunService: PreviousTestRunService,
repositoryCoverageService: RepositoryCoverageService,
) {
fun findBranchType(call: ApplicationCall): BranchType {
Expand Down Expand Up @@ -78,17 +74,16 @@ fun Route.repositoryCoverage(
) {
val fullRepoName = "$orgPart/$repoPart"

val mostRecentTestRun = previousTestRunService.findMostRecentRunWithCoverage(BranchType.MAINLINE, fullRepoName, projectName)
val coveredPercentage = mostRecentTestRun?.publicId?.let { publicId -> coverageService.getCoveredLinePercentage(publicId) }
val repositoryCurrentCoverage = repositoryCoverageService.fetchRepositoryCurrentCoverage(
fullRepoName,
projectName,
BranchSearch(branchType = BranchType.MAINLINE)
)

if (mostRecentTestRun != null && coveredPercentage != null) {
if (repositoryCurrentCoverage != null) {
call.respond(
HttpStatusCode.OK,
RepositoryCurrentCoverage(
id = mostRecentTestRun.publicId.id,
coveredPercentage = coveredPercentage,
createdTimestamp = mostRecentTestRun.createdTimestamp
)
repositoryCurrentCoverage
)
} else {
call.respond(HttpStatusCode.NoContent)
Expand Down
Loading

0 comments on commit 89d24c4

Please sign in to comment.