Skip to content

Commit

Permalink
feat(core): Add run statistics endpoint for products
Browse files Browse the repository at this point in the history
Signed-off-by: Johanna Lamppu <[email protected]>
  • Loading branch information
lamppu authored and sschuberth committed Dec 20, 2024
1 parent 02adf04 commit 2c7c227
Show file tree
Hide file tree
Showing 4 changed files with 545 additions and 2 deletions.
109 changes: 108 additions & 1 deletion core/src/main/kotlin/api/ProductsRoute.kt
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ import io.github.smiley4.ktorswaggerui.dsl.routing.post
import io.github.smiley4.ktorswaggerui.dsl.routing.put

import io.ktor.http.HttpStatusCode
import io.ktor.server.application.call
import io.ktor.server.request.receive
import io.ktor.server.response.respond
import io.ktor.server.routing.Route
Expand All @@ -36,6 +35,7 @@ import org.eclipse.apoapsis.ortserver.api.v1.mapping.mapToApi
import org.eclipse.apoapsis.ortserver.api.v1.mapping.mapToModel
import org.eclipse.apoapsis.ortserver.api.v1.model.CreateRepository
import org.eclipse.apoapsis.ortserver.api.v1.model.CreateSecret
import org.eclipse.apoapsis.ortserver.api.v1.model.OrtRunStatistics
import org.eclipse.apoapsis.ortserver.api.v1.model.SortDirection
import org.eclipse.apoapsis.ortserver.api.v1.model.SortProperty
import org.eclipse.apoapsis.ortserver.api.v1.model.UpdateProduct
Expand All @@ -44,6 +44,7 @@ import org.eclipse.apoapsis.ortserver.api.v1.model.Username
import org.eclipse.apoapsis.ortserver.core.apiDocs.deleteProductById
import org.eclipse.apoapsis.ortserver.core.apiDocs.deleteSecretByProductIdAndName
import org.eclipse.apoapsis.ortserver.core.apiDocs.deleteUserFromProductGroup
import org.eclipse.apoapsis.ortserver.core.apiDocs.getOrtRunStatisticsByProductId
import org.eclipse.apoapsis.ortserver.core.apiDocs.getProductById
import org.eclipse.apoapsis.ortserver.core.apiDocs.getRepositoriesByProductId
import org.eclipse.apoapsis.ortserver.core.apiDocs.getSecretByProductIdAndName
Expand All @@ -62,8 +63,11 @@ import org.eclipse.apoapsis.ortserver.model.Repository
import org.eclipse.apoapsis.ortserver.model.Secret
import org.eclipse.apoapsis.ortserver.model.VulnerabilityWithAccumulatedData
import org.eclipse.apoapsis.ortserver.model.authorization.ProductPermission
import org.eclipse.apoapsis.ortserver.services.IssueService
import org.eclipse.apoapsis.ortserver.services.PackageService
import org.eclipse.apoapsis.ortserver.services.ProductService
import org.eclipse.apoapsis.ortserver.services.RepositoryService
import org.eclipse.apoapsis.ortserver.services.RuleViolationService
import org.eclipse.apoapsis.ortserver.services.SecretService
import org.eclipse.apoapsis.ortserver.services.VulnerabilityService

