Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/feature/organization_list_view' …
Browse files Browse the repository at this point in the history
…into feature/organization_list_view
  • Loading branch information
sanyavertolet committed May 16, 2022
2 parents 99cd734 + a5b5cfa commit f302d4b
Show file tree
Hide file tree
Showing 8 changed files with 75 additions and 23 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
/**
* Utilities related to Swagger
*/

package org.cqfn.save.backend.configs

import io.swagger.v3.oas.annotations.Parameter
import io.swagger.v3.oas.annotations.enums.ParameterIn
import io.swagger.v3.oas.annotations.enums.SecuritySchemeType
import io.swagger.v3.oas.annotations.responses.ApiResponse
import io.swagger.v3.oas.annotations.responses.ApiResponses
Expand All @@ -17,3 +23,14 @@ import io.swagger.v3.oas.annotations.security.SecurityScheme
ApiResponse(responseCode = "401", description = "Unauthorized", content = [])
)
annotation class ApiSwaggerSupport

/**
* Indicates that an operation requires `X-Authorization-Source` header to be set
*/
@Parameter(
`in` = ParameterIn.HEADER,
name = "X-Authorization-Source",
required = true,
example = "basic"
)
annotation class RequiresAuthorizationSourceHeader
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.cqfn.save.backend.controllers

import org.cqfn.save.backend.configs.ApiSwaggerSupport
import org.cqfn.save.backend.configs.RequiresAuthorizationSourceHeader
import org.cqfn.save.backend.security.OrganizationPermissionEvaluator
import org.cqfn.save.backend.security.ProjectPermissionEvaluator
import org.cqfn.save.backend.service.OrganizationService
Expand All @@ -15,8 +16,6 @@ import org.cqfn.save.permission.SetRoleRequest
import org.cqfn.save.v1

