Skip to content

Commit

Permalink
fix: settings for scan types (#511)
Browse files Browse the repository at this point in the history
* fix: settings for scan types

* fix: settings for severity types

* fix: settings for issue option types

* refactor: improve code readability
  • Loading branch information
teodora-sandu authored Apr 18, 2024
1 parent b532c4c commit 0460fe4
Show file tree
Hide file tree
Showing 6 changed files with 136 additions and 90 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# Snyk Security Changelog

## [2.7.15]
### Fixed
- Re-enable scan results when re-enabling different scan types

## [2.7.14]
### Added
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import com.intellij.openapi.progress.runBackgroundableTask
import com.intellij.openapi.project.Project
import io.snyk.plugin.events.SnykProductsOrSeverityListener
import io.snyk.plugin.events.SnykResultsFilteringListener
import io.snyk.plugin.events.SnykScanListener
import io.snyk.plugin.events.SnykSettingsListener
import io.snyk.plugin.getAmplitudeExperimentService
import io.snyk.plugin.getSnykAnalyticsService
Expand Down Expand Up @@ -46,6 +47,7 @@ class SnykProjectSettingsConfigurable(val project: Project) : SearchableConfigur
isCrashReportingModified() ||
snykSettingsDialog.isScanTypeChanged() ||
snykSettingsDialog.isSeverityEnablementChanged() ||
snykSettingsDialog.isIssueOptionChanged() ||
snykSettingsDialog.manageBinariesAutomatically() != settingsStateService.manageBinariesAutomatically ||
snykSettingsDialog.getCliPath() != settingsStateService.cliPath ||
snykSettingsDialog.getCliBaseDownloadURL() != settingsStateService.cliBaseDownloadURL ||
Expand Down Expand Up @@ -92,6 +94,7 @@ class SnykProjectSettingsConfigurable(val project: Project) : SearchableConfigur

snykSettingsDialog.saveScanTypeChanges()
snykSettingsDialog.saveSeveritiesEnablementChanges()
snykSettingsDialog.saveIssueOptionChanges()

if (isProjectSettingsAvailable(project)) {
val snykProjectSettingsService = getSnykProjectSettingsService(project)
Expand Down
4 changes: 4 additions & 0 deletions src/main/kotlin/io/snyk/plugin/ui/SnykSettingsDialog.kt
Original file line number Diff line number Diff line change
Expand Up @@ -603,6 +603,10 @@ class SnykSettingsDialog(

fun saveSeveritiesEnablementChanges() = severityEnablementPanel.apply()

fun isIssueOptionChanged() = issueViewOptionsPanel.isModified()

fun saveIssueOptionChanges() = issueViewOptionsPanel.apply()

fun getAdditionalParameters(): String = additionalParametersTextField.text

private fun initializeValidation() {
Expand Down
49 changes: 28 additions & 21 deletions src/main/kotlin/io/snyk/plugin/ui/settings/IssueViewOptionsPanel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package io.snyk.plugin.ui.settings

import com.intellij.openapi.project.Project
import com.intellij.ui.components.JBCheckBox
import com.intellij.ui.dsl.builder.actionListener
import com.intellij.ui.dsl.builder.bindSelected
import com.intellij.util.ui.JBUI
import io.snyk.plugin.getSnykTaskQueueService
import io.snyk.plugin.pluginSettings
Expand All @@ -13,52 +15,57 @@ class IssueViewOptionsPanel(
private val settings
get() = pluginSettings()

private var currentOpenIssuesEnabled = settings.openIssuesEnabled
private var currentIgnoredIssuesEnabled = settings.ignoredIssuesEnabled

val panel = com.intellij.ui.dsl.builder.panel {
row {
checkBox(
text = "Open issues",
).component.apply {
isSelected = settings.openIssuesEnabled
this.addItemListener {
if (canOptionChange(this, settings.openIssuesEnabled)) {
settings.openIssuesEnabled = this.isSelected
getSnykTaskQueueService(project)?.scan()
}
).applyToComponent {
name = text
}
.actionListener{ event, it ->
if (canBeChanged(it, it.isSelected)) {
currentOpenIssuesEnabled = it.isSelected
getSnykTaskQueueService(project)?.scan()
}
name = "Open issues"
}
// bindSelected is needed to trigger apply() on the settings dialog that this panel is rendered in
// that way we trigger the re-rendering of the Tree Nodes
.bindSelected(settings::openIssuesEnabled)
}
row {
checkBox(
text = "Ignored issues",
).component.apply {
isSelected = settings.ignoredIssuesEnabled
this.addItemListener {
if (canOptionChange(this, settings.ignoredIssuesEnabled)) {
settings.ignoredIssuesEnabled = this.isSelected
getSnykTaskQueueService(project)?.scan()
}
).applyToComponent {
name = text
}
.actionListener{ event, it ->
if (canBeChanged(it, it.isSelected)) {
currentIgnoredIssuesEnabled = it.isSelected
getSnykTaskQueueService(project)?.scan()
}
name = "Ignored issues"
}
.bindSelected(settings::ignoredIssuesEnabled)
}
}.apply {
name = "issueViewPanel"
border = JBUI.Borders.empty(2)
}

private fun canOptionChange(component: JBCheckBox, wasEnabled: Boolean): Boolean {
private fun canBeChanged(component: JBCheckBox, isSelected: Boolean): Boolean {
val onlyOneEnabled = arrayOf(
settings.openIssuesEnabled,
settings.ignoredIssuesEnabled,
currentOpenIssuesEnabled,
currentIgnoredIssuesEnabled,
).count { it } == 1

if (onlyOneEnabled && wasEnabled) {
component.isSelected = true
if (onlyOneEnabled && !isSelected) {
SnykBalloonNotificationHelper.showWarnBalloonForComponent(
"At least one option should be selected",
component
)
component.isSelected = true
return false
}
return true
Expand Down
107 changes: 64 additions & 43 deletions src/main/kotlin/io/snyk/plugin/ui/settings/ScanTypesPanel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,12 @@ import com.intellij.openapi.Disposable
import com.intellij.openapi.progress.runBackgroundableTask
import com.intellij.openapi.project.Project
import com.intellij.openapi.ui.popup.Balloon
import com.intellij.ui.dsl.builder.panel
import com.intellij.ui.HyperlinkLabel
import com.intellij.ui.components.ActionLink
import com.intellij.ui.components.JBCheckBox
import com.intellij.ui.dsl.builder.actionListener
import com.intellij.ui.dsl.builder.bindSelected
import com.intellij.util.Alarm
import com.intellij.util.ui.JBUI
import com.intellij.util.ui.UIUtil
Expand All @@ -24,7 +27,6 @@ import snyk.common.ProductType
import snyk.common.isSnykCodeAvailable
import snyk.common.toSnykCodeSettingsUrl
import java.awt.Component
import java.awt.event.ItemEvent
import java.awt.event.MouseAdapter
import java.awt.event.MouseEvent
import javax.swing.JLabel
Expand All @@ -40,6 +42,13 @@ class ScanTypesPanel(
private val settings
get() = pluginSettings()

private var currentOSSScanEnabled = settings.ossScanEnable
private var currentAdvisorEnabled = settings.advisorEnable
private var currentSnykCodeSecurityScanEnabled = settings.snykCodeSecurityIssuesScanEnable
private var currentSnykCodeQualityScanEnabled = settings.snykCodeQualityIssuesScanEnable
private var currentIaCScanEnabled = settings.iacScanEnabled
private var currentContainerScanEnabled = settings.containerScanEnabled

private val alarm = Alarm(Alarm.ThreadToUse.POOLED_THREAD, parentDisposable)

private var codeSecurityCheckbox: JBCheckBox? = null
Expand All @@ -52,7 +61,7 @@ class ScanTypesPanel(
}
}

val codeAlertPanel = com.intellij.ui.dsl.builder.panel {
val codeAlertPanel = panel {
row {
cell(snykCodeAlertHyperLinkLabel)
cell(snykCodeReCheckLinkLabel)
Expand All @@ -62,77 +71,93 @@ class ScanTypesPanel(
this.isVisible = false
}

val panel = com.intellij.ui.dsl.builder.panel {
val panel = panel {
row {
checkBox(ProductType.OSS.productSelectionName).applyToComponent {
name = text
cliScanComments?.let { comment(it) }
label("").component.convertIntoHelpHintLabel(ProductType.OSS.description)
isSelected = settings.ossScanEnable
this.addItemListener {
correctLastProductDisabled(it)
settings.ossScanEnable = this.isSelected
}
.actionListener{ event, it ->
val isSelected = it.isSelected
if (canBeChanged(it, !isSelected)) {
// we need to change the settings in here in order for the validation to work pre-apply
currentOSSScanEnabled = isSelected
}
}
// bindSelected is needed to trigger isModified() and then apply() on the settings dialog that this panel is rendered in
// that way we trigger the re-rendering of the Tree Nodes
.bindSelected(settings::ossScanEnable)
}
row {
checkBox(ProductType.ADVISOR.productSelectionName).applyToComponent {
name = text
label("").component.convertIntoHelpHintLabel(ProductType.ADVISOR.description)
isSelected = settings.advisorEnable
this.addItemListener {
correctLastProductDisabled(it)
settings.advisorEnable = this.isSelected
}
.actionListener{ event, it ->
val isSelected = it.isSelected
if (canBeChanged(it, !isSelected)) {
currentAdvisorEnabled = isSelected
}
}
.bindSelected(settings::advisorEnable)
}
row {
checkBox(ProductType.IAC.productSelectionName).applyToComponent {
name = text
label("").component.convertIntoHelpHintLabel(ProductType.IAC.description)
isSelected = settings.iacScanEnabled
this.addItemListener {
correctLastProductDisabled(it)
settings.iacScanEnabled = this.isSelected
}
.actionListener{ event, it ->
val isSelected = it.isSelected
if (canBeChanged(it, !isSelected)) {
currentIaCScanEnabled = isSelected
}
}
.bindSelected(settings::iacScanEnabled)
}
row {
checkBox(ProductType.CONTAINER.productSelectionName).applyToComponent {
name = text
label("").component.convertIntoHelpHintLabel(ProductType.CONTAINER.description)
isSelected = settings.containerScanEnabled
this.addItemListener {
correctLastProductDisabled(it)
settings.containerScanEnabled = this.isSelected
}
.actionListener{ event, it ->
val isSelected = it.isSelected
if (canBeChanged(it, !isSelected)) {
currentContainerScanEnabled = isSelected
val imagesCache = getKubernetesImageCache(project)
if (this.isSelected) imagesCache?.cacheKubernetesFileFromProject() else imagesCache?.clear()
if (isSelected) imagesCache?.cacheKubernetesFileFromProject() else imagesCache?.clear()
}
}
.bindSelected(settings::containerScanEnabled)
}
row {
checkBox(ProductType.CODE_SECURITY.productSelectionName).applyToComponent {
name = text
codeSecurityCheckbox = this
isSelected = settings.snykCodeSecurityIssuesScanEnable
label("").component.convertIntoHelpHintLabel(ProductType.CODE_SECURITY.description)
this.addItemListener {
correctLastProductDisabled(it)
settings.snykCodeSecurityIssuesScanEnable = this.isSelected
isSelected = settings.snykCodeSecurityIssuesScanEnable
}
.actionListener{ event, it ->
val isSelected = it.isSelected
if (canBeChanged(it, !isSelected)) {
currentSnykCodeSecurityScanEnabled = isSelected
snykCodeComment?.isVisible = shouldSnykCodeCommentBeVisible()
}
}
.bindSelected(settings::snykCodeSecurityIssuesScanEnable)
checkBox(ProductType.CODE_QUALITY.productSelectionName).applyToComponent {
name = text
codeQualityCheckbox = this
isSelected = settings.snykCodeQualityIssuesScanEnable
label("").component.convertIntoHelpHintLabel(ProductType.CODE_QUALITY.description)
this.addItemListener {
correctLastProductDisabled(it)
settings.snykCodeQualityIssuesScanEnable = this.isSelected
}
.actionListener{ event, it ->
val isSelected = it.isSelected
if (canBeChanged(it, !isSelected)) {
currentSnykCodeQualityScanEnabled = isSelected
snykCodeComment?.isVisible = shouldSnykCodeCommentBeVisible()
}
}
.bindSelected(settings::snykCodeQualityIssuesScanEnable)
}
row {
snykCodeComment = label("")
Expand All @@ -148,11 +173,6 @@ class ScanTypesPanel(
border = JBUI.Borders.empty(2)
}

private fun JBCheckBox.correctLastProductDisabled(it: ItemEvent) {
val deselected = it.stateChange == ItemEvent.DESELECTED
isLastProductDisabling(this, deselected)
}

private fun JLabel.convertIntoHelpHintLabel(text: String) {
icon = AllIcons.General.ContextHelp
addMouseListener(ShowHintMouseAdapter(this, text))
Expand Down Expand Up @@ -322,23 +342,24 @@ class ScanTypesPanel(
}
}

private fun isLastProductDisabling(component: JBCheckBox, deselected: Boolean): Boolean {
private fun canBeChanged(component: JBCheckBox, isSelected: Boolean): Boolean {
val onlyOneEnabled = arrayOf(
settings.ossScanEnable,
settings.snykCodeSecurityIssuesScanEnable,
settings.snykCodeQualityIssuesScanEnable,
settings.iacScanEnabled,
settings.containerScanEnabled,
settings.advisorEnable,
currentOSSScanEnabled,
currentSnykCodeSecurityScanEnabled,
currentSnykCodeQualityScanEnabled,
currentIaCScanEnabled,
currentContainerScanEnabled,
currentAdvisorEnabled,
).count { it } == 1

if (onlyOneEnabled && deselected) {
component.isSelected = true
if (onlyOneEnabled && !isSelected) {
SnykBalloonNotificationHelper.showWarnBalloonForComponent(
"At least one Scan type should be enabled",
component
)
component.isSelected = false
return false
}
return onlyOneEnabled
return true
}
}
Loading

0 comments on commit 0460fe4

Please sign in to comment.