Skip to content

Commit

Permalink
IJMP-1898 Zowe config from .json enchancement: plus button when conne…
Browse files Browse the repository at this point in the history
…ction is not added yet

Fixed adding invalid connections from Zowe config file

Signed-off-by: Katsiaryna Tsytsenia <[email protected]>
  • Loading branch information
Katsiaryna Tsytsenia authored and Katsiaryna Tsytsenia committed Sep 28, 2024
1 parent 6254152 commit 8d1f99d
Show file tree
Hide file tree
Showing 3 changed files with 111 additions and 120 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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) {
Expand Down
197 changes: 88 additions & 109 deletions src/main/kotlin/org/zowe/explorer/zowe/service/ZoweConfigServiceImpl.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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-"

/**
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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<ConnectionConfig> {
it.name == getZoweConnectionName(myProject, type, profileName) && it.zoweConfigPath == getZoweConfigLocation(
myProject,
type
)
}.collect(Collectors.toList())
val zoweConnectionList = configCrudable
.find<ConnectionConfig> {
it.name == getZoweConnectionName(myProject, type, profileName)
&& it.zoweConfigPath == getZoweConfigLocation(myProject, type)
}
.collect(Collectors.toList())
return if (zoweConnectionList.isEmpty()) null else zoweConnectionList[0]
}

Expand All @@ -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") {
Expand Down Expand Up @@ -275,36 +257,31 @@ class ZoweConfigServiceImpl(override val myProject: Project) : ZoweConfigService
this.globalZoweConfig
zoweConfig ?: throw Exception("Cannot get $type Zowe config")

var allPreparedConn = mutableListOf<ConnectionConfig>()
if (checkConnection) {
val failedURLs = mutableListOf<String>()
allPreparedConn = testAndPrepareAllZosmfConnections(zoweConfig, type, failedURLs)
if (failedURLs.isNotEmpty()) {
val andMore = if (failedURLs.size > 3)
"..."
else
""
notifyUiOnConnectionFailure(
"Connection failed to:",
"${failedURLs.joinToString(separator = ", <p>")} ${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 = ", <p>")} $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)
}
}

Expand All @@ -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
}
Expand All @@ -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<String>
): MutableList<ConnectionConfig> {
var allPreparedConn = mutableListOf<ConnectionConfig>()
for (zosmfConnection in zoweConfig.getListOfZosmfConections()) {
val zoweConnection = prepareConnection(zosmfConnection, type)
try {
testAndPrepareConnection(zoweConnection)
} catch (t: Throwable) {
failedURLs.add(zoweConnection.url)
type: ZoweConfigType
): Pair<List<ConnectionConfig>, List<ConnectionConfig>> {
return zoweConfig.getListOfZosmfConections()
.fold(
mutableListOf<ConnectionConfig>() to mutableListOf<ConnectionConfig>()
) { (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
}

/**
Expand Down Expand Up @@ -382,7 +360,7 @@ class ZoweConfigServiceImpl(override val myProject: Project) : ZoweConfigService
}

} catch (e: Exception) {
notifyError(e)
NotificationsService.getService().notifyError(e)
}
}

Expand Down Expand Up @@ -432,18 +410,20 @@ class ZoweConfigServiceImpl(override val myProject: Project) : ZoweConfigService
val allConnections = configCrudable.getAll<ConnectionConfig>().toList()
val allConnectionsNames: MutableList<String> = 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() {
Expand Down Expand Up @@ -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)
Expand All @@ -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
}

/**
Expand Down
26 changes: 15 additions & 11 deletions src/test/kotlin/org/zowe/explorer/config/ZoweConfigTestSpec.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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") {
Expand Down Expand Up @@ -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<String>(), any<String>(), any<String>(), any<String>(), 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
}

Expand Down Expand Up @@ -724,4 +728,4 @@ class ZoweConfigTestSpec : WithApplicationShouldSpec({

}

})
})

0 comments on commit 8d1f99d

Please sign in to comment.