Skip to content

Commit 4c668a8

Browse files
committed
feat: show decoded value for base64 values in Secrets, ConfigMaps (redhat-developer#663)
Signed-off-by: Andre Dietisheim <[email protected]>
1 parent 0a05c6a commit 4c668a8

File tree

3 files changed

+143
-0
lines changed

3 files changed

+143
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
@file:Suppress("UnstableApiUsage")
2+
3+
package com.redhat.devtools.intellij.kubernetes.editor.inlay
4+
5+
import com.intellij.codeInsight.hints.ChangeListener
6+
import com.intellij.codeInsight.hints.FactoryInlayHintsCollector
7+
import com.intellij.codeInsight.hints.ImmediateConfigurable
8+
import com.intellij.codeInsight.hints.InlayHintsCollector
9+
import com.intellij.codeInsight.hints.InlayHintsProvider
10+
import com.intellij.codeInsight.hints.InlayHintsSink
11+
import com.intellij.codeInsight.hints.NoSettings
12+
import com.intellij.codeInsight.hints.SettingsKey
13+
import com.intellij.codeInsight.hints.presentation.PresentationFactory
14+
import com.intellij.json.psi.JsonProperty
15+
import com.intellij.openapi.editor.Editor
16+
import com.intellij.openapi.editor.impl.EditorImpl
17+
import com.intellij.psi.PsiElement
18+
import com.intellij.psi.PsiFile
19+
import com.intellij.refactoring.suggested.endOffset
20+
import com.intellij.refactoring.suggested.startOffset
21+
import com.intellij.ui.dsl.builder.panel
22+
import com.redhat.devtools.intellij.kubernetes.editor.util.decodeBase64
23+
import com.redhat.devtools.intellij.kubernetes.editor.util.getContent
24+
import com.redhat.devtools.intellij.kubernetes.editor.util.getData
25+
import com.redhat.devtools.intellij.kubernetes.editor.util.getKubernetesResourceInfo
26+
import com.redhat.devtools.intellij.kubernetes.editor.util.isKubernetesResource
27+
import org.jetbrains.yaml.psi.YAMLKeyValue
28+
import javax.swing.JComponent
29+
30+
internal class Base64InlayHintsProvider: InlayHintsProvider<NoSettings> {
31+
32+
override val key: SettingsKey<NoSettings> = SettingsKey("LSP.hints")
33+
override val name: String = "Kubernetes"
34+
override val previewText: String = "Preview"
35+
36+
override fun createSettings(): NoSettings {
37+
return NoSettings()
38+
}
39+
40+
override fun createConfigurable(settings: NoSettings): ImmediateConfigurable {
41+
return object : ImmediateConfigurable {
42+
override fun createComponent(listener: ChangeListener): JComponent = panel {}
43+
44+
override val mainCheckboxText: String = "Show hints for:"
45+
46+
override val cases: List<ImmediateConfigurable.Case> = emptyList()
47+
}
48+
}
49+
50+
override fun getCollectorFor(
51+
file: PsiFile,
52+
editor: Editor,
53+
settings: NoSettings,
54+
sink: InlayHintsSink
55+
): InlayHintsCollector? {
56+
val project = editor.project ?: return null
57+
val virtualFile = file.virtualFile ?: return null
58+
val info = getKubernetesResourceInfo(virtualFile, project)
59+
if (!isKubernetesResource("Secret", info)
60+
&& !isKubernetesResource("ConfigMap", info)
61+
) {
62+
return null
63+
}
64+
return Collector(editor)
65+
}
66+
67+
private class Collector(editor: Editor): FactoryInlayHintsCollector(editor) {
68+
override fun collect(element: PsiElement, editor: Editor, sink: InlayHintsSink): Boolean {
69+
if (!element.isValid) {
70+
return true
71+
}
72+
val content = getContent(element) ?: return true
73+
val data = getData(content) ?: return true
74+
data.children.toList()
75+
.forEach { child ->
76+
val value = when (child) {
77+
is YAMLKeyValue ->
78+
child.value?.text
79+
is JsonProperty ->
80+
child.value?.text
81+
else -> null
82+
}
83+
val decoded = decodeBase64(value)
84+
if (decoded != null) {
85+
val presentation = PresentationFactory(editor as EditorImpl).smallText(decoded)
86+
sink.addInlineElement(child.endOffset, true, presentation, false)
87+
}
88+
}
89+
90+
return true
91+
}
92+
93+
}
94+
}

src/main/kotlin/com/redhat/devtools/intellij/kubernetes/editor/util/ResourceEditorUtils.kt

+46
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,15 @@
1010
******************************************************************************/
1111
package com.redhat.devtools.intellij.kubernetes.editor.util
1212

13+
import com.intellij.json.psi.JsonElement
1314
import com.intellij.json.psi.JsonElementGenerator
1415
import com.intellij.json.psi.JsonFile
1516
import com.intellij.json.psi.JsonProperty
1617
import com.intellij.json.psi.JsonValue
1718
import com.intellij.openapi.application.ReadAction
1819
import com.intellij.openapi.editor.Document
1920
import com.intellij.openapi.project.Project
21+
import com.intellij.openapi.util.text.Strings
2022
import com.intellij.openapi.vfs.VirtualFile
2123
import com.intellij.psi.PsiDocumentManager
2224
import com.intellij.psi.PsiElement
@@ -27,9 +29,12 @@ import org.jetbrains.yaml.YAMLElementGenerator
2729
import org.jetbrains.yaml.YAMLUtil
2830
import org.jetbrains.yaml.psi.YAMLFile
2931
import org.jetbrains.yaml.psi.YAMLKeyValue
32+
import org.jetbrains.yaml.psi.YAMLPsiElement
3033
import org.jetbrains.yaml.psi.YAMLValue
34+
import java.util.Base64
3135

3236
private const val KEY_METADATA = "metadata"
37+
private const val KEY_DATA = "data"
3338
private const val KEY_RESOURCE_VERSION = "resourceVersion"
3439

3540
/**
@@ -59,6 +64,11 @@ fun isKubernetesResource(resourceInfo: KubernetesResourceInfo?): Boolean {
5964
&& resourceInfo?.typeInfo?.kind?.isNotBlank() ?: false
6065
}
6166

67+
fun isKubernetesResource(kind: String, resourceInfo: KubernetesResourceInfo?): Boolean {
68+
return resourceInfo?.typeInfo?.apiGroup?.isNotBlank() ?: false
69+
&& kind == resourceInfo?.typeInfo?.kind
70+
}
71+
6272
/**
6373
* Returns [KubernetesResourceInfo] for the given file and project. Returns `null` if it could not be retrieved.
6474
*
@@ -134,6 +144,13 @@ private fun createOrUpdateResourceVersion(resourceVersion: String, metadata: YAM
134144
}
135145
}
136146

147+
fun getContent(element: PsiElement): PsiElement? {
148+
if (element !is PsiFile) {
149+
return null
150+
}
151+
return getContent(element)
152+
}
153+
137154
private fun getContent(file: PsiFile): PsiElement? {
138155
return when (file) {
139156
is YAMLFile -> {
@@ -172,6 +189,35 @@ private fun getMetadata(content: PsiElement): PsiElement? {
172189
}
173190
}
174191

192+
fun getData(element: PsiElement): PsiElement? {
193+
return when (element) {
194+
is YAMLPsiElement ->
195+
element.children
196+
.filterIsInstance(YAMLKeyValue::class.java)
197+
.find { it.name == KEY_DATA }
198+
?.value
199+
is JsonElement ->
200+
element.children.toList()
201+
.filterIsInstance(JsonProperty::class.java)
202+
.find { it.name == KEY_DATA }
203+
?.value
204+
else ->
205+
null
206+
}
207+
}
208+
209+
fun decodeBase64(value: String?): String? {
210+
if (Strings.isEmptyOrSpaces(value)) {
211+
return null
212+
}
213+
return try {
214+
val bytes = Base64.getDecoder().decode(value)
215+
String(bytes)
216+
} catch (e: IllegalArgumentException) {
217+
null
218+
}
219+
}
220+
175221
private fun getResourceVersion(metadata: YAMLKeyValue): YAMLKeyValue? {
176222
return metadata.value?.children
177223
?.filterIsInstance(YAMLKeyValue::class.java)

src/main/resources/META-INF/plugin.xml

+3
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,9 @@
206206
<nonProjectFileWritingAccessExtension implementation="com.redhat.devtools.intellij.common.editor.AllowNonProjectEditing" />
207207
<postStartupActivity implementation="com.redhat.devtools.intellij.kubernetes.KubernetesPluginInitializer" />
208208
<editorTabTitleProvider implementation="com.redhat.devtools.intellij.kubernetes.editor.ResourceEditorTabTitleProvider"/>
209+
<codeInsight.inlayProvider language="yaml"
210+
implementationClass="com.redhat.devtools.intellij.kubernetes.editor.inlay.Base64InlayHintsProvider"
211+
id="MarkdownTableInlayProvider"/>
209212
</extensions>
210213

211214
<extensionPoints>

0 commit comments

Comments
 (0)