Skip to content

Commit

Permalink
feat(services): Implement a service for handling Issues
Browse files Browse the repository at this point in the history
Signed-off-by: Marcel Bochtler <marcel.bochtler@bosch.com>
  • Loading branch information
MarcelBochtler committed Sep 12, 2024
1 parent b4996ce commit 42adab3
Show file tree
Hide file tree
Showing 3 changed files with 481 additions and 0 deletions.
31 changes: 31 additions & 0 deletions model/src/commonMain/kotlin/IssueWithIdentifier.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
* Copyright (C) 2024 The ORT Server Authors (See <https://github.com/eclipse-apoapsis/ort-server/blob/main/NOTICE>)
*
* 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.model

import org.eclipse.apoapsis.ortserver.model.runs.Identifier
import org.eclipse.apoapsis.ortserver.model.runs.Issue

/**
* A union data class to associate an [Issue] with an [Identifier].
*/
data class IssueWithIdentifier(
val issue: Issue,
val identifier: Identifier?
)
195 changes: 195 additions & 0 deletions services/hierarchy/src/main/kotlin/IssueService.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
/*
* Copyright (C) 2024 The ORT Server Authors (See <https://github.com/eclipse-apoapsis/ort-server/blob/main/NOTICE>)
*
* 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 org.eclipse.apoapsis.ortserver.dao.dbQuery
import org.eclipse.apoapsis.ortserver.dao.tables.AdvisorJobsTable
import org.eclipse.apoapsis.ortserver.dao.tables.AnalyzerJobsTable
import org.eclipse.apoapsis.ortserver.dao.tables.OrtRunsIssuesTable
import org.eclipse.apoapsis.ortserver.dao.tables.ScanResultsTable
import org.eclipse.apoapsis.ortserver.dao.tables.ScanSummariesIssuesTable
import org.eclipse.apoapsis.ortserver.dao.tables.ScanSummariesTable
import org.eclipse.apoapsis.ortserver.dao.tables.ScannerJobsTable
import org.eclipse.apoapsis.ortserver.dao.tables.runs.advisor.AdvisorResultsIssuesTable
import org.eclipse.apoapsis.ortserver.dao.tables.runs.advisor.AdvisorResultsTable
import org.eclipse.apoapsis.ortserver.dao.tables.runs.advisor.AdvisorRunsIdentifiersTable
import org.eclipse.apoapsis.ortserver.dao.tables.runs.advisor.AdvisorRunsTable
import org.eclipse.apoapsis.ortserver.dao.tables.runs.analyzer.AnalyzerRunsIdentifiersIssuesTable
import org.eclipse.apoapsis.ortserver.dao.tables.runs.analyzer.AnalyzerRunsTable
import org.eclipse.apoapsis.ortserver.dao.tables.runs.scanner.ScannerRunsScanResultsTable
import org.eclipse.apoapsis.ortserver.dao.tables.runs.scanner.ScannerRunsScannersTable
import org.eclipse.apoapsis.ortserver.dao.tables.runs.scanner.ScannerRunsTable
import org.eclipse.apoapsis.ortserver.dao.tables.runs.shared.IdentifiersIssuesTable
import org.eclipse.apoapsis.ortserver.dao.tables.runs.shared.IdentifiersTable
import org.eclipse.apoapsis.ortserver.dao.tables.runs.shared.IssueDao
import org.eclipse.apoapsis.ortserver.dao.tables.runs.shared.IssuesTable
import org.eclipse.apoapsis.ortserver.dao.utils.listCustomQuery
import org.eclipse.apoapsis.ortserver.model.IssueWithIdentifier
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.jetbrains.exposed.sql.Database
import org.jetbrains.exposed.sql.Op
import org.jetbrains.exposed.sql.Query
import org.jetbrains.exposed.sql.ResultRow
import org.jetbrains.exposed.sql.alias
import org.jetbrains.exposed.sql.innerJoin
import org.jetbrains.exposed.sql.selectAll
import org.jetbrains.exposed.sql.union

/**
* 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<IssueWithIdentifier> = db.dbQuery {
val ortRunIssuesQuery = createOrtRunIssuesQuery(ortRunId)
val analyzerIssuesQuery = createAnalyzerIssuesQuery(ortRunId)
val advisorIssuesQuery = createAdvisorIssuesQuery(ortRunId)
val scannerIssuesQuery = createScannerIssuesQuery(ortRunId)

val combinedQuery =
ortRunIssuesQuery.union(analyzerIssuesQuery).union(advisorIssuesQuery).union(scannerIssuesQuery)

val subQueryAlias = combinedQuery.alias("combined_query").selectAll()

val modifiedParameters = parameters.copy(
sortFields = parameters.sortFields.map { sortField ->
sortField.copy(name = "combined_query.${sortField.name}")
}
)

// FIXME: listCustomQuery currently doesn't work with a custom query, because it doesn't support aliases, and
// checks if the column is sortable.
val result = IssueDao.listCustomQuery(modifiedParameters, ResultRow::toIssueWithIdentifier) {
subQueryAlias
}

result
}

private fun createOrtRunIssuesQuery(ortRunId: Long) = (IssuesTable innerJoin OrtRunsIssuesTable).select(
IssuesTable.timestamp,
IssuesTable.issueSource,
IssuesTable.message,
IssuesTable.severity,
IssuesTable.affectedPath,
Op.nullOp<Unit>().alias("type"),
Op.nullOp<Unit>().alias("namespace"),
Op.nullOp<Unit>().alias("name"),
Op.nullOp<Unit>().alias("version")
).where { OrtRunsIssuesTable.ortRunId eq ortRunId }

private fun createAnalyzerIssuesQuery(ortRunId: Long): Query {
val analyzerIssuesIdentifiersJoin = (
IssuesTable innerJoin
IdentifiersIssuesTable innerJoin
AnalyzerRunsIdentifiersIssuesTable innerJoin
AnalyzerRunsTable innerJoin
AnalyzerJobsTable innerJoin
IdentifiersTable
)

return analyzerIssuesIdentifiersJoin.select(
IssuesTable.timestamp,
IssuesTable.issueSource,
IssuesTable.message,
IssuesTable.severity,
IssuesTable.affectedPath,
IdentifiersTable.type,
IdentifiersTable.name,
IdentifiersTable.namespace,
IdentifiersTable.version
).where { AnalyzerJobsTable.ortRunId eq ortRunId }
}

private fun createAdvisorIssuesQuery(ortRunId: Long): Query {
val advisorIssuesIdentifiersJoin = IssuesTable
.innerJoin(AdvisorResultsIssuesTable, { IssuesTable.id }, { issueId })
.innerJoin(AdvisorResultsTable, { AdvisorResultsIssuesTable.advisorResultId }, { AdvisorResultsTable.id })
.innerJoin(AdvisorRunsTable, { AdvisorResultsTable.advisorRunIdentifierId }, { AdvisorRunsTable.id })
.innerJoin(AdvisorJobsTable, { AdvisorRunsTable.advisorJobId }, { AdvisorJobsTable.id })
.innerJoin(AdvisorRunsIdentifiersTable, { AdvisorRunsTable.id }, { advisorRunId })
.innerJoin(IdentifiersTable, { AdvisorRunsIdentifiersTable.identifierId }, { IdentifiersTable.id })

return advisorIssuesIdentifiersJoin.select(
IssuesTable.timestamp,
IssuesTable.issueSource,
IssuesTable.message,
IssuesTable.severity,
IssuesTable.affectedPath,
IdentifiersTable.type,
IdentifiersTable.name,
IdentifiersTable.namespace,
IdentifiersTable.version
).where { AdvisorJobsTable.ortRunId eq ortRunId }
}

private fun createScannerIssuesQuery(ortRunId: Long): Query {
val scannerIssuesIdentifiersJoin = (
IssuesTable innerJoin
ScanSummariesIssuesTable innerJoin
ScanSummariesTable innerJoin
ScanResultsTable innerJoin
ScannerRunsScanResultsTable innerJoin
ScannerRunsTable innerJoin
ScannerJobsTable innerJoin
ScannerRunsScannersTable innerJoin
IdentifiersTable
)

return scannerIssuesIdentifiersJoin.select(
IssuesTable.timestamp,
IssuesTable.issueSource,
IssuesTable.message,
IssuesTable.severity,
IssuesTable.affectedPath,
IdentifiersTable.type,
IdentifiersTable.name,
IdentifiersTable.namespace,
IdentifiersTable.version
).where { ScannerJobsTable.ortRunId eq ortRunId }
}
}

private fun ResultRow.toIssueWithIdentifier(): IssueWithIdentifier {
val issue = Issue(
timestamp = this[IssuesTable.timestamp],
source = this[IssuesTable.issueSource],
message = this[IssuesTable.message],
severity = this[IssuesTable.severity],
affectedPath = this[IssuesTable.affectedPath]
)

val identifier = this.getOrNull(IdentifiersTable.type)?.let {
Identifier(
type = this[IdentifiersTable.type],
namespace = this[IdentifiersTable.namespace],
name = this[IdentifiersTable.name],
version = this[IdentifiersTable.version]
)
}

return IssueWithIdentifier(issue, identifier)
}
Loading

0 comments on commit 42adab3

Please sign in to comment.