poapsis.ortserver.services.hierarchy-service.0.1.0-RC6.source-code.IssueService.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of hierarchy-service Show documentation
Show all versions of hierarchy-service Show documentation
Part of the ORT Server, the reference implementation of Eclipse Apoapsis.
The newest version!
/*
* Copyright (C) 2024 The ORT Server Authors (See )
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
* License-Filename: LICENSE
*/
package org.eclipse.apoapsis.ortserver.services
import kotlinx.datetime.Instant
import org.eclipse.apoapsis.ortserver.dao.QueryParametersException
import org.eclipse.apoapsis.ortserver.dao.dbQuery
import org.eclipse.apoapsis.ortserver.dao.tables.shared.IdentifiersTable
import org.eclipse.apoapsis.ortserver.dao.tables.shared.IssuesTable
import org.eclipse.apoapsis.ortserver.dao.tables.shared.OrtRunsIssuesTable
import org.eclipse.apoapsis.ortserver.model.Severity
import org.eclipse.apoapsis.ortserver.model.runs.Identifier
import org.eclipse.apoapsis.ortserver.model.runs.Issue
import org.eclipse.apoapsis.ortserver.model.util.ListQueryParameters
import org.eclipse.apoapsis.ortserver.model.util.ListQueryResult
import org.eclipse.apoapsis.ortserver.model.util.OrderDirection
import org.eclipse.apoapsis.ortserver.model.util.OrderDirection.DESCENDING
import org.eclipse.apoapsis.ortserver.model.util.OrderField
import org.jetbrains.exposed.sql.Column
import org.jetbrains.exposed.sql.Database
import org.jetbrains.exposed.sql.JoinType
import org.jetbrains.exposed.sql.Query
import org.jetbrains.exposed.sql.ResultRow
import org.jetbrains.exposed.sql.SortOrder
import org.jetbrains.exposed.sql.innerJoin
/**
* A service to manage and get information about issues.
*/
class IssueService(private val db: Database) {
suspend fun listForOrtRunId(
ortRunId: Long,
parameters: ListQueryParameters = ListQueryParameters.DEFAULT
): ListQueryResult = db.dbQuery {
val ortRunIssuesQuery = createOrtRunIssuesQuery(ortRunId)
val totalCount = ortRunIssuesQuery.count()
// There always has to be some sort order defined, else the rows would be returned in random order
// and tests that rely on a deterministic order would fail.
val sortFields = parameters.sortFields.ifEmpty {
listOf(OrderField("timestamp", DESCENDING))
}
// For sorting we need to find the corresponding columns in the query.
// TODO: Find a mechanism that does not rely on the internal names of table columns.
val sortColumns = ortRunIssuesQuery.set.fields.mapNotNull { it as? Column }.associateBy { it.name }
val orders = sortFields.map { sortField ->
val column = sortColumns[sortField.name]
?: throw QueryParametersException("Field for sorting not found in query alias: '${sortField.name}'.")
column to sortField.direction.toSortOrder()
}.toTypedArray()
val orderedQuery = ortRunIssuesQuery.orderBy(*orders)
val paginatedQuery = parameters.limit?.let { orderedQuery.limit(it).offset(parameters.offset ?: 0) }
?: orderedQuery
ListQueryResult(
paginatedQuery.map(ResultRow::toIssue),
parameters,
totalCount
)
}
suspend fun countForOrtRunId(ortRunId: Long): Long = db.dbQuery {
OrtRunsIssuesTable
.select(OrtRunsIssuesTable.id)
.where { OrtRunsIssuesTable.ortRunId eq ortRunId }
.count()
}
private fun createOrtRunIssuesQuery(ortRunId: Long): Query {
val issuesIdentifiersJoin = OrtRunsIssuesTable
.innerJoin(IssuesTable, { issueId }, { IssuesTable.id })
.join(IdentifiersTable, JoinType.LEFT, OrtRunsIssuesTable.identifierId, IdentifiersTable.id)
return issuesIdentifiersJoin.select(
OrtRunsIssuesTable.timestamp,
IssuesTable.issueSource,
IssuesTable.message,
IssuesTable.severity,
IssuesTable.affectedPath,
IdentifiersTable.type,
IdentifiersTable.name,
IdentifiersTable.namespace,
IdentifiersTable.version,
OrtRunsIssuesTable.worker
).where { OrtRunsIssuesTable.ortRunId eq ortRunId }
}
}
@Suppress("ComplexCondition")
private fun ResultRow.toIssue(): Issue {
// The exposed library seems not to fully support fields on alias queries when unions are used.
// Use the field indexes in order to extract the values from the ResultRow instead of the field names.
// Disadvantage: More fragility, losing type safety at compile time.
val columns = fieldIndex.keys.toList()
val type = this[columns[5]] as String?
val name = this[columns[6]] as String?
val namespace = this[columns[7]] as String?
val version = this[columns[8]] as String?
val identifier = if (type == null || name == null || namespace == null || version == null) {
null
} else {
Identifier(type, namespace, name, version)
}
return Issue(
timestamp = this[columns[0]] as Instant,
source = this[columns[1]] as String,
message = this[columns[2]] as String,
severity = this[columns[3]] as Severity,
affectedPath = this[columns[4]] as String?,
worker = this[columns[9]] as String?,
identifier = identifier
)
}
/**
* Convert this [OrderDirection] constant to the corresponding [SortOrder].
*/
fun OrderDirection.toSortOrder(): SortOrder =
when (this) {
OrderDirection.ASCENDING -> SortOrder.ASC
OrderDirection.DESCENDING -> SortOrder.DESC
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy