diff --git a/src/main/kotlin/de/fraunhofer/iem/dataprovider/gitlab/GitlabService.kt b/src/main/kotlin/de/fraunhofer/iem/dataprovider/gitlab/GitlabService.kt index f4cad52a6be229d153f630053c9771a2d426e002..0ced35eebc9d8dbc3dd1adcc1d4a45580c1268cd 100644 --- a/src/main/kotlin/de/fraunhofer/iem/dataprovider/gitlab/GitlabService.kt +++ b/src/main/kotlin/de/fraunhofer/iem/dataprovider/gitlab/GitlabService.kt @@ -12,7 +12,7 @@ class GitlabService( ) { suspend fun queryOpenCodeProject(repoId: Long) { - taskManager.addEvent(RepoChangedEvent(repoId = repoId, gitConfiguration = openCodeGitlabConfiguration)) + taskManager.addEvent(RepoChangedEvent(repoId = repoId)) } } \ No newline at end of file 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 4da97112047ec2ad9554861955d72aa1b5cd6f1c..d69796995469a866489b062e000c96853ceee3e7 100644 --- a/src/main/kotlin/de/fraunhofer/iem/dataprovider/taskManager/TaskManager.kt +++ b/src/main/kotlin/de/fraunhofer/iem/dataprovider/taskManager/TaskManager.kt @@ -1,5 +1,6 @@ package de.fraunhofer.iem.dataprovider.taskManager +import de.fraunhofer.iem.dataprovider.gitlab.OpenCodeGitlabConfiguration import de.fraunhofer.iem.dataprovider.logger.getLogger import de.fraunhofer.iem.dataprovider.taskManager.model.* import de.fraunhofer.iem.dataprovider.taskManager.tasks.* @@ -24,8 +25,9 @@ import java.util.* */ @Component class TaskManager( - private val config: Config, - private val toolRunService: ToolRunService + private val config: Config, + private val openCodeGitlabConfiguration: OpenCodeGitlabConfiguration, + private val toolRunService: ToolRunService ) { // The used default dispatcher is ok for CPU-bound workloads. However, @@ -81,7 +83,7 @@ class TaskManager( ioWorker.addTask( GetGitlabProjectTask( event.repoId, - event.gitConfiguration, + openCodeGitlabConfiguration, ::addEvent, toolRunService ) @@ -124,13 +126,22 @@ class TaskManager( toolRunService ) + val gitlabTask = GetRepositoryDetailsTask( + event.repoId, + openCodeGitlabConfiguration, + toolRunService, + ::addEvent + ) + taskIds.add(odcTask.taskID) taskIds.add(detektTask.taskID) + taskIds.add(gitlabTask.taskID) dependentEvents[groupId] = taskIds worker.addTask(odcTask) worker.addTask(detektTask) + worker.addTask(gitlabTask) } is SarifProcessGroupDone -> { diff --git a/src/main/kotlin/de/fraunhofer/iem/dataprovider/taskManager/model/Event.kt b/src/main/kotlin/de/fraunhofer/iem/dataprovider/taskManager/model/Event.kt index 7d3eaade44f873907fa74c3dfe4f41e4a2c675cc..83952b82c059090a2cdb055887ef0339b45d7fc3 100644 --- a/src/main/kotlin/de/fraunhofer/iem/dataprovider/taskManager/model/Event.kt +++ b/src/main/kotlin/de/fraunhofer/iem/dataprovider/taskManager/model/Event.kt @@ -1,12 +1,12 @@ package de.fraunhofer.iem.dataprovider.taskManager.model -import de.fraunhofer.iem.dataprovider.gitlab.GitConfiguration import de.fraunhofer.iem.dataprovider.taskManager.tasks.GitRepository +import de.fraunhofer.iem.dataprovider.toolRun.model.RepositoryDetails import de.fraunhofer.iem.dataprovider.toolRun.model.Sarif import java.util.* sealed class Event -class RepoChangedEvent(val gitConfiguration: GitConfiguration, val repoId: Long) : Event() +class RepoChangedEvent(val repoId: Long) : Event() sealed class TaskDone : Event() { abstract val taskId: UUID @@ -18,3 +18,5 @@ class GetGitlabProjectDone(override val taskId: UUID, val repoId: UUID, val gitR class SarifProcessDone(override val taskId: UUID, val repoId: UUID, val sarif: Sarif) : TaskDone() class SarifProcessGroupDone(override val taskId: UUID, val repoId: UUID, val groupId: UUID, val sarif: Sarif) : TaskDone() + +class GetRepositoryDetailsDone(override val taskId: UUID, val repositoryDetailsEntity: RepositoryDetails) : TaskDone() diff --git a/src/main/kotlin/de/fraunhofer/iem/dataprovider/taskManager/tasks/GetRepositoryDetailsTask.kt b/src/main/kotlin/de/fraunhofer/iem/dataprovider/taskManager/tasks/GetRepositoryDetailsTask.kt new file mode 100644 index 0000000000000000000000000000000000000000..4fd547b97d871860d7bc683507d784a3011bf90d --- /dev/null +++ b/src/main/kotlin/de/fraunhofer/iem/dataprovider/taskManager/tasks/GetRepositoryDetailsTask.kt @@ -0,0 +1,67 @@ +package de.fraunhofer.iem.dataprovider.taskManager.tasks + +import de.fraunhofer.iem.dataprovider.gitlab.GitConfiguration +import de.fraunhofer.iem.dataprovider.taskManager.model.Event +import de.fraunhofer.iem.dataprovider.taskManager.model.GetRepositoryDetailsDone +import de.fraunhofer.iem.dataprovider.toolRun.ToolRunService +import de.fraunhofer.iem.dataprovider.toolRun.model.Repository +import org.gitlab4j.api.GitLabApi +import org.gitlab4j.api.models.Commit +import org.gitlab4j.api.models.Project +import java.util.UUID + +class GetRepositoryDetailsTask( + private val repoId: UUID, + private val gitlabConfiguration: GitConfiguration, + private val toolRunService: ToolRunService, + override val responseChannel: suspend (event: Event) -> Unit, +) : Task() { + private val gitlabApi: GitLabApi = GitLabApi(gitlabConfiguration.host, gitlabConfiguration.accessToken) + + override suspend fun execute() { + logger.info("Gitlab config ${gitlabConfiguration.host} ${gitlabConfiguration.accessToken}") + val repositoryEntity = toolRunService.findRepoByID(repoId); + logger.info(repositoryEntity.toString()) + if (repositoryEntity != null) { + logger.info("ID: ${repositoryEntity.repoId}") + val project = gitlabApi.projectApi.getProject(repositoryEntity.repoId) + // Note: We only take commits from the default branch + val commits = gitlabApi.commitsApi.getCommits(project.id, project.defaultBranch, ".") + + val numberOfCommits = commits.count() + val numberOfSignedCommits = getNumberOfSignedCommits(repositoryEntity, commits) + val isDefaultBranchProtected = isDefaultBranchProtected(repositoryEntity, project) + val repositoryDetailsDto = RepositoryDetailsDto(repositoryEntity, numberOfCommits, numberOfSignedCommits, isDefaultBranchProtected) + logger.info("Collected repository details from $repoId (${repositoryDetailsDto}) successfully") + val repositoryDetailsEntity = toolRunService.createRepositoryDetails(repositoryDetailsDto); + logger.info("Stored repository details for $repoId (${repositoryDetailsEntity}) successfully") + responseChannel(GetRepositoryDetailsDone(taskID, repositoryDetailsEntity)) + } else { + logger.error("Repository $repoId can not be found in the database") + } + } + + // TODO: This should probably live somewhere else and encapsulate the logic + private fun getNumberOfSignedCommits(repositoryEntity: Repository, commits: List<Commit>): Int { + var numberOfSignedCommits = 0 + for (commit in commits) { + val signature = gitlabApi.commitsApi.getOptionalGpgSignature(repositoryEntity.repoId, commit.id) + if (signature !== null) { + numberOfSignedCommits++ + } + } + return numberOfSignedCommits + } + + // TODO: This should probably live somewhere else and encapsulate the logic + private fun isDefaultBranchProtected(repositoryEntity: Repository, project: Project): Boolean { + return try { + val defaultBranchName = project.defaultBranch; + val branch = gitlabApi.repositoryApi.getBranch(repositoryEntity.repoId, defaultBranchName); + branch.protected + } catch (e: Exception) { + // in theory, error probably happens if branch can't be found. In this case we default to false + false; + } + } +} diff --git a/src/main/kotlin/de/fraunhofer/iem/dataprovider/taskManager/tasks/RepositoryDetailsDto.kt b/src/main/kotlin/de/fraunhofer/iem/dataprovider/taskManager/tasks/RepositoryDetailsDto.kt new file mode 100644 index 0000000000000000000000000000000000000000..4c24fec643f567a9ae83cf37171e001ddbee389a --- /dev/null +++ b/src/main/kotlin/de/fraunhofer/iem/dataprovider/taskManager/tasks/RepositoryDetailsDto.kt @@ -0,0 +1,18 @@ +package de.fraunhofer.iem.dataprovider.taskManager.tasks + +import de.fraunhofer.iem.dataprovider.toolRun.model.Repository +import de.fraunhofer.iem.dataprovider.toolRun.model.RepositoryDetails +import java.sql.Timestamp +import java.time.Instant + +data class RepositoryDetailsDto(val repository: Repository, val numberOfCommits: Int, val numberOfSignedCommits: Int, val isDefaultBranchProtected: Boolean) + +fun RepositoryDetailsDto.toDbObject(): RepositoryDetails { + val repositoryDetails = RepositoryDetails() + repositoryDetails.repository = this.repository + repositoryDetails.timestamp = Timestamp.from(Instant.now()) + repositoryDetails.numberOfCommits = this.numberOfCommits + repositoryDetails.numberOfSignedCommits = this.numberOfSignedCommits + repositoryDetails.isDefaultBranchProtected = this.isDefaultBranchProtected + return repositoryDetails +} \ No newline at end of file diff --git a/src/main/kotlin/de/fraunhofer/iem/dataprovider/toolRun/RepositoryDetailsRepository.kt b/src/main/kotlin/de/fraunhofer/iem/dataprovider/toolRun/RepositoryDetailsRepository.kt new file mode 100644 index 0000000000000000000000000000000000000000..3d51a9d055f9fce679c2ee6dfc38d92ae1e23732 --- /dev/null +++ b/src/main/kotlin/de/fraunhofer/iem/dataprovider/toolRun/RepositoryDetailsRepository.kt @@ -0,0 +1,8 @@ +package de.fraunhofer.iem.dataprovider.toolRun + +import de.fraunhofer.iem.dataprovider.toolRun.model.RepositoryDetails +import org.springframework.data.jpa.repository.JpaRepository +import java.util.* + + +interface RepositoryDetailsRepository : JpaRepository<RepositoryDetails, UUID> {} diff --git a/src/main/kotlin/de/fraunhofer/iem/dataprovider/toolRun/ToolRunService.kt b/src/main/kotlin/de/fraunhofer/iem/dataprovider/toolRun/ToolRunService.kt index 2e8808b9dadf4c2dec73681937ee12580cb79399..c62e2cbcb4b31df425d9d98c1bf9058f63b64232 100644 --- a/src/main/kotlin/de/fraunhofer/iem/dataprovider/toolRun/ToolRunService.kt +++ b/src/main/kotlin/de/fraunhofer/iem/dataprovider/toolRun/ToolRunService.kt @@ -3,6 +3,7 @@ package de.fraunhofer.iem.dataprovider.toolRun import de.fraunhofer.iem.dataprovider.gitlab.Platform import de.fraunhofer.iem.dataprovider.logger.getLogger import de.fraunhofer.iem.dataprovider.taskManager.tasks.GitRepository +import de.fraunhofer.iem.dataprovider.taskManager.tasks.RepositoryDetailsDto import de.fraunhofer.iem.dataprovider.taskManager.tasks.toDbObject import de.fraunhofer.iem.dataprovider.toolRun.model.* import org.springframework.stereotype.Service @@ -11,6 +12,7 @@ import java.util.* @Service class ToolRunService( private val repositoryRepository: RepositoryRepository, + private val repositoryDetailsRepository: RepositoryDetailsRepository, private val toolRunRepository: ToolRunRepository, private val toolRepository: ToolRepository, private val ruleRepository: RuleRepository @@ -121,4 +123,18 @@ class ToolRunService( } return repo } + + // TODO: We should decide if we want to return optionals (null if not found) or throw exceptions or ... + // TODO: Must be consistent across all methods + fun findRepoByID(id: UUID): Repository? { + val repository = repositoryRepository.findById(id) + if (repository.isEmpty) { + return null + } + return repository.get() + } + + fun createRepositoryDetails(repositoryDetailsDto: RepositoryDetailsDto): RepositoryDetails { + return repositoryDetailsRepository.save(repositoryDetailsDto.toDbObject()) + } } diff --git a/src/main/kotlin/de/fraunhofer/iem/dataprovider/toolRun/model/RepositoryDetails.kt b/src/main/kotlin/de/fraunhofer/iem/dataprovider/toolRun/model/RepositoryDetails.kt new file mode 100644 index 0000000000000000000000000000000000000000..52a0fe37ae638b4f68f413c49d544c46a369470b --- /dev/null +++ b/src/main/kotlin/de/fraunhofer/iem/dataprovider/toolRun/model/RepositoryDetails.kt @@ -0,0 +1,32 @@ +package de.fraunhofer.iem.dataprovider.toolRun.model + +import jakarta.persistence.* +import org.hibernate.annotations.JdbcTypeCode +import org.hibernate.type.SqlTypes +import java.sql.Timestamp +import java.util.* + +@Entity +@Table(name = "repository_details") +class RepositoryDetails { + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + @Column(name = "id", nullable = false) + var id: UUID? = null + + @ManyToOne(fetch = FetchType.EAGER) + var repository: Repository? = null + + @Column(name = "number_of_commits") + var numberOfCommits: Int? = null + + @Column(name = "number_of_signed_commits") + var numberOfSignedCommits: Int? = null + + @Column(name = "is_default_branch_protected") + var isDefaultBranchProtected: Boolean? = null + + @JdbcTypeCode(SqlTypes.TIMESTAMP) + @Column(name = "timestamp") + var timestamp: Timestamp? = null +} \ No newline at end of file