From 8d1f99dcaaebed1702aae2dd469698492e3ff7c3 Mon Sep 17 00:00:00 2001 From: Katsiaryna Tsytsenia Date: Fri, 27 Sep 2024 13:50:18 +0300 Subject: [PATCH] IJMP-1898 Zowe config from .json enchancement: plus button when connection is not added yet Fixed adding invalid connections from Zowe config file Signed-off-by: Katsiaryna Tsytsenia --- .../zowe/actions/UpdateZoweConfigAction.kt | 8 + .../zowe/service/ZoweConfigServiceImpl.kt | 197 ++++++++---------- .../explorer/config/ZoweConfigTestSpec.kt | 26 ++- 3 files changed, 111 insertions(+), 120 deletions(-) diff --git a/src/main/kotlin/org/zowe/explorer/zowe/actions/UpdateZoweConfigAction.kt b/src/main/kotlin/org/zowe/explorer/zowe/actions/UpdateZoweConfigAction.kt index 7ef55583..c78a79d2 100644 --- a/src/main/kotlin/org/zowe/explorer/zowe/actions/UpdateZoweConfigAction.kt +++ b/src/main/kotlin/org/zowe/explorer/zowe/actions/UpdateZoweConfigAction.kt @@ -14,6 +14,7 @@ package org.zowe.explorer.zowe.actions +import com.intellij.icons.AllIcons import com.intellij.openapi.actionSystem.ActionUpdateThread import com.intellij.openapi.actionSystem.AnActionEvent import com.intellij.openapi.actionSystem.CommonDataKeys @@ -92,6 +93,13 @@ class UpdateZoweConfigAction : DumbAwareAction() { zoweConfigService.globalZoweConfig?.extractSecureProperties(vFile.path.split("/").toTypedArray()) } val zoweState = zoweConfigService.getZoweConfigState(false, type = type) + if (zoweState == ZoweConfigState.NEED_TO_ADD) { + e.presentation.icon = AllIcons.General.Add + e.presentation.text = "Add Zowe Config connections" + } else { + e.presentation.icon = AllIcons.Actions.BuildLoadChanges + e.presentation.text = "Update Zowe Config connections" + } e.presentation.isEnabledAndVisible = zoweState == ZoweConfigState.NEED_TO_UPDATE || zoweState == ZoweConfigState.NEED_TO_ADD if (type == ZoweConfigType.LOCAL) { diff --git a/src/main/kotlin/org/zowe/explorer/zowe/service/ZoweConfigServiceImpl.kt b/src/main/kotlin/org/zowe/explorer/zowe/service/ZoweConfigServiceImpl.kt index 94024bb6..1e5745cf 100644 --- a/src/main/kotlin/org/zowe/explorer/zowe/service/ZoweConfigServiceImpl.kt +++ b/src/main/kotlin/org/zowe/explorer/zowe/service/ZoweConfigServiceImpl.kt @@ -14,10 +14,8 @@ package org.zowe.explorer.zowe.service -import com.intellij.notification.Notification import com.intellij.notification.NotificationGroupManager import com.intellij.notification.NotificationType -import com.intellij.notification.Notifications import com.intellij.openapi.actionSystem.AnActionEvent import com.intellij.openapi.application.runReadAction import com.intellij.openapi.application.runWriteAction @@ -36,6 +34,7 @@ import org.zowe.explorer.dataops.DataOpsManager import org.zowe.explorer.dataops.operations.InfoOperation import org.zowe.explorer.dataops.operations.ZOSInfoOperation import org.zowe.explorer.explorer.EXPLORER_NOTIFICATION_GROUP_ID +import org.zowe.explorer.telemetry.NotificationsService import org.zowe.explorer.utils.crudable.find import org.zowe.explorer.utils.crudable.getAll import org.zowe.explorer.utils.runTask @@ -62,9 +61,6 @@ import java.util.regex.Pattern import java.util.stream.Collectors import kotlin.collections.set - -const val ZOWE_CONFIG_NOTIFICATION_GROUP_ID = "org.zowe.explorerzowe.service.ZoweConfigNotificationGroupId" - const val ZOWE_PROJECT_PREFIX = "zowe-" /** @@ -121,22 +117,6 @@ class ZoweConfigServiceImpl(override val myProject: Project) : ZoweConfigService override var globalZoweConfig: ZoweConfig? = null - /** - * Displays an error notification if an error was received. - * @param t thrown error. - * @param title error text. - */ - private fun notifyError(t: Throwable, title: String? = null) { - Notifications.Bus.notify( - Notification( - ZOWE_CONFIG_NOTIFICATION_GROUP_ID, - title ?: "Error with Zowe config file", - t.message ?: t.toString(), - NotificationType.ERROR - ) - ) - } - /** * Checks project contains zowe.config.json. If zowe config presented * it will parse it and save to object model inside zoweConfig field. @@ -186,12 +166,12 @@ class ZoweConfigServiceImpl(override val myProject: Project) : ZoweConfigService * @return ConnectionConfig instance related to zowe config if it exists or null otherwise. */ private fun findExistingConnection(type: ZoweConfigType, profileName: String): ConnectionConfig? { - val zoweConnectionList = configCrudable.find { - it.name == getZoweConnectionName(myProject, type, profileName) && it.zoweConfigPath == getZoweConfigLocation( - myProject, - type - ) - }.collect(Collectors.toList()) + val zoweConnectionList = configCrudable + .find { + it.name == getZoweConnectionName(myProject, type, profileName) + && it.zoweConfigPath == getZoweConfigLocation(myProject, type) + } + .collect(Collectors.toList()) return if (zoweConnectionList.isEmpty()) null else zoweConnectionList[0] } @@ -206,12 +186,14 @@ class ZoweConfigServiceImpl(override val myProject: Project) : ZoweConfigService /** * Added notification about connection failure with action of force connection adding. - * @param title - notification title. - * @param content - notification content. + * @param title notification title. + * @param content notification content. * @return Nothing. */ private fun notifyUiOnConnectionFailure(title: String, content: String, type: ZoweConfigType) { - NotificationGroupManager.getInstance().getNotificationGroup(EXPLORER_NOTIFICATION_GROUP_ID) + NotificationGroupManager + .getInstance() + .getNotificationGroup(EXPLORER_NOTIFICATION_GROUP_ID) .createNotification(title, content, NotificationType.ERROR) .apply { addAction(object : DumbAwareAction("Add Anyway") { @@ -275,36 +257,31 @@ class ZoweConfigServiceImpl(override val myProject: Project) : ZoweConfigService this.globalZoweConfig zoweConfig ?: throw Exception("Cannot get $type Zowe config") - var allPreparedConn = mutableListOf() - if (checkConnection) { - val failedURLs = mutableListOf() - allPreparedConn = testAndPrepareAllZosmfConnections(zoweConfig, type, failedURLs) - if (failedURLs.isNotEmpty()) { - val andMore = if (failedURLs.size > 3) - "..." - else - "" - notifyUiOnConnectionFailure( - "Connection failed to:", - "${failedURLs.joinToString(separator = ",

")} ${andMore}", - type - ) - return - } + val (allPreparedConn, failedConnections) = testAndPrepareAllZosmfConnections(zoweConfig, type) + if (checkConnection and failedConnections.isNotEmpty()) { + val andMore = if (failedConnections.size > 3) "..." else "" + notifyUiOnConnectionFailure( + "Connection failed to:", + "${failedConnections.map{it.url}.joinToString(separator = ",

")} $andMore", + type + ) } - for (zosmfConnection in allPreparedConn) { + val conToAdd = if (checkConnection) + allPreparedConn.subtract(failedConnections.toSet()) + else + allPreparedConn + conToAdd.forEach { zosmfConnection -> val connectionOpt = configCrudable.addOrUpdate(zosmfConnection) if (!connectionOpt.isEmpty) { - var topic = if (type == ZoweConfigType.LOCAL) + val topic = if (type == ZoweConfigType.LOCAL) LOCAL_ZOWE_CONFIG_CHANGED else GLOBAL_ZOWE_CONFIG_CHANGED sendTopic(topic).onConfigSaved(zoweConfig, zosmfConnection) } } - } catch (e: Exception) { - notifyError(e) + NotificationsService.getService().notifyError(e) } } @@ -317,9 +294,9 @@ class ZoweConfigServiceImpl(override val myProject: Project) : ZoweConfigService private fun prepareConnection(zosmfConnection: ZOSConnection, type: ZoweConfigType): ConnectionConfig { val username = zosmfConnection.user val password = zosmfConnection.password - val zoweConnection = findExistingConnection(type, zosmfConnection.profileName)?.let { - zosmfConnection.toConnectionConfig(it.uuid, it.zVersion, type = type) - } ?: zosmfConnection.toConnectionConfig(UUID.randomUUID().toString(), type = type) + val zoweConnection = findExistingConnection(type, zosmfConnection.profileName) + ?.let { zosmfConnection.toConnectionConfig(it.uuid, it.zVersion, type = type) } + ?: zosmfConnection.toConnectionConfig(UUID.randomUUID().toString(), type = type) CredentialService.getService().setCredentials(zoweConnection.uuid, username, password) return zoweConnection } @@ -328,24 +305,25 @@ class ZoweConfigServiceImpl(override val myProject: Project) : ZoweConfigService * Convert all zosmf connections from zowe config file to ConnectionConfig and tests them * @param zoweConfig * @param type of zowe config - * @return list of URLs which did not pass the test + * @return pair of lists, one is the connections list, the second is the list of URLs which did not pass the test */ private fun testAndPrepareAllZosmfConnections( zoweConfig: ZoweConfig, - type: ZoweConfigType, - failedURLs: MutableList - ): MutableList { - var allPreparedConn = mutableListOf() - for (zosmfConnection in zoweConfig.getListOfZosmfConections()) { - val zoweConnection = prepareConnection(zosmfConnection, type) - try { - testAndPrepareConnection(zoweConnection) - } catch (t: Throwable) { - failedURLs.add(zoweConnection.url) + type: ZoweConfigType + ): Pair, List> { + return zoweConfig.getListOfZosmfConections() + .fold( + mutableListOf() to mutableListOf() + ) { (allConnectionConfigs, failedURLs), zosmfConnection -> + val zoweConnection = prepareConnection(zosmfConnection, type) + try { + testAndPrepareConnection(zoweConnection) + } catch (t: Throwable) { + failedURLs.add(zoweConnection) + } + allConnectionConfigs.add(zoweConnection) + allConnectionConfigs to failedURLs } - allPreparedConn.add(zoweConnection) - } - return allPreparedConn } /** @@ -382,7 +360,7 @@ class ZoweConfigServiceImpl(override val myProject: Project) : ZoweConfigService } } catch (e: Exception) { - notifyError(e) + NotificationsService.getService().notifyError(e) } } @@ -432,18 +410,20 @@ class ZoweConfigServiceImpl(override val myProject: Project) : ZoweConfigService val allConnections = configCrudable.getAll().toList() val allConnectionsNames: MutableList = allConnections.map { it.name }.toMutableList() - allConnections.filter { it.zoweConfigPath == getZoweConfigLocation(myProject, type) }.forEach { - var index = 1 - var newName = it.name - while (allConnectionsNames.contains(newName)) { - newName = it.name.plus(index.toString()) - index++ + allConnections + .filter { it.zoweConfigPath == getZoweConfigLocation(myProject, type) } + .forEach { + var index = 1 + var newName = it.name + while (allConnectionsNames.contains(newName)) { + newName = it.name.plus(index.toString()) + index++ + } + allConnectionsNames.add(newName) + it.name = newName + it.zoweConfigPath = null + configCrudable.update(it) } - allConnectionsNames.add(newName) - it.name = newName - it.zoweConfigPath = null - configCrudable.update(it) - } } private fun createZoweSchemaJsonIfNotExists() { @@ -491,7 +471,7 @@ class ZoweConfigServiceImpl(override val myProject: Project) : ZoweConfigService try { scanForZoweConfig(type) } catch (e: Exception) { - notifyError(e) + NotificationsService.getService().notifyError(e) } } val zoweConfig = if (type == ZoweConfigType.LOCAL) @@ -500,38 +480,37 @@ class ZoweConfigServiceImpl(override val myProject: Project) : ZoweConfigService globalZoweConfig ?: return ZoweConfigState.NOT_EXISTS findAllZosmfExistingConnection(type) ?: return ZoweConfigState.NEED_TO_ADD - var ret = ZoweConfigState.SYNCHRONIZED - - for (zosConnection in zoweConfig.getListOfZosmfConections()) { - val existingConnection = - findExistingConnection(type, zosConnection.profileName) - if (existingConnection == null) - ret = setZoweConfigState(ret, ZoweConfigState.NEED_TO_ADD) - else { - val newConnectionList = zoweConfig.getListOfZosmfConections() - .filter { it.profileName == getProfileNameFromConnName(existingConnection.name) } - val newConnection = if (newConnectionList.isNotEmpty()) { - newConnectionList[0].toConnectionConfig( - existingConnection.uuid.toString(), existingConnection.zVersion, existingConnection.owner, type = type - ) - } else { - ret = setZoweConfigState(ret, ZoweConfigState.NEED_TO_ADD) - continue - } - val zoweUsername = zosConnection.user - val zowePassword = zosConnection.password - - ret = if (existingConnection == newConnection && - getUsername(newConnection) == zoweUsername && - getPassword(newConnection) == zowePassword - ) { - setZoweConfigState(ret, ZoweConfigState.SYNCHRONIZED) + + return zoweConfig + .getListOfZosmfConections() + .fold(ZoweConfigState.SYNCHRONIZED) { prevZoweConfigState, zosConnection -> + val existingConnection = findExistingConnection(type, zosConnection.profileName) + val currZoweConfigState = if (existingConnection == null) { + ZoweConfigState.NEED_TO_ADD } else { - setZoweConfigState(ret, ZoweConfigState.NEED_TO_UPDATE) + val newConnectionList = zoweConfig.getListOfZosmfConections() + .filter { it.profileName == getProfileNameFromConnName(existingConnection.name) } + if (newConnectionList.isNotEmpty()) { + val newConnection = newConnectionList[0].toConnectionConfig( + existingConnection.uuid, existingConnection.zVersion, existingConnection.owner, type = type + ) + val zoweUsername = zosConnection.user + val zowePassword = zosConnection.password + if ( + existingConnection == newConnection + && getUsername(newConnection) == zoweUsername + && getPassword(newConnection) == zowePassword + ) { + ZoweConfigState.SYNCHRONIZED + } else { + ZoweConfigState.NEED_TO_UPDATE + } + } else { + ZoweConfigState.NEED_TO_ADD + } } + setZoweConfigState(prevZoweConfigState, currZoweConfigState) } - } - return ret } /** diff --git a/src/test/kotlin/org/zowe/explorer/config/ZoweConfigTestSpec.kt b/src/test/kotlin/org/zowe/explorer/config/ZoweConfigTestSpec.kt index b82b976f..382e7e1f 100644 --- a/src/test/kotlin/org/zowe/explorer/config/ZoweConfigTestSpec.kt +++ b/src/test/kotlin/org/zowe/explorer/config/ZoweConfigTestSpec.kt @@ -41,8 +41,10 @@ import org.zowe.explorer.dataops.operations.InfoOperation import org.zowe.explorer.dataops.operations.ZOSInfoOperation import org.zowe.explorer.explorer.Explorer import org.zowe.explorer.explorer.WorkingSet +import org.zowe.explorer.telemetry.NotificationsService import org.zowe.explorer.testutils.WithApplicationShouldSpec import org.zowe.explorer.testutils.testServiceImpl.TestDataOpsManagerImpl +import org.zowe.explorer.testutils.testServiceImpl.TestNotificationsServiceImpl import org.zowe.explorer.utils.crudable.* import org.zowe.explorer.utils.runIfTrue import org.zowe.explorer.utils.validateForBlank @@ -329,7 +331,6 @@ class ZoweConfigTestSpec : WithApplicationShouldSpec({ isInputStreamCalled shouldBe true isReturnedZoweConfig shouldBe true isScanForZoweConfigCalled shouldBe true - isZOSInfoCalled shouldBe false } should("delete zowe team config connection") { @@ -360,20 +361,23 @@ class ZoweConfigTestSpec : WithApplicationShouldSpec({ j = true listOf(jWSConf).stream() } - var isShowOkCancelDialogCalled = false - val showOkCancelDialogMock: (String, String, String, String, Icon?) -> Int = ::showOkCancelDialog - mockkStatic(showOkCancelDialogMock as KFunction<*>) - every { - showOkCancelDialogMock(any(), any(), any(), any(), any()) - } answers { - isShowOkCancelDialogCalled = true - Messages.OK + val notificationsService = NotificationsService.getService() as TestNotificationsServiceImpl + notificationsService.testInstance = object : TestNotificationsServiceImpl() { + override fun notifyError( + t: Throwable, + project: Project?, + custTitle: String?, + custDetailsShort: String?, + custDetailsLong: String? + ) { + notified = true + } } mockedZoweConfigService.deleteZoweConfig(type = ZoweConfigType.LOCAL) f shouldBe true j shouldBe true - isShowOkCancelDialogCalled shouldBe true + notified shouldBe true isConnectionDeleted shouldBe false } @@ -724,4 +728,4 @@ class ZoweConfigTestSpec : WithApplicationShouldSpec({ } -}) \ No newline at end of file +})