From 836971dc9afee340d7c94bca283f675bf4530f1d Mon Sep 17 00:00:00 2001 From: Jan-Niclas Struewer <j.n.struewer@gmail.com> Date: Tue, 23 May 2023 21:23:24 +0200 Subject: [PATCH] updated db structure to enable a query to get the latest results for all tools for a certain repository --- .../iem/dataprovider/gitlab/Repository.kt | 6 +- .../iem/dataprovider/sarif/SarifExtensions.kt | 20 ++---- .../dataprovider/taskManager/TaskManager.kt | 13 ++-- .../taskManager/tasks/DetektTask.kt | 9 +-- .../dataprovider/taskManager/tasks/OdcTask.kt | 8 +-- .../taskManager/tasks/ProcessTask.kt | 23 +++---- .../dataprovider/toolResult/RuleRepository.kt | 10 +++ .../dataprovider/toolResult/ToolRepository.kt | 12 ++++ .../iem/dataprovider/toolResult/ToolRun.kt | 46 +++++++++++-- .../dataprovider/toolResult/ToolRunService.kt | 67 +++++++++++++++++++ 10 files changed, 163 insertions(+), 51 deletions(-) create mode 100644 src/main/kotlin/de/fraunhofer/iem/dataprovider/toolResult/RuleRepository.kt create mode 100644 src/main/kotlin/de/fraunhofer/iem/dataprovider/toolResult/ToolRepository.kt create mode 100644 src/main/kotlin/de/fraunhofer/iem/dataprovider/toolResult/ToolRunService.kt diff --git a/src/main/kotlin/de/fraunhofer/iem/dataprovider/gitlab/Repository.kt b/src/main/kotlin/de/fraunhofer/iem/dataprovider/gitlab/Repository.kt index fb409de3..9a6ccf02 100644 --- a/src/main/kotlin/de/fraunhofer/iem/dataprovider/gitlab/Repository.kt +++ b/src/main/kotlin/de/fraunhofer/iem/dataprovider/gitlab/Repository.kt @@ -14,7 +14,11 @@ class Repository { var id: UUID? = null @OrderBy("time_stamp DESC") - @OneToMany(mappedBy = "repository", cascade = [CascadeType.ALL], orphanRemoval = true) + @OneToMany( + mappedBy = "repository", + cascade = [CascadeType.MERGE, CascadeType.REFRESH, CascadeType.REMOVE, CascadeType.DETACH], + orphanRemoval = true + ) var toolRuns: MutableList<ToolRun> = mutableListOf() @Column(name = "name") diff --git a/src/main/kotlin/de/fraunhofer/iem/dataprovider/sarif/SarifExtensions.kt b/src/main/kotlin/de/fraunhofer/iem/dataprovider/sarif/SarifExtensions.kt index b535c2e3..145f0cad 100644 --- a/src/main/kotlin/de/fraunhofer/iem/dataprovider/sarif/SarifExtensions.kt +++ b/src/main/kotlin/de/fraunhofer/iem/dataprovider/sarif/SarifExtensions.kt @@ -21,7 +21,6 @@ fun de.fraunhofer.iem.dataprovider.sarif.Rule.asDbObject(): Rule { fun de.fraunhofer.iem.dataprovider.sarif.Tool.asDbObject(): Tool { val tool = Tool() - tool.addRules(this.driver.rules.map { it.asDbObject() }) tool.fullName = this.driver.fullName tool.name = this.driver.name tool.version = this.driver.version @@ -38,7 +37,7 @@ fun Location.asDbObject(): ResultLocation { return resultLocation } -fun Result.asDbObject(rules: Collection<Rule>): ToolResult { +fun Result.asDbObject(): ToolResult { val toolResult = ToolResult() when (this.level.uppercase(Locale.getDefault())) { @@ -48,26 +47,19 @@ fun Result.asDbObject(rules: Collection<Rule>): ToolResult { else -> toolResult.level = Level.None } - rules - .filter { it.sarifRuleId == this.ruleId } - .forEach { it.addResultLocation(toolResult) } - toolResult.message = this.message.text toolResult.addLocations(this.locations.map { it.asDbObject() }) return toolResult } -fun Sarif.asDbObject(): List<ToolRun> { - return this.runs.map { run -> - val tr = ToolRun() - tr.tool = run.tool.asDbObject() - tr.addToolResults(run.results.map { it.asDbObject(tr.tool!!.rules) }) - tr.timeStamp = Timestamp.from(Instant.now()) - tr - } +fun Run.asDbObject(): ToolRun { + val tr = ToolRun() + tr.timeStamp = Timestamp.from(Instant.now()) + return tr } + fun getSarifFromFilePath(resultPath: Path): Sarif { val resFile = resultPath.toFile() if (resFile.exists()) { diff --git a/src/main/kotlin/de/fraunhofer/iem/dataprovider/taskManager/TaskManager.kt b/src/main/kotlin/de/fraunhofer/iem/dataprovider/taskManager/TaskManager.kt index d2fda8fa..33fab379 100644 --- a/src/main/kotlin/de/fraunhofer/iem/dataprovider/taskManager/TaskManager.kt +++ b/src/main/kotlin/de/fraunhofer/iem/dataprovider/taskManager/TaskManager.kt @@ -5,7 +5,7 @@ import de.fraunhofer.iem.dataprovider.gitlab.RepositoryRepository import de.fraunhofer.iem.dataprovider.logger.getLogger import de.fraunhofer.iem.dataprovider.sarif.Sarif import de.fraunhofer.iem.dataprovider.taskManager.tasks.* -import de.fraunhofer.iem.dataprovider.toolResult.ToolRunRepository +import de.fraunhofer.iem.dataprovider.toolResult.ToolRunService import jakarta.annotation.PreDestroy import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers @@ -42,7 +42,7 @@ class DetektDone(override val taskId: UUID, val sarif: Sarif) : TaskDone() class TaskManager( private val config: Config, private val repositoryRepository: RepositoryRepository, - private val toolRunRepository: ToolRunRepository + private val toolRunService: ToolRunService ) { // The used default dispatcher is ok for CPU-bound workloads. However, @@ -128,8 +128,8 @@ class TaskManager( analysisResultsPath, ::addEvent, event.repoId, - repositoryRepository, - toolRunRepository, groupId + groupId, + toolRunService ) val detektTask = DetektTask( @@ -137,9 +137,8 @@ class TaskManager( analysisResultsPath, ::addEvent, event.repoId, - repositoryRepository, - toolRunRepository, - groupID = groupId + groupID = groupId, + toolRunService ) taskIds.add(odcTask.taskID) diff --git a/src/main/kotlin/de/fraunhofer/iem/dataprovider/taskManager/tasks/DetektTask.kt b/src/main/kotlin/de/fraunhofer/iem/dataprovider/taskManager/tasks/DetektTask.kt index af6ec9ae..40355b69 100644 --- a/src/main/kotlin/de/fraunhofer/iem/dataprovider/taskManager/tasks/DetektTask.kt +++ b/src/main/kotlin/de/fraunhofer/iem/dataprovider/taskManager/tasks/DetektTask.kt @@ -1,9 +1,7 @@ package de.fraunhofer.iem.dataprovider.taskManager.tasks - -import de.fraunhofer.iem.dataprovider.gitlab.RepositoryRepository import de.fraunhofer.iem.dataprovider.taskManager.Event -import de.fraunhofer.iem.dataprovider.toolResult.ToolRunRepository +import de.fraunhofer.iem.dataprovider.toolResult.ToolRunService import org.springframework.core.io.ClassPathResource import org.springframework.core.io.Resource import java.nio.file.Path @@ -15,9 +13,8 @@ class DetektTask( projectPath: String, outputPath: String, override val responseChannel: suspend (task: Event) -> Unit, override val repoId: UUID, - override val repository: RepositoryRepository, - override val toolRunRepository: ToolRunRepository, - override val groupID: UUID? = null + override val groupID: UUID? = null, + override val toolRunService: ToolRunService ) : SarifTask() { private val resource: Resource = ClassPathResource("scripts/detekt.sh") diff --git a/src/main/kotlin/de/fraunhofer/iem/dataprovider/taskManager/tasks/OdcTask.kt b/src/main/kotlin/de/fraunhofer/iem/dataprovider/taskManager/tasks/OdcTask.kt index 12c653c5..bfb92400 100644 --- a/src/main/kotlin/de/fraunhofer/iem/dataprovider/taskManager/tasks/OdcTask.kt +++ b/src/main/kotlin/de/fraunhofer/iem/dataprovider/taskManager/tasks/OdcTask.kt @@ -1,8 +1,7 @@ package de.fraunhofer.iem.dataprovider.taskManager.tasks -import de.fraunhofer.iem.dataprovider.gitlab.RepositoryRepository import de.fraunhofer.iem.dataprovider.taskManager.Event -import de.fraunhofer.iem.dataprovider.toolResult.ToolRunRepository +import de.fraunhofer.iem.dataprovider.toolResult.ToolRunService import org.springframework.core.io.ClassPathResource import org.springframework.core.io.Resource import java.nio.file.Path @@ -15,9 +14,8 @@ class OdcTask( projectPath: String, outputPath: String, override val responseChannel: suspend (task: Event) -> Unit, override val repoId: UUID, - override val repository: RepositoryRepository, - override val toolRunRepository: ToolRunRepository, - override val groupID: UUID? = null + override val groupID: UUID? = null, + override val toolRunService: ToolRunService ) : SarifTask() { private val resource: Resource = ClassPathResource("scripts/odc.sh") diff --git a/src/main/kotlin/de/fraunhofer/iem/dataprovider/taskManager/tasks/ProcessTask.kt b/src/main/kotlin/de/fraunhofer/iem/dataprovider/taskManager/tasks/ProcessTask.kt index f1793de1..552f62c0 100644 --- a/src/main/kotlin/de/fraunhofer/iem/dataprovider/taskManager/tasks/ProcessTask.kt +++ b/src/main/kotlin/de/fraunhofer/iem/dataprovider/taskManager/tasks/ProcessTask.kt @@ -1,15 +1,14 @@ package de.fraunhofer.iem.dataprovider.taskManager.tasks -import de.fraunhofer.iem.dataprovider.gitlab.RepositoryRepository import de.fraunhofer.iem.dataprovider.sarif.Sarif -import de.fraunhofer.iem.dataprovider.sarif.asDbObject import de.fraunhofer.iem.dataprovider.sarif.getSarifFromFilePath import de.fraunhofer.iem.dataprovider.taskManager.SarifProcessDone import de.fraunhofer.iem.dataprovider.taskManager.SarifProcessGroupDone -import de.fraunhofer.iem.dataprovider.toolResult.ToolRunRepository +import de.fraunhofer.iem.dataprovider.toolResult.ToolRunService import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch +import java.io.IOException import java.nio.file.Files import java.nio.file.Path import java.nio.file.Paths @@ -48,8 +47,7 @@ sealed class ProcessTask : Task() { sealed class SarifTask : ProcessTask() { protected abstract val repoId: UUID - protected abstract val repository: RepositoryRepository - protected abstract val toolRunRepository: ToolRunRepository + protected abstract val toolRunService: ToolRunService protected abstract val resultPath: Path @@ -59,16 +57,13 @@ sealed class SarifTask : ProcessTask() { // has findings it might return an exit code != 0 even tho the process finished correctly logger.info("Handle Process return in $javaClass") - val sarifResult = getSarifFromFilePath(resultPath) - val repo = repository.findById(repoId) - val sarifDb = sarifResult.asDbObject() - val results = toolRunRepository.saveAll(sarifDb) - - repo.ifPresent { - it.addToolResults(results) - repository.save(it) + try { + val sarifResult = getSarifFromFilePath(resultPath) + toolRunService.saveSarif(sarifResult, repoId) + sendResult(sarifResult) + } catch (e: IOException) { + // TODO: send meaningful event and react on error } - sendResult(sarifResult) } override suspend fun sendResult(sarif: Sarif) { diff --git a/src/main/kotlin/de/fraunhofer/iem/dataprovider/toolResult/RuleRepository.kt b/src/main/kotlin/de/fraunhofer/iem/dataprovider/toolResult/RuleRepository.kt new file mode 100644 index 00000000..ea3d707b --- /dev/null +++ b/src/main/kotlin/de/fraunhofer/iem/dataprovider/toolResult/RuleRepository.kt @@ -0,0 +1,10 @@ +package de.fraunhofer.iem.dataprovider.toolResult + +import org.springframework.data.jpa.repository.JpaRepository +import java.util.* + +interface RuleRepository : JpaRepository<Rule, UUID> { + + + fun findByTool_Id(id: UUID): List<Rule> +} \ No newline at end of file diff --git a/src/main/kotlin/de/fraunhofer/iem/dataprovider/toolResult/ToolRepository.kt b/src/main/kotlin/de/fraunhofer/iem/dataprovider/toolResult/ToolRepository.kt new file mode 100644 index 00000000..1989e6ac --- /dev/null +++ b/src/main/kotlin/de/fraunhofer/iem/dataprovider/toolResult/ToolRepository.kt @@ -0,0 +1,12 @@ +package de.fraunhofer.iem.dataprovider.toolResult + +import org.springframework.data.jpa.repository.JpaRepository +import java.util.* + +interface ToolRepository : JpaRepository<Tool, UUID> { + fun findByFullNameIgnoreCaseAndNameIgnoreCaseAndVersionIgnoreCase( + fullName: String?, + name: String?, + version: String? + ): Tool? +} \ No newline at end of file diff --git a/src/main/kotlin/de/fraunhofer/iem/dataprovider/toolResult/ToolRun.kt b/src/main/kotlin/de/fraunhofer/iem/dataprovider/toolResult/ToolRun.kt index 7b947239..76f05694 100644 --- a/src/main/kotlin/de/fraunhofer/iem/dataprovider/toolResult/ToolRun.kt +++ b/src/main/kotlin/de/fraunhofer/iem/dataprovider/toolResult/ToolRun.kt @@ -24,8 +24,7 @@ class ToolRun { @Column(name = "id", nullable = false) var id: UUID? = null - - @OneToOne(orphanRemoval = true, cascade = [CascadeType.ALL]) + @OneToOne(orphanRemoval = true, cascade = [CascadeType.REFRESH, CascadeType.REMOVE, CascadeType.DETACH]) @JoinColumn(name = "tool_id") var tool: Tool? = null @@ -36,6 +35,24 @@ class ToolRun { @ManyToOne(fetch = FetchType.EAGER) var repository: Repository? = null + @OneToMany( + mappedBy = "toolRun", + cascade = [CascadeType.MERGE, CascadeType.REFRESH, CascadeType.REMOVE, CascadeType.DETACH], + orphanRemoval = true + ) + var rules: MutableSet<Rule> = mutableSetOf() + + fun addRules(rules: Collection<Rule>) { + rules.forEach { + this.addRule(it) + } + } + + fun addRule(rule: Rule) { + rules.add(rule) + rule.toolRun = this + } + @OneToMany(mappedBy = "toolRun", cascade = [CascadeType.ALL], orphanRemoval = true) var toolResults: MutableSet<ToolResult> = mutableSetOf() @@ -68,7 +85,11 @@ class Tool { @Column(name = "version") var version: String? = null - @OneToMany(mappedBy = "tool", cascade = [CascadeType.ALL], orphanRemoval = true) + @OneToMany( + mappedBy = "tool", + cascade = [CascadeType.MERGE, CascadeType.REFRESH, CascadeType.REMOVE, CascadeType.DETACH], + orphanRemoval = true + ) var rules: MutableSet<Rule> = mutableSetOf() fun addRules(rules: Collection<Rule>) { @@ -81,6 +102,16 @@ class Tool { rules.add(rule) rule.tool = this } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other == null || Hibernate.getClass(this) != Hibernate.getClass(other)) return false + other as Tool + + return id != null && id == other.id + } + + override fun hashCode(): Int = javaClass.hashCode() } @@ -186,9 +217,16 @@ class Rule { @Column(name = "short_description", columnDefinition = "TEXT") var shortDescription: String? = null - @OneToMany(mappedBy = "rule", cascade = [CascadeType.ALL], orphanRemoval = true) + @OneToMany( + mappedBy = "rule", + cascade = [CascadeType.MERGE, CascadeType.REFRESH, CascadeType.REFRESH, CascadeType.DETACH], + orphanRemoval = true + ) var toolResults: MutableSet<ToolResult> = mutableSetOf() + @ManyToOne + var toolRun: ToolRun? = null + fun addResultLocations(toolResults: Collection<ToolResult>) { toolResults.forEach { this.addResultLocation(it) diff --git a/src/main/kotlin/de/fraunhofer/iem/dataprovider/toolResult/ToolRunService.kt b/src/main/kotlin/de/fraunhofer/iem/dataprovider/toolResult/ToolRunService.kt new file mode 100644 index 00000000..36f8edc1 --- /dev/null +++ b/src/main/kotlin/de/fraunhofer/iem/dataprovider/toolResult/ToolRunService.kt @@ -0,0 +1,67 @@ +package de.fraunhofer.iem.dataprovider.toolResult + +import de.fraunhofer.iem.dataprovider.gitlab.RepositoryRepository +import de.fraunhofer.iem.dataprovider.sarif.Sarif +import de.fraunhofer.iem.dataprovider.sarif.asDbObject +import org.springframework.stereotype.Service +import java.util.* + +@Service +class ToolRunService( + private val repositoryRepository: RepositoryRepository, + private val toolRunRepository: ToolRunRepository, + private val toolRepository: ToolRepository, + private val ruleRepository: RuleRepository +) { + + // TODO: this func should be a suspend fun. We can achieve this by + // wrapping the db return types in flux, mono, or futures + fun saveSarif(sarif: Sarif, repoId: UUID) { + + val repo = repositoryRepository.findById(repoId) + + sarif.runs.forEach { run -> + val fullName = run.tool.driver.fullName + val name = run.tool.driver.name + val version = run.tool.driver.version + + val tool = + toolRepository.findByFullNameIgnoreCaseAndNameIgnoreCaseAndVersionIgnoreCase(fullName, name, version) + ?: toolRepository.save(run.tool.asDbObject()) + + val rules = if (tool.id != null) { + ruleRepository.findByTool_Id(tool.id!!).toMutableList() + } else { + mutableListOf() + } + + run.tool.driver.rules.forEach { sarifRule -> + if (rules.find { it.sarifRuleId == sarifRule.id } == null) { + val rule = ruleRepository.save(sarifRule.asDbObject()) + rules.add(rule) + tool.addRule(rule) + } + } + + val toolResults = run.results.map { sarifResult -> + val tr = sarifResult.asDbObject() + rules.filter { it.sarifRuleId == sarifResult.ruleId } + .forEach { it.addResultLocation(tr) } + tr + } + + + val toolRun = run.asDbObject() + toolRun.tool = tool + toolRun.addRules(rules) + toolRun.addToolResults(toolResults) + + val result = toolRunRepository.save(toolRun) + toolRepository.save(tool) + repo.ifPresent { + it.addToolResult(result) + repositoryRepository.save(it) + } + } + } +} \ No newline at end of file -- GitLab