From 528314e165d5acefcad480297fd39b1e077af6bf Mon Sep 17 00:00:00 2001 From: Alexander Frolov Date: Tue, 23 Aug 2022 16:55:43 +0300 Subject: [PATCH] Fixes for `TestSuiteSelector` and `TestSuiteSourceCreationComponent` (#1091) Fixes for TestSuiteSelector and TestSuiteSourceCreationComponent ### What's done: * Added (returned back) testSuiteSelector for general tests - all the avaliable ones * Fixed an issue with TestSuiteSelector in browser mode - now input form is cleared when an entity from avaliable list is chosen * Fixed an issue with unclosing modal - now when TestSuiteSource is successfully created, a new request for a fetch is sent (#1079) --- save-backend/backend-api-docs.json | 51 +++++++++++++++++++ .../controllers/TestSuitesSourceController.kt | 19 ++++++- .../service/TestSuitesSourceService.kt | 5 ++ .../basic/TestSuiteSourceCreationComponent.kt | 10 ++++ .../contests/ContestCreationComponent.kt | 6 +-- .../testsuiteselector/TestSuiteSelector.kt | 39 ++++++++++++-- .../TestSuiteSelectorBrowserMode.kt | 37 ++++++++------ 7 files changed, 142 insertions(+), 25 deletions(-) diff --git a/save-backend/backend-api-docs.json b/save-backend/backend-api-docs.json index 61514c6224..90dfccc0e7 100644 --- a/save-backend/backend-api-docs.json +++ b/save-backend/backend-api-docs.json @@ -3983,6 +3983,57 @@ ] } }, + "/api/v1/test-suites-sources/avaliable": { + "get": { + "tags": [ + "test-suites-source" + ], + "summary": "Get organizations with public test suite sources.", + "description": "Get list of organizations with public test suite sources", + "operationId": "getOrganizationNamesWithPublicTestSuiteSources", + "parameters": [ + { + "name": "X-Authorization-Source", + "in": "header", + "required": true, + "example": "basic" + } + ], + "responses": { + "200": { + "description": "Successfully fetched organizations with public test suite sources.", + "content": { + "*/*": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/TestSuitesSourceDto" + } + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "*/*": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/TestSuitesSourceDto" + } + } + } + } + } + }, + "security": [ + { + "basic": [] + } + ] + } + }, "/api/v1/projects/": { "get": { "tags": [ diff --git a/save-backend/src/main/kotlin/com/saveourtool/save/backend/controllers/TestSuitesSourceController.kt b/save-backend/src/main/kotlin/com/saveourtool/save/backend/controllers/TestSuitesSourceController.kt index a6c8dfd31b..1cded31631 100644 --- a/save-backend/src/main/kotlin/com/saveourtool/save/backend/controllers/TestSuitesSourceController.kt +++ b/save-backend/src/main/kotlin/com/saveourtool/save/backend/controllers/TestSuitesSourceController.kt @@ -351,8 +351,7 @@ class TestSuitesSourceController( when (testSuitesSourceService.createSourceIfNotPresent(testSuitesSource)) { SourceSaveStatus.EXIST -> Mono.just(ResponseEntity.status(HttpStatus.CONFLICT).body(SourceSaveStatus.EXIST)) SourceSaveStatus.CONFLICT -> Mono.just(ResponseEntity.status(HttpStatus.CONFLICT).body(SourceSaveStatus.CONFLICT)) - SourceSaveStatus.NEW -> testSuitesSourceService.fetch(testSuitesSource.toDto()) - .map { ResponseEntity.ok(SourceSaveStatus.NEW) } + SourceSaveStatus.NEW -> Mono.just(ResponseEntity.ok(SourceSaveStatus.NEW)) } } @@ -513,6 +512,22 @@ class TestSuitesSourceController( } } + @GetMapping("/api/$v1/test-suites-sources/avaliable") + @RequiresAuthorizationSourceHeader + @PreAuthorize("permitAll()") + @Operation( + method = "GET", + summary = "Get organizations with public test suite sources.", + description = "Get list of organizations with public test suite sources", + ) + @ApiResponse(responseCode = "200", description = "Successfully fetched organizations with public test suite sources.") + fun getOrganizationNamesWithPublicTestSuiteSources( + authentication: Authentication, + ): Mono = testSuitesSourceService.getAvaliableTestSuiteSources().toMono() + .map {testSuitesSourceList -> + testSuitesSourceList.map { it.toDto() } + } + private fun TestSuitesSourceDto.downloadSnapshot( version: String ): Mono = testSuitesSourceSnapshotStorage.findKey(organizationName, name, version) diff --git a/save-backend/src/main/kotlin/com/saveourtool/save/backend/service/TestSuitesSourceService.kt b/save-backend/src/main/kotlin/com/saveourtool/save/backend/service/TestSuitesSourceService.kt index 83dc331337..6d3120cda6 100644 --- a/save-backend/src/main/kotlin/com/saveourtool/save/backend/service/TestSuitesSourceService.kt +++ b/save-backend/src/main/kotlin/com/saveourtool/save/backend/service/TestSuitesSourceService.kt @@ -166,6 +166,11 @@ class TestSuitesSourceService( } } + /** + * @return list of organizations that have open public test suite sources + */ + fun getAvaliableTestSuiteSources(): List = testSuitesSourceRepository.findAll() + /** * @param testSuitesSource test suites source which requested to be fetched * @return empty response diff --git a/save-frontend/src/main/kotlin/com/saveourtool/save/frontend/components/basic/TestSuiteSourceCreationComponent.kt b/save-frontend/src/main/kotlin/com/saveourtool/save/frontend/components/basic/TestSuiteSourceCreationComponent.kt index 0add02f189..8e00c6c420 100644 --- a/save-frontend/src/main/kotlin/com/saveourtool/save/frontend/components/basic/TestSuiteSourceCreationComponent.kt +++ b/save-frontend/src/main/kotlin/com/saveourtool/save/frontend/components/basic/TestSuiteSourceCreationComponent.kt @@ -103,6 +103,15 @@ fun ChildrenBuilder.showTestSuiteSourceCreationModal( private fun testSuiteSourceCreationComponent() = FC { props -> val (testSuiteSource, setTestSuiteSource) = useState(TestSuitesSourceDto.empty.copy(organizationName = props.organizationName)) val (saveStatus, setSaveStatus) = useState(null) + val fetchTestSuiteSource = useRequest { + post( + url = "$apiUrl/test-suites-sources/${testSuiteSource.organizationName}/${testSuiteSource.name}/fetch", + headers = jsonHeaders, + body = undefined, + loadingHandler = ::noopLoadingHandler, + responseHandler = ::noopResponseHandler, + ) + } val onSubmitButtonPressed = useRequest { val response = post( url = "/api/$v1/test-suites-sources/create", @@ -112,6 +121,7 @@ private fun testSuiteSourceCreationComponent() = FC()) diff --git a/save-frontend/src/main/kotlin/com/saveourtool/save/frontend/components/basic/contests/ContestCreationComponent.kt b/save-frontend/src/main/kotlin/com/saveourtool/save/frontend/components/basic/contests/ContestCreationComponent.kt index 4fe8e9029e..7d985ea22f 100644 --- a/save-frontend/src/main/kotlin/com/saveourtool/save/frontend/components/basic/contests/ContestCreationComponent.kt +++ b/save-frontend/src/main/kotlin/com/saveourtool/save/frontend/components/basic/contests/ContestCreationComponent.kt @@ -4,7 +4,7 @@ package com.saveourtool.save.frontend.components.basic.contests import com.saveourtool.save.entities.ContestDto import com.saveourtool.save.frontend.components.basic.* -import com.saveourtool.save.frontend.components.basic.testsuiteselector.showPublicTestSuitesSelectorModal +import com.saveourtool.save.frontend.components.basic.testsuiteselector.showGeneralTestSuitesSelectorModal import com.saveourtool.save.frontend.externals.modal.CssProperties import com.saveourtool.save.frontend.externals.modal.Styles import com.saveourtool.save.frontend.externals.modal.modal @@ -150,10 +150,10 @@ private fun contestCreationComponent() = FC { pro div { className = ClassName("card") contestCreationCard { - showPublicTestSuitesSelectorModal( + showGeneralTestSuitesSelectorModal( contestDto.testSuiteIds, testSuitesSelectorWindowOpenness, - useState(emptyList()) + useState(emptyList()), ) { setContestDto(contestDto.copy(testSuiteIds = it)) } diff --git a/save-frontend/src/main/kotlin/com/saveourtool/save/frontend/components/basic/testsuiteselector/TestSuiteSelector.kt b/save-frontend/src/main/kotlin/com/saveourtool/save/frontend/components/basic/testsuiteselector/TestSuiteSelector.kt index 2ac3119cf7..fc34dd4035 100644 --- a/save-frontend/src/main/kotlin/com/saveourtool/save/frontend/components/basic/testsuiteselector/TestSuiteSelector.kt +++ b/save-frontend/src/main/kotlin/com/saveourtool/save/frontend/components/basic/testsuiteselector/TestSuiteSelector.kt @@ -36,9 +36,14 @@ external interface TestSuiteSelectorProps : Props { /** * Specific organization name which reduces list of test suites source. - * If it's null we show public tests + * If null, all the test suites are shown */ var specificOrganizationName: String? + + /** + * If this flag is true public tests will be shown + */ + var isStandardMode: Boolean } /** @@ -52,6 +57,8 @@ enum class TestSuiteSelectorMode { } /** + * Browse standard test suites + * * @param initTestSuiteIds initial value * @param windowOpenness state to control openness of window * @param testSuiteIdsInSelectorState state for intermediate result in selector @@ -63,10 +70,29 @@ fun ChildrenBuilder.showPublicTestSuitesSelectorModal( testSuiteIdsInSelectorState: StateInstance>, setSelectedTestSuiteIds: (List) -> Unit, ) { - showTestSuitesSelectorModal(null, initTestSuiteIds, windowOpenness, testSuiteIdsInSelectorState, setSelectedTestSuiteIds) + showTestSuitesSelectorModal(null, true, initTestSuiteIds, windowOpenness, testSuiteIdsInSelectorState, setSelectedTestSuiteIds) +} + +/** + * Browse all the avaliable test suites. + * + * @param initTestSuiteIds initial value + * @param windowOpenness state to control openness of window + * @param testSuiteIdsInSelectorState state for intermediate result in selector + * @param setSelectedTestSuiteIds consumer for result + */ +fun ChildrenBuilder.showGeneralTestSuitesSelectorModal( + initTestSuiteIds: List, + windowOpenness: WindowOpenness, + testSuiteIdsInSelectorState: StateInstance>, + setSelectedTestSuiteIds: (List) -> Unit, +) { + showTestSuitesSelectorModal(null, false, initTestSuiteIds, windowOpenness, testSuiteIdsInSelectorState, setSelectedTestSuiteIds) } /** + * Browse test suites of a given organization + * * @param organizationName * @param initTestSuiteIds initial value * @param windowOpenness state to control openness of window @@ -80,11 +106,13 @@ fun ChildrenBuilder.showPrivateTestSuitesSelectorModal( testSuiteIdsInSelectorState: StateInstance>, setSelectedTestSuiteIds: (List) -> Unit, ) { - showTestSuitesSelectorModal(organizationName, initTestSuiteIds, windowOpenness, testSuiteIdsInSelectorState, setSelectedTestSuiteIds) + showTestSuitesSelectorModal(organizationName, false, initTestSuiteIds, windowOpenness, testSuiteIdsInSelectorState, setSelectedTestSuiteIds) } +@Suppress("TOO_MANY_PARAMETERS", "LongParameterList") private fun ChildrenBuilder.showTestSuitesSelectorModal( specificOrganizationName: String?, + isStandardMode: Boolean, initTestSuiteIds: List, windowOpenness: WindowOpenness, testSuiteIdsInSelectorState: StateInstance>, @@ -102,7 +130,7 @@ private fun ChildrenBuilder.showTestSuitesSelectorModal( currentlySelectedTestSuiteIds = initTestSuiteIds windowOpenness.closeWindow() } - showTestSuitesSelectorModal(windowOpenness.isOpen(), specificOrganizationName, initTestSuiteIds, onSubmit, onTestSuiteIdUpdate, onCancel) + showTestSuitesSelectorModal(windowOpenness.isOpen(), specificOrganizationName, isStandardMode, initTestSuiteIds, onSubmit, onTestSuiteIdUpdate, onCancel) } @Suppress( @@ -114,6 +142,7 @@ private fun ChildrenBuilder.showTestSuitesSelectorModal( private fun ChildrenBuilder.showTestSuitesSelectorModal( isOpen: Boolean, specificOrganizationName: String?, + isStandardMode: Boolean, preselectedTestSuiteIds: List, onSubmit: () -> Unit, onTestSuiteIdUpdate: (List) -> Unit, @@ -150,6 +179,7 @@ private fun ChildrenBuilder.showTestSuitesSelectorModal( this.onTestSuiteIdUpdate = onTestSuiteIdUpdate this.preselectedTestSuiteIds = preselectedTestSuiteIds this.specificOrganizationName = specificOrganizationName + this.isStandardMode = isStandardMode } } @@ -233,6 +263,7 @@ private fun testSuiteSelector() = FC { props -> this.onTestSuiteIdsUpdate = props.onTestSuiteIdUpdate this.preselectedTestSuiteIds = props.preselectedTestSuiteIds this.specificOrganizationName = props.specificOrganizationName + this.isStandardMode = props.isStandardMode } TestSuiteSelectorMode.SEARCH -> testSuiteSelectorSearchMode { this.onTestSuiteIdsUpdate = props.onTestSuiteIdUpdate diff --git a/save-frontend/src/main/kotlin/com/saveourtool/save/frontend/components/basic/testsuiteselector/TestSuiteSelectorBrowserMode.kt b/save-frontend/src/main/kotlin/com/saveourtool/save/frontend/components/basic/testsuiteselector/TestSuiteSelectorBrowserMode.kt index 979d7f5f09..558514f7e8 100644 --- a/save-frontend/src/main/kotlin/com/saveourtool/save/frontend/components/basic/testsuiteselector/TestSuiteSelectorBrowserMode.kt +++ b/save-frontend/src/main/kotlin/com/saveourtool/save/frontend/components/basic/testsuiteselector/TestSuiteSelectorBrowserMode.kt @@ -48,9 +48,14 @@ external interface TestSuiteSelectorBrowserModeProps : Props { /** * Specific organization name which reduces list of test suites source. - * If it's null we show public tests + * If null, all the test suites are shown */ var specificOrganizationName: String? + + /** + * If this flag is true public tests will be shown + */ + var isStandardMode: Boolean } @Suppress( @@ -158,22 +163,20 @@ private fun testSuiteSelectorBrowserMode() = FC>(emptyList()) val (availableTestSuiteSources, setAvailableTestSuiteSources) = useState>(emptyList()) + useRequest { - val response = props.specificOrganizationName?.let { organizationName -> - get( - url = "$apiUrl/test-suites-sources/$organizationName/list", - headers = jsonHeaders, - loadingHandler = ::noopLoadingHandler, - responseHandler = ::noopResponseHandler, - ) - } ?: run { - get( - url = "$apiUrl/test-suites-sources/public-list", - headers = jsonHeaders, - loadingHandler = ::noopLoadingHandler, - responseHandler = ::noopResponseHandler, - ) + val url = when { + props.isStandardMode -> "$apiUrl/test-suites-sources/public-list" + props.specificOrganizationName != null -> "$apiUrl/test-suites-sources/avaliable" + else -> "$apiUrl/test-suites-sources/${props.specificOrganizationName}/list" } + val response = get( + url = url, + headers = jsonHeaders, + loadingHandler = ::noopLoadingHandler, + responseHandler = ::noopResponseHandler, + ) + val testSuitesSources: TestSuitesSourceDtoList = response.decodeFromJsonString() setAvailableOrganizations(testSuitesSources.map { it.organizationName }.distinct()) setAvailableTestSuiteSources(testSuitesSources.map { it.name }) @@ -291,7 +294,6 @@ private fun testSuiteSelectorBrowserMode() = FC setSelectedOrganization(organization) + setNamePrefix("") } selectedTestSuiteSource == null -> showAvaliableOptions( availableTestSuiteSources.filter { it.contains(namePrefix, true) } ) { testSuiteSource -> setSelectedTestSuiteSource(testSuiteSource) + setNamePrefix("") } selectedTestSuiteVersion == null -> showAvaliableOptions( availableTestSuitesVersions.filter { it.contains(namePrefix, true) } ) { testSuiteVersion -> setSelectedTestSuiteVersion(testSuiteVersion) + setNamePrefix("") } else -> showAvaliableTestSuites( availableTestSuites.filter { it.name.contains(namePrefix, true) },