import io.swagger.v3.oas.annotations.Operation
import io.swagger.v3.oas.annotations.Parameter
import io.swagger.v3.oas.annotations.enums.ParameterIn
import io.swagger.v3.oas.annotations.responses.ApiResponse
import io.swagger.v3.oas.annotations.tags.Tag
import io.swagger.v3.oas.annotations.tags.Tags
Expand Down Expand Up @@ -54,10 +53,8 @@ class PermissionController(
@GetMapping("/projects/{organizationName}/{projectName}/users/roles")
@Operation(
description = "Get role for a user on a particular project. Returns self role if no userName is set.",
parameters = [
Parameter(`in` = ParameterIn.HEADER, name = "X-Authorization-Source", required = true),
]
)
@RequiresAuthorizationSourceHeader
@ApiResponse(responseCode = "200", description = "Successfully fetched user's role")
@ApiResponse(
responseCode = "404", description = "Requested user or project doesn't exist or the user doesn't have enough permissions " +
Expand Down Expand Up @@ -92,10 +89,8 @@ class PermissionController(
@PostMapping("/projects/{organizationName}/{projectName}/users/roles")
@Operation(
description = "Set role for a user on a particular project",
parameters = [
Parameter(`in` = ParameterIn.HEADER, name = "X-Authorization-Source", required = true),
]
)
@RequiresAuthorizationSourceHeader
@ApiResponse(responseCode = "200", description = "Permission added")
@ApiResponse(responseCode = "403", description = "User doesn't have permissions to manage this members")
@ApiResponse(responseCode = "404", description = "Requested user or project doesn't exist")
Expand Down Expand Up @@ -138,10 +133,8 @@ class PermissionController(
@DeleteMapping("/projects/{organizationName}/{projectName}/users/roles/{userName}")
@Operation(
description = "Removes user's role on a particular project",
parameters = [
Parameter(`in` = ParameterIn.HEADER, name = "X-Authorization-Source", required = true),
]
)
@RequiresAuthorizationSourceHeader
@ApiResponse(responseCode = "200", description = "Permission removed")
@ApiResponse(responseCode = "403", description = "User doesn't have permissions to manage this members")
@ApiResponse(responseCode = "404", description = "Requested user or project doesn't exist")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,42 +3,50 @@ package org.cqfn.save.backend.controllers
import org.cqfn.save.agent.AgentState
import org.cqfn.save.agent.TestExecutionDto
import org.cqfn.save.agent.TestSuiteExecutionStatisticDto
import org.cqfn.save.backend.configs.ApiSwaggerSupport
import org.cqfn.save.backend.configs.RequiresAuthorizationSourceHeader
import org.cqfn.save.backend.repository.TestDataFilesystemRepository
import org.cqfn.save.backend.security.ProjectPermissionEvaluator
import org.cqfn.save.backend.service.ExecutionService
import org.cqfn.save.backend.service.TestExecutionService
import org.cqfn.save.backend.utils.justOrNotFound
import org.cqfn.save.core.utils.runIf
import org.cqfn.save.domain.TestResultLocation
import org.cqfn.save.domain.TestResultStatus
import org.cqfn.save.permission.Permission
import org.cqfn.save.test.TestDto
import org.cqfn.save.v1

import io.swagger.v3.oas.annotations.tags.Tag
import io.swagger.v3.oas.annotations.tags.Tags
import org.slf4j.LoggerFactory
import org.springframework.dao.DataAccessException
import org.springframework.http.HttpStatus
import org.springframework.http.ResponseEntity
import org.springframework.security.core.Authentication
import org.springframework.transaction.annotation.Transactional
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RequestParam
import org.springframework.web.bind.annotation.RestController
import org.springframework.web.bind.annotation.*
import org.springframework.web.server.ResponseStatusException
import reactor.core.publisher.Flux
import reactor.core.publisher.Mono

import java.math.BigInteger

import kotlin.io.path.exists

/**
* Controller to work with test execution
*
* @param testExecutionService service for test execution
*/
@ApiSwaggerSupport
@Tags(Tag(name = "test-executions"))
@RestController
@Transactional
class TestExecutionController(private val testExecutionService: TestExecutionService,
private val executionService: ExecutionService,
private val projectPermissionEvaluator: ProjectPermissionEvaluator,
private val testDataFilesystemRepository: TestDataFilesystemRepository
) {
/**
* Returns a page of [TestExecutionDto]s with [executionId]
Expand All @@ -49,24 +57,38 @@ class TestExecutionController(private val testExecutionService: TestExecutionSer
* @param status
* @param testSuite
* @param authentication
* @param checkDebugInfo if true, response will contain information about whether debug info data is available for this test execution
* @return a list of [TestExecutionDto]s
*/
@GetMapping(path = ["/api/$v1/testExecutions"])
@RequiresAuthorizationSourceHeader
@Suppress("LongParameterList", "TOO_MANY_PARAMETERS", "TYPE_ALIAS")
fun getTestExecutions(
@RequestParam executionId: Long,
@RequestParam page: Int,
@RequestParam size: Int,
@RequestParam(required = false) status: TestResultStatus?,
@RequestParam(required = false) testSuite: String?,
@RequestParam(required = false, defaultValue = "false") checkDebugInfo: Boolean,
authentication: Authentication,
): Mono<List<TestExecutionDto>> = justOrNotFound(executionService.findExecution(executionId)).filterWhen {
): Flux<TestExecutionDto> = justOrNotFound(executionService.findExecution(executionId)).filterWhen {
projectPermissionEvaluator.checkPermissions(authentication, it, Permission.READ)
}
.map {
.flatMapIterable {
log.debug("Request to get test executions on page $page with size $size for execution $executionId")
testExecutionService.getTestExecutions(executionId, page, size, status, testSuite)
.map { it.toDto() }
}
.map { it.toDto() }
.runIf({ checkDebugInfo }) {
map { testExecutionDto ->
val debugInfoFile = testDataFilesystemRepository.getLocation(
executionId,
testExecutionDto
)
testExecutionDto.copy(
hasDebugInfo = debugInfoFile.exists()
)
}
}

/**
Expand All @@ -78,6 +100,7 @@ class TestExecutionController(private val testExecutionService: TestExecutionSer
* @return a list of [TestExecutionDto]s
*/
@GetMapping(path = ["/api/$v1/testLatestExecutions"])
@RequiresAuthorizationSourceHeader
@Suppress("TYPE_ALIAS", "MagicNumber")
fun getTestExecutionsByStatus(
@RequestParam executionId: Long,
Expand Down Expand Up @@ -120,6 +143,7 @@ class TestExecutionController(private val testExecutionService: TestExecutionSer
* @return TestExecution
*/
@PostMapping(path = ["/api/$v1/testExecutions"])
@RequiresAuthorizationSourceHeader
fun getTestExecutionByLocation(@RequestParam executionId: Long,
@RequestBody testResultLocation: TestResultLocation,
authentication: Authentication,
Expand All @@ -146,6 +170,7 @@ class TestExecutionController(private val testExecutionService: TestExecutionSer
* @param authentication
*/
@GetMapping(path = ["/api/$v1/testExecution/count"])
@RequiresAuthorizationSourceHeader
fun getTestExecutionsCount(
@RequestParam executionId: Long,
@RequestParam(required = false) status: TestResultStatus?,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package org.cqfn.save.backend.repository

import org.cqfn.save.agent.TestExecutionDto
import org.cqfn.save.backend.configs.ConfigProperties
import org.cqfn.save.domain.TestResultDebugInfo
import org.cqfn.save.domain.TestResultLocation
import org.cqfn.save.entities.TestExecution

import com.fasterxml.jackson.databind.ObjectMapper
import okio.Path.Companion.toPath
import org.springframework.core.io.FileSystemResource
import org.springframework.stereotype.Repository
import org.springframework.transaction.annotation.Transactional
Expand Down Expand Up @@ -73,6 +75,20 @@ class TestDataFilesystemRepository(configProperties: ConfigProperties,
return getLocation(testExecution.execution.id!!, testResultLocation)
}

/**
* Get location of additional data for [testExecutionDto]
*
* @param executionId
* @param testExecutionDto
* @return path to file with additional data
*/
@Suppress("UnsafeCallOnNullableType")
fun getLocation(executionId: Long, testExecutionDto: TestExecutionDto): Path {
val path = testExecutionDto.filePath.toPath()
val testResultLocation = TestResultLocation(testExecutionDto.testSuiteName!!, testExecutionDto.pluginName, path.parent.toString(), path.name)
return getLocation(executionId, testResultLocation)
}

/**
* @param executionId
* @param testResultLocation
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import kotlinx.serialization.Serializable
* @property tags list of tags of current test
* @property missingWarnings missing warnings
* @property matchedWarnings matched warnings
* @property hasDebugInfo whether debug info data is available for this test execution
*/
@Serializable
data class TestExecutionDto(
Expand All @@ -28,4 +29,5 @@ data class TestExecutionDto(
val tags: List<String> = emptyList(),
val missingWarnings: Int?,
val matchedWarnings: Int?,
val hasDebugInfo: Boolean? = null,
)
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ class TestExecution(
*
* @return a new [TestExecutionDto]
*/
@Suppress("UnsafeCallOnNullableType")
fun toDto() = TestExecutionDto(
test.filePath,
test.pluginName,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -322,7 +322,7 @@ class ExecutionView : AbstractView<ExecutionProps, ExecutionState>(false) {
""
}
get(
url = "$apiUrl/testExecutions?executionId=${props.executionId}&page=$page&size=$size$status$testSuite",
url = "$apiUrl/testExecutions?executionId=${props.executionId}&page=$page&size=$size$status$testSuite&checkDebugInfo=true",
headers = Headers().apply {
set("Accept", "application/json")
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ fun testExecutionDetailsView() = fc<Props> {
// fixme: after https://github.com/analysis-dev/save-cloud/issues/364 can be passed via history state to avoid requests
useRequest(arrayOf(status, testResultDebugInfo, executionId, testResultLocation), isDeferred = false) {
val testExecutionDtoResponse = post(
"$apiUrl/testExecutions?executionId=$executionId",
"$apiUrl/testExecutions?executionId=$executionId&checkDebugInfo=true",
Headers().apply {
set("Content-Type", "application/json")
},
Expand Down

0 comments on commit f302d4b

Please sign in to comment.