Skip to content

Commit

Permalink
Execution run by contest id #4
Browse files Browse the repository at this point in the history
  • Loading branch information
nulls committed Aug 11, 2022
1 parent 6c73558 commit 1bc4755
Show file tree
Hide file tree
Showing 6 changed files with 119 additions and 38 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import com.saveourtool.save.utils.DATABASE_DELIMITER
import com.saveourtool.save.utils.debug
import com.saveourtool.save.utils.getLogger
import com.saveourtool.save.utils.switchIfEmptyToResponseException
import com.saveourtool.save.v1
import io.micrometer.core.instrument.MeterRegistry
import org.slf4j.Logger
import org.springframework.boot.web.reactive.function.client.WebClientCustomizer
Expand All @@ -38,48 +39,25 @@ import reactor.core.publisher.Mono
import reactor.core.scheduler.Schedulers

@RestController
@RequestMapping("/api/run")
@RequestMapping("/api/$v1/run")
class RunExecutionController(
private val projectService: ProjectService,
private val executionService: ExecutionService,
private val executionInfoStorage: ExecutionInfoStorage,
private val testService: TestService,
private val testExecutionService: TestExecutionService,
private val contestService: ContestService,
private val meterRegistry: MeterRegistry,
configProperties: ConfigProperties,
objectMapper: ObjectMapper,
// kotlinSerializationWebClientCustomizer: WebClientCustomizer,
) {
private val webClientOrchestrator = WebClient.builder()
.baseUrl(configProperties.orchestratorUrl)
.codecs {
it.defaultCodecs().multipartCodecs().encoder(Jackson2JsonEncoder(objectMapper))
}
// .apply(kotlinSerializationWebClientCustomizer::customize)
.build()
private val scheduler = Schedulers.boundedElastic()


@PostMapping("/triggerByContest")
fun triggerByContestId(
@RequestBody originalRequest: ExecutionRunRequestByContest,
authentication: Authentication,
): Mono<IdResponse> = justOrNotFound(contestService.findById(originalRequest.contestId))
.map { contest ->
ExecutionRunRequest(
projectCoordinates = originalRequest.projectCoordinates,
testSuiteIds = contest.testSuiteIds.split(DATABASE_DELIMITER).map { it.toLong() },
files = originalRequest.files,
sdk = originalRequest.sdk,
execCmd = originalRequest.execCmd,
batchSizeForAnalyzer = originalRequest.batchSizeForAnalyzer,
)
}
.flatMap {
trigger(it, authentication)
}

@PostMapping("/trigger")
fun trigger(
@RequestBody request: ExecutionRunRequest,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package com.saveourtool.save.backend.controllers

import com.saveourtool.save.backend.ByteBufferFluxResponse
import com.saveourtool.save.backend.StringResponse
import com.saveourtool.save.backend.configs.ApiSwaggerSupport
import com.saveourtool.save.backend.configs.ConfigProperties
import com.saveourtool.save.backend.configs.RequiresAuthorizationSourceHeader
import com.saveourtool.save.backend.service.*
import com.saveourtool.save.backend.storage.TestSuitesSourceSnapshotStorage
Expand All @@ -18,13 +20,16 @@ import io.swagger.v3.oas.annotations.responses.ApiResponse
import io.swagger.v3.oas.annotations.tags.Tag
import io.swagger.v3.oas.annotations.tags.Tags
import org.slf4j.Logger
import org.springframework.boot.web.reactive.function.client.WebClientCustomizer
import org.springframework.http.HttpStatus
import org.springframework.http.MediaType
import org.springframework.http.ResponseEntity
import org.springframework.http.codec.multipart.Part
import org.springframework.security.access.prepost.PreAuthorize
import org.springframework.security.core.Authentication
import org.springframework.web.bind.annotation.*
import org.springframework.web.reactive.function.client.WebClient
import org.springframework.web.reactive.function.client.toEntity
import reactor.core.publisher.Mono
import reactor.kotlin.core.publisher.toMono
import reactor.kotlin.core.util.function.component1
Expand All @@ -48,7 +53,14 @@ class TestSuitesSourceController(
private val organizationService: OrganizationService,
private val gitService: GitService,
private val executionService: ExecutionService,
configProperties: ConfigProperties,
jackson2WebClientCustomizer: WebClientCustomizer,
) {
private val preprocessorWebClient = WebClient.builder()
.apply(jackson2WebClientCustomizer::customize)
.baseUrl(configProperties.preprocessorUrl)
.build()

/**
* @param organizationName
* @return list of [TestSuitesSourceDto] found by provided values or empty response
Expand Down Expand Up @@ -503,15 +515,23 @@ class TestSuitesSourceController(
authentication: Authentication,
): Mono<List<String>> = testSuitesSourceService.getOrganizationsWithPublicTestSuiteSources().toMono()

@PostMapping("/api/$v1/test-suites-sources/{organizationName}/{name}/fetch")
fun triggerFetch(
@PathVariable organizationName: String,
@PathVariable name: String,
authentication: Authentication,
): ResponseEntity<Unit> {
testSuitesSourceService.findByName(organizationName, name)
return ResponseEntity.accepted()
.body(Unit)
}
): Mono<StringResponse> = blockingToMono { testSuitesSourceService.findByName(organizationName, name) }
.flatMap {
preprocessorWebClient.post()
.uri("/test-suites-sources/fetch")
.bodyValue(it.toDto())
.retrieve()
.toEntity<Unit>()
}
.map {
ResponseEntity.ok()
.body("Trigger fetching new tests from $name in $organizationName")
}

private fun TestSuitesSourceDto.downloadSnapshot(
version: String
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,8 @@ class ExecutionService(
execCmd = execCmd,
batchSizeForAnalyzer = batchSizeForAnalyzer,
)
log.info("Creating a new execution id=${execution.id} for project id=${project.id}")
return execution
val savedExecution = executionRepository.save(execution)
log.info("Created a new execution id=${savedExecution.id} for project id=${project.id}")
return savedExecution
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ package com.saveourtool.save.entities
import com.saveourtool.save.domain.FileInfo
import com.saveourtool.save.domain.ProjectCoordinates
import com.saveourtool.save.domain.Sdk
import kotlinx.serialization.Serializable

@Serializable
data class ExecutionRunRequest(
val projectCoordinates: ProjectCoordinates,

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

package com.saveourtool.save.frontend.components.basic

import com.saveourtool.save.entities.ContestDto
import com.saveourtool.save.entities.GitDto
import com.saveourtool.save.frontend.components.views.ProjectView
import com.saveourtool.save.frontend.externals.fontawesome.faQuestionCircle
Expand Down Expand Up @@ -49,6 +50,8 @@ external interface TestResourcesProps : PropsWithChildren {
var projectName: String
var organizationName: String
var onContestEnrollerResponse: (String) -> Unit
var availableContests: List<ContestDto>
var selectedContest: ContestDto

// properties for CUSTOM_TESTS mode
var availableGitCredentials: List<GitDto>
Expand Down Expand Up @@ -130,6 +133,7 @@ fun testResourcesSelection(
setExecCmd: (String) -> Unit,
setBatchSize: (String) -> Unit,
setSelectedLanguageForStandardTests: (String) -> Unit,
updateContestFromInputField: (ContestDto) -> Unit,
) = FC<TestResourcesProps> { props ->
val (isContestEnrollerOpen, setIsContestEnrollerOpen) = useState(false)
showContestEnrollerModal(
Expand Down Expand Up @@ -336,11 +340,30 @@ fun testResourcesSelection(
div {
className = ClassName(cardStyleByTestingType(props, TestingType.CONTEST_MODE))
div {
className = ClassName("card-body control-label col-auto justify-content-between justify-content-center font-weight-bold text-danger mb-4 pl-0")
+"Stay turned! Soon you will be able to run your tool in contest mode!"
className = ClassName("input-group-prepend")

select {
className = ClassName("form-control")
props.availableContests.forEach {
option {
+it.label()
}
}
required = true
value = props.selectedContest.label()
onChange = { event ->
val selectedContestLabel = event.target.value
val selectedContest = requireNotNull(props.availableContests.find { it.label() == selectedContestLabel }) {
"Invalid contest is selected $selectedContestLabel"
}
updateContestFromInputField(selectedContest)
}
}
}
}
}

private fun ContestDto.label(): String = "$organizationName/$name"

private fun cardStyleByTestingType(props: TestResourcesProps, testingType: TestingType) =
if (props.testingType == testingType) "card shadow mb-4 w-100" else "d-none"
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,21 @@ external interface ProjectExecutionRouteProps : PropsWithChildren {
var currentUserInfo: UserInfo?
}

external interface StateWithRole : State {
/**
* Role of a user that is seeing this view
*/
var selfRole: Role
}

external interface StandardTestsState : State {

}

/**
* [State] of project view component
*/
external interface ProjectViewState : State {
external interface ProjectViewState : StateWithRole {
/**
* Currently loaded for display Project
*/
Expand Down Expand Up @@ -206,15 +217,21 @@ external interface ProjectViewState : State {
*/
var closeButtonLabel: String?

/**
* Role of a user that is seeing this view
*/
var selfRole: Role

/**
* File for delete
*/
var file: FileInfo

/**
*
*/
var selectedContest: ContestDto

/**
*
*/
var availableContests: List<ContestDto>
}

/**
Expand Down Expand Up @@ -258,6 +275,11 @@ class ProjectView : AbstractView<ProjectExecutionRouteProps, ProjectViewState>(f
setState {
selectedLanguageForStandardTests = it
}
},
updateContestFromInputField = {
setState {
selectedContest = it
}
}
)
private val projectInfo = projectInfo(
Expand Down Expand Up @@ -293,6 +315,8 @@ class ProjectView : AbstractView<ProjectExecutionRouteProps, ProjectViewState>(f
)
state.selectedGitCredential = GitDto("N/A")
state.availableGitCredentials = emptyList()
state.selectedContest = ContestDto.empty
state.availableContests = emptyList()
state.gitBranchOrCommitFromInputField = ""
state.execCmd = ""
state.batchSizeForAnalyzer = ""
Expand Down Expand Up @@ -370,6 +394,13 @@ class ProjectView : AbstractView<ProjectExecutionRouteProps, ProjectViewState>(f
availableGitCredentials = gitCredentials
gitCredentials.firstOrNull()?.let { selectedGitCredential = it }
}

val contests = getContests()
setState {
availableContests = contests
contests.firstOrNull()?.let { selectedContest = it }
}

fetchLatestExecutionId()
}
}
Expand All @@ -378,6 +409,7 @@ class ProjectView : AbstractView<ProjectExecutionRouteProps, ProjectViewState>(f
private fun submitExecutionRequest() {
when (state.testingType) {
TestingType.CUSTOM_TESTS -> submitExecutionRequestWithCustomTests()
TestingType.CONTEST_MODE -> submitExecutionRequestByContest()
else -> {
if (selectedStandardSuites.isEmpty()) {
setState {
Expand Down Expand Up @@ -430,6 +462,19 @@ class ProjectView : AbstractView<ProjectExecutionRouteProps, ProjectViewState>(f
submitRequest("/submitExecutionRequest", Headers(), formData)
}

private fun submitExecutionRequestByContest() {
val selectedSdk = "${state.selectedSdk}:${state.selectedSdkVersion}".toSdk()
val executionRequest = ExecutionRunRequest(
projectCoordinates = ProjectCoordinates(state.project.organization.name, state.project.name),
testSuiteIds = state.selectedContest.testSuiteIds,
files = state.files.toList(),
sdk = selectedSdk,
execCmd = state.execCmd,
batchSizeForAnalyzer = state.batchSizeForAnalyzer,
)
submitRequest("/run/trigger", jsonHeaders, Json.encodeToString(executionRequest))
}

private fun submitRequest(url: String, headers: Headers, body: dynamic) {
scope.launch {
val response = post(
Expand Down Expand Up @@ -618,6 +663,7 @@ class ProjectView : AbstractView<ProjectExecutionRouteProps, ProjectViewState>(f
testingType = state.testingType
isSubmitButtonPressed = state.isSubmitButtonPressed
gitDto = gitDto
// properties for CONTEST_TESTS mode
projectName = props.name
organizationName = props.owner
onContestEnrollerResponse = {
Expand All @@ -627,6 +673,8 @@ class ProjectView : AbstractView<ProjectExecutionRouteProps, ProjectViewState>(f
errorLabel = "Contest enrollment"
}
}
selectedContest = state.selectedContest
availableContests = state.availableContests
// properties for CUSTOM_TESTS mode
testRootPath = state.testRootPath
selectedGitCredential = state.selectedGitCredential
Expand Down Expand Up @@ -968,6 +1016,15 @@ class ProjectView : AbstractView<ProjectExecutionRouteProps, ProjectViewState>(f
it.decodeFromJsonString<List<GitDto>>()
}

private suspend fun getContests() = get(
"$apiUrl/contests/active",
Headers(),
loadingHandler = ::noopLoadingHandler,
)
.unsafeMap {
it.decodeFromJsonString<List<ContestDto>>()
}

companion object :
RStatics<ProjectExecutionRouteProps, ProjectViewState, ProjectView, Context<RequestStatusContext>>(ProjectView::class) {
const val TEST_ROOT_DIR_HINT = """
Expand Down

0 comments on commit 1bc4755

Please sign in to comment.