Expand All @@ -75,6 +79,9 @@ fun Route.products() = route("products/{productId}") {
val repositoryService by inject<RepositoryService>()
val secretService by inject<SecretService>()
val vulnerabilityService by inject<VulnerabilityService>()
val issueService by inject<IssueService>()
val ruleViolationService by inject<RuleViolationService>()
val packageService by inject<PackageService>()

get(getProductById) {
requirePermission(ProductPermission.READ)
Expand Down Expand Up @@ -266,4 +273,104 @@ fun Route.products() = route("products/{productId}") {
call.respond(HttpStatusCode.OK, pagedResponse)
}
}

route("statistics") {
route("runs") {
get(getOrtRunStatisticsByProductId) {
requirePermission(ProductPermission.READ)

val productId = call.requireIdParameter("productId")

val repositoryIds = productService.getRepositoryIdsForProduct(productId)

val latestRunsWithAnalyzerJobInFinalState = repositoryIds.mapNotNull {
repositoryService.getLatestOrtRunIdWithAnalyzerJobInFinalState(it)
}.toLongArray()

val issuesCount = if (latestRunsWithAnalyzerJobInFinalState.isNotEmpty()) {
issueService.countForOrtRunIds(*latestRunsWithAnalyzerJobInFinalState)
} else {
null
}

val issuesBySeverity = if (latestRunsWithAnalyzerJobInFinalState.isNotEmpty()) {
issueService
.countBySeverityForOrtRunIds(*latestRunsWithAnalyzerJobInFinalState)
.map
.mapKeys { it.key.mapToApi() }
} else {
null
}

val latestRunsWithSuccessfulAnalyzerJob = repositoryIds.mapNotNull {
repositoryService.getLatestOrtRunIdWithSuccessfulAnalyzerJob(it)
}.toLongArray()

val packagesCount = if (latestRunsWithSuccessfulAnalyzerJob.isNotEmpty()) {
packageService.countForOrtRunIds(*latestRunsWithSuccessfulAnalyzerJob)
} else {
null
}

val ecosystems = if (latestRunsWithSuccessfulAnalyzerJob.isNotEmpty()) {
packageService.countEcosystemsForOrtRunIds(*latestRunsWithSuccessfulAnalyzerJob)
.map { it.mapToApi() }
} else {
null
}

val latestRunsWithSuccessfulAdvisorJob = repositoryIds.mapNotNull {
repositoryService.getLatestOrtRunIdWithSuccessfulAdvisorJob(it)
}.toLongArray()

val vulnerabilitiesCount = if (latestRunsWithSuccessfulAdvisorJob.isNotEmpty()) {
vulnerabilityService.countForOrtRunIds(*latestRunsWithSuccessfulAdvisorJob)
} else {
null
}

val vulnerabilitiesByRating = if (latestRunsWithSuccessfulAdvisorJob.isNotEmpty()) {
vulnerabilityService
.countByRatingForOrtRunIds(*latestRunsWithSuccessfulAdvisorJob)
.map
.mapKeys { it.key.mapToApi() }
} else {
null
}

val latestRunsWithSuccessfulEvaluatorJob = repositoryIds.mapNotNull {
repositoryService.getLatestOrtRunIdWithSuccessfulEvaluatorJob(it)
}.toLongArray()

val ruleViolationsCount = if (latestRunsWithSuccessfulEvaluatorJob.isNotEmpty()) {
ruleViolationService.countForOrtRunIds(*latestRunsWithSuccessfulEvaluatorJob)
} else {
null
}

val ruleViolationsBySeverity = if (latestRunsWithSuccessfulEvaluatorJob.isNotEmpty()) {
ruleViolationService
.countBySeverityForOrtRunIds(*latestRunsWithSuccessfulEvaluatorJob)
.map
.mapKeys { it.key.mapToApi() }
} else {
null
}

call.respond(
HttpStatusCode.OK,
OrtRunStatistics(
issuesCount = issuesCount,
issuesCountBySeverity = issuesBySeverity,
packagesCount = packagesCount,
ecosystems = ecosystems,
vulnerabilitiesCount = vulnerabilitiesCount,
vulnerabilitiesCountByRating = vulnerabilitiesByRating,
ruleViolationsCount = ruleViolationsCount,
ruleViolationsCountBySeverity = ruleViolationsBySeverity
)
)
}
}
}
}
52 changes: 52 additions & 0 deletions core/src/main/kotlin/apiDocs/ProductsDocs.kt
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,17 @@ import io.ktor.http.HttpStatusCode

import org.eclipse.apoapsis.ortserver.api.v1.model.CreateRepository
import org.eclipse.apoapsis.ortserver.api.v1.model.CreateSecret
import org.eclipse.apoapsis.ortserver.api.v1.model.EcosystemStats
import org.eclipse.apoapsis.ortserver.api.v1.model.Identifier
import org.eclipse.apoapsis.ortserver.api.v1.model.OrtRunStatistics
import org.eclipse.apoapsis.ortserver.api.v1.model.PagedResponse
import org.eclipse.apoapsis.ortserver.api.v1.model.PagingData
import org.eclipse.apoapsis.ortserver.api.v1.model.Product
import org.eclipse.apoapsis.ortserver.api.v1.model.ProductVulnerability
import org.eclipse.apoapsis.ortserver.api.v1.model.Repository
import org.eclipse.apoapsis.ortserver.api.v1.model.RepositoryType
import org.eclipse.apoapsis.ortserver.api.v1.model.Secret
import org.eclipse.apoapsis.ortserver.api.v1.model.Severity
import org.eclipse.apoapsis.ortserver.api.v1.model.SortDirection
import org.eclipse.apoapsis.ortserver.api.v1.model.SortProperty
import org.eclipse.apoapsis.ortserver.api.v1.model.UpdateProduct
Expand Down Expand Up @@ -468,3 +471,52 @@ val getVulnerabilitiesAcrossRepositoriesByProductId: OpenApiRoute.() -> Unit = {
}
}
}

val getOrtRunStatisticsByProductId: OpenApiRoute.() -> Unit = {
operationId = "GetOrtRunStatisticsByProductId"
summary = "Get statistics about ORT runs across the repositories of a product."
tags = listOf("Products")

request {
pathParameter<Long>("productId") {
description = "The product's ID."
}
}

response {
HttpStatusCode.OK to {
jsonBody<OrtRunStatistics> {
example("Get run statistics across repositories of a product") {
value = OrtRunStatistics(
issuesCount = 131,
issuesCountBySeverity = mapOf(
Severity.HINT to 40,
Severity.WARNING to 0,
Severity.ERROR to 91
),
packagesCount = 953,
ecosystems = listOf(
EcosystemStats("Maven", 578),
EcosystemStats("NPM", 326),
EcosystemStats("PyPI", 49)
),
vulnerabilitiesCount = 163,
vulnerabilitiesCountByRating = mapOf(
VulnerabilityRating.NONE to 11,
VulnerabilityRating.LOW to 1,
VulnerabilityRating.MEDIUM to 47,
VulnerabilityRating.HIGH to 83,
VulnerabilityRating.CRITICAL to 21
),
ruleViolationsCount = 104,
ruleViolationsCountBySeverity = mapOf(
Severity.HINT to 0,
Severity.WARNING to 6,
Severity.ERROR to 98
)
)
}
}
}
}
}
Loading

0 comments on commit 2c7c227

Please sign in to comment.