Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/splunk config #45

Merged
merged 27 commits into from
Sep 11, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
e1fdbf6
Prompt for plugin specific config during 'plugin initialize'
martinvisser Aug 21, 2023
9cb71ef
Split initialize and configure into two separate sub-commands
martinvisser Aug 22, 2023
f816879
Add keys to migrate
martinvisser Aug 28, 2023
998d6e0
Make config (more) type-safe
martinvisser Aug 28, 2023
a7037ce
Add autocompletion for Splunk indexes
GuusdeWit Aug 29, 2023
80d6bd3
Add tests for Splunk indexes autocompletion
GuusdeWit Aug 30, 2023
dd9c7e1
Add keys to migrate to plugin specific config
martinvisser Aug 30, 2023
ccdb7f4
Fix autocomplete for cli and alfred
martinvisser Sep 1, 2023
14dd479
Format according to ktlint+detekt
martinvisser Sep 1, 2023
af34926
Include Alfred workflows
martinvisser Sep 1, 2023
6630c2e
Use Configurable class instead of interface
martinvisser Sep 4, 2023
8f86122
Update names in GIT workflow
martinvisser Sep 6, 2023
3d5a4a2
Include zsh autocompletion for Splunk plugin
GuusdeWit Sep 8, 2023
69e1a1e
Remove git- prefix for zsh autocomplete
martinvisser Sep 8, 2023
44d71aa
Use -p/--project for project autocomplete
martinvisser Sep 8, 2023
aed8f15
Bump RET version to 0.2.0-SNAPSHOT
martinvisser Sep 11, 2023
ac734d9
Key are named the same in AzDo
martinvisser Sep 11, 2023
a8b4868
Update codeowners
martinvisser Sep 11, 2023
411ac9f
Bump version to 0.1.0-SNAPSHOT
martinvisser Sep 11, 2023
fbfcd37
Remove useless comments
martinvisser Sep 11, 2023
10c727f
Add test for projects
martinvisser Sep 11, 2023
489271f
Include Alfred Workflows in build/release
martinvisser Sep 11, 2023
2304b9b
Assemble Alfred Workflow during prepare-package
martinvisser Sep 11, 2023
48e921b
Remove commented out build config
martinvisser Sep 11, 2023
8dc432e
No need for extra antrun
martinvisser Sep 11, 2023
a28566b
Add -ntp to build to reduce output spam
martinvisser Sep 11, 2023
8295e9d
Update RET to 0.2.0
martinvisser Sep 11, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Split initialize and configure into two separate sub-commands
  • Loading branch information
martinvisser committed Sep 11, 2023
commit 9cb71ef7753bdebd1ad975334b3c3926bdee1a9f
Original file line number Diff line number Diff line change
@@ -1,22 +1,21 @@
package io.rabobank.ret.git.plugin.provider.azure

import io.rabobank.ret.configuration.Configurable
import io.rabobank.ret.configuration.ConfigurablePlugin
import io.rabobank.ret.configuration.ConfigurationProperty
import io.rabobank.ret.configuration.RetConfig
import jakarta.enterprise.context.ApplicationScoped

@ApplicationScoped
class AzureDevopsPluginConfig(retConfig: RetConfig) : Configurable {
val email: String = retConfig[EMAIL].orEmpty()
val pat: String = retConfig[PAT].orEmpty()
val projectId: String = retConfig[PROJECT].orEmpty()
val organization: String = retConfig[ORGANIZATION].orEmpty()
class AzureDevopsPluginConfig : ConfigurablePlugin() {
val email by lazy { config[EMAIL].orEmpty() }
val pat by lazy { config[PAT].orEmpty() }
val projectId by lazy { config[PROJECT].orEmpty() }
val organization by lazy { config[ORGANIZATION].orEmpty() }

override fun properties(): List<ConfigurationProperty> = listOf(
ConfigurationProperty(EMAIL, "Enter your email-address"),
ConfigurationProperty(PAT, "Enter your Azure Personal Access Token (PAT)"),
ConfigurationProperty(PROJECT, "Enter your Azure project"),
ConfigurationProperty(ORGANIZATION, "Enter your Azure organization"),
override fun properties() = listOf(
ConfigurationProperty(EMAIL, "Enter your email address", required = true),
ConfigurationProperty(PAT, "Enter your Azure Personal Access Token (PAT)", required = true),
ConfigurationProperty(PROJECT, "Enter your Azure project", required = true),
ConfigurationProperty(ORGANIZATION, "Enter your Azure organization", required = true),
)

private companion object {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,9 @@ class AzureDevopsUrlFactory(
.path("_git")
.path(repositoryName)
.path("pullrequestcreate")
.also {
.apply {
if (sourceRef != null) {
it.queryParam("sourceRef", sourceRef)
queryParam("sourceRef", sourceRef)
}
}
.buildToURL()
Expand Down
1 change: 1 addition & 0 deletions git-plugin/src/main/resources/application.properties
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
plugin.name=git
quarkus.banner.enabled=false
azure.devops.baseUrl=https://dev.azure.com
quarkus.native.resources.includes=autocompletion/zsh/completions.zsh
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,17 @@ package io.rabobank.ret.git.plugin
import io.quarkus.test.junit.QuarkusTest
import io.rabobank.ret.IntelliSearch
import io.rabobank.ret.RetContext
import io.rabobank.ret.git.plugin.command.AutoCompleteCommand
import io.rabobank.ret.git.plugin.output.OutputHandler
import io.rabobank.ret.git.plugin.provider.Branch
import io.rabobank.ret.git.plugin.provider.GitProvider
import io.rabobank.ret.git.plugin.provider.Pipeline
import io.rabobank.ret.git.plugin.provider.PipelineRun
import io.rabobank.ret.git.plugin.provider.PipelineRunResult
import io.rabobank.ret.git.plugin.provider.PipelineRunState
import io.rabobank.ret.git.plugin.provider.PullRequest
import io.rabobank.ret.git.plugin.provider.Repository
import io.rabobank.ret.git.plugin.provider.Reviewer
import io.rabobank.ret.git.plugin.command.AutoCompleteCommand
import io.rabobank.ret.git.plugin.output.OutputHandler
import io.rabobank.ret.git.plugin.provider.GitProvider
import io.rabobank.ret.picocli.mixin.ContextAwareness
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.BeforeEach
Expand Down Expand Up @@ -99,9 +99,7 @@ class AutoCompleteCommandTest {
val exitCode = commandLine.execute("git-repository")
assertThat(exitCode).isEqualTo(0)

verify(outputHandler).listRepositories(
allMockedRepositories,
)
verify(outputHandler).listRepositories(allMockedRepositories)
}

@ParameterizedTest
Expand Down
Original file line number Diff line number Diff line change
@@ -1,42 +1,70 @@
package io.rabobank.ret.git.plugin.config

import io.rabobank.ret.configuration.Configurable
import io.rabobank.ret.configuration.RetConfig
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import io.rabobank.ret.git.plugin.provider.azure.AzureDevopsPluginConfig
import io.rabobank.ret.util.OsUtils
import jakarta.enterprise.inject.Instance
import org.assertj.core.api.Assertions
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.io.TempDir
import org.mockito.kotlin.mock
import org.mockito.kotlin.whenever
import java.nio.file.Files
import java.nio.file.Path

class AzureDevopsPluginConfigLoadTest {
private val retFolder by lazy { Files.createDirectory(mockUserHomeDirectory.resolve(".ret")) }
private val pluginsPath by lazy { Files.createDirectory(retFolder.resolve("plugins")) }
private val pluginConfigFileName = "git.json"
private val pluginConfig = AzureDevopsPluginConfig()
.apply {
pluginName = "git"
objectMapper = jacksonObjectMapper()
}

@TempDir
lateinit var mockUserHomeDirectory: Path

@BeforeEach
fun setUp() {
Files.createFile(pluginsPath.resolve(pluginConfigFileName))

val objectMapper = jacksonObjectMapper()
val config = mapOf(
"azure_devops_email" to "[email protected]",
"azure_devops_pat" to "this_is_a_pat",
"azure_devops_project" to "this_is_the_project",
"azure_devops_organization" to "my-organization",
)
objectMapper.writeValue(pluginsPath.resolve(pluginConfigFileName).toFile(), config)
}

@Test
fun shouldLoadConfiguration() {
val osUtils = mock<OsUtils>()
val configurables = mock<Instance<Configurable>>()
whenever(osUtils.getHomeDirectory()).thenReturn("src/test/resources")
val osUtils = mock<OsUtils> {
whenever(it.getHomeDirectory()).thenReturn(mockUserHomeDirectory.toString())
whenever(it.getPluginConfig("git")).thenReturn(pluginsPath.resolve(pluginConfigFileName))
}

val pluginConfig = AzureDevopsPluginConfig(RetConfig(osUtils, configurables, "1.0.0"))
pluginConfig.osUtils = osUtils

Assertions.assertThat(pluginConfig.email).isEqualTo("[email protected]")
Assertions.assertThat(pluginConfig.pat).isEqualTo("this_is_a_pat")
Assertions.assertThat(pluginConfig.projectId).isEqualTo("this_is_the_project")
Assertions.assertThat(pluginConfig.organization).isEqualTo("my-organization")
assertThat(pluginConfig.email).isEqualTo("[email protected]")
assertThat(pluginConfig.pat).isEqualTo("this_is_a_pat")
assertThat(pluginConfig.projectId).isEqualTo("this_is_the_project")
assertThat(pluginConfig.organization).isEqualTo("my-organization")
}

@Test
fun shouldLoadCorrectlyWithEmptyConfiguration() {
val osUtils = mock<OsUtils>()
val configurables = mock<Instance<Configurable>>()
whenever(osUtils.getHomeDirectory()).thenReturn("src/test/resources/nonexisting")
val osUtils = mock<OsUtils> {
whenever(it.getHomeDirectory()).thenReturn("$mockUserHomeDirectory/nonexisting")
}

val pluginConfig = AzureDevopsPluginConfig(RetConfig(osUtils, configurables, "1.0.0"))
pluginConfig.osUtils = osUtils

Assertions.assertThat(pluginConfig.email).isEmpty()
Assertions.assertThat(pluginConfig.pat).isEmpty()
Assertions.assertThat(pluginConfig.projectId).isEmpty()
Assertions.assertThat(pluginConfig.organization).isEmpty()
assertThat(pluginConfig.email).isEmpty()
assertThat(pluginConfig.pat).isEmpty()
assertThat(pluginConfig.projectId).isEmpty()
assertThat(pluginConfig.organization).isEmpty()
}
}
}
4 changes: 0 additions & 4 deletions git-plugin/src/test/resources/.ret/ret.config

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,47 +1,55 @@
package io.rabobank.ret.splunk.plugin

import io.quarkus.logging.Log
import io.quarkus.picocli.runtime.annotations.TopCommand
import io.quarkus.runtime.annotations.RegisterForReflection
import io.rabobank.ret.RetContext
import io.rabobank.ret.commands.PluginConfigureCommand
import io.rabobank.ret.commands.PluginInitializeCommand
import io.rabobank.ret.picocli.mixin.ContextAwareness
import io.rabobank.ret.splunk.plugin.splunk.SplunkConfig
import io.rabobank.ret.util.BrowserUtils
import io.rabobank.ret.util.Logged
import jakarta.ws.rs.core.UriBuilder
import picocli.CommandLine
import picocli.CommandLine.Command
import picocli.CommandLine.Mixin
import picocli.CommandLine.Option
import picocli.CommandLine.Parameters

@TopCommand
@CommandLine.Command(
@Command(
name = "splunk",
description = ["Plugin to interact with Splunk"],
subcommands = [
PluginInitializeCommand::class,
PluginConfigureCommand::class,
],
)
@RegisterForReflection(targets = [RetContext::class])
@Logged
class SplunkEntryCommand(
private val browserUtils: BrowserUtils,
private val retContext: RetContext,
private val splunkConfig: SplunkConfig,
) : Runnable {
@CommandLine.Mixin
@Mixin
lateinit var contextAwareness: ContextAwareness

@CommandLine.Option(
@Option(
names = ["--index", "-i"],
description = ["Provide the index to query on"],
paramLabel = "index",
)
var providedIndex: String? = null

@CommandLine.Option(
@Option(
names = ["--app", "-a"],
description = ["Provide the app name to query on"],
paramLabel = "appName",
)
var providedAppName: String? = null

@CommandLine.Parameters(
@Parameters(
paramLabel = "query",
description = ["The Splunk query to execute"],
arity = "0..*",
Expand All @@ -53,10 +61,10 @@ class SplunkEntryCommand(
override fun run() {
val queryArguments = mutableListOf<String>()

val appName = providedAppName ?: retContext.gitRepository
val appName = providedAppName ?: retContext.gitRepository?.removeSuffix(".git")
val searchField = splunkConfig.searchField ?: "appName"

providedIndex?.let { queryArguments += "index=$it" }
queryArguments += providedIndex?.let { "index=$it" }
?: splunkConfig.indexes.joinToString(" OR ", "(", ")") { "index=$it" }
appName?.let { queryArguments += "$searchField=$it" }
queryArguments += queryParts
Expand All @@ -72,6 +80,7 @@ class SplunkEntryCommand(
.build()
.toASCIIString()

Log.info("Querying splunk with url '$url'")
browserUtils.openUrl(url)
}
}
Original file line number Diff line number Diff line change
@@ -1,36 +1,25 @@
package io.rabobank.ret.splunk.plugin.splunk

import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.module.kotlin.readValue
import io.rabobank.ret.configuration.Configurable
import io.rabobank.ret.configuration.Question
import io.rabobank.ret.util.OsUtils
import io.rabobank.ret.configuration.ConfigurablePlugin
import io.rabobank.ret.configuration.ConfigurationProperty
import jakarta.enterprise.context.ApplicationScoped
import java.nio.file.Path

@ApplicationScoped
class SplunkConfig(osUtils: OsUtils, objectMapper: ObjectMapper) : Configurable {
private val pluginConfig = Path.of(osUtils.getHomeDirectory(), ".ret", "plugins", "splunk-plugin.json")
private val config by lazy {
runCatching {
objectMapper.readValue<Map<String, String>>(pluginConfig.toFile())
}.getOrDefault(emptyMap())
}

class SplunkConfig : ConfigurablePlugin() {
val baseUrl by lazy { config[BASE_URL] }
val app by lazy { config[APP] }
val indexes by lazy { config[INDEXES]?.run { split(",").map { it.trim() } }.orEmpty() }
val searchField by lazy { config[SEARCH_FIELD] }

override fun prompts() = listOf(
Question(BASE_URL, "Enter the Splunk base URL", required = true),
Question(APP, "Enter your Splunk app name", required = true),
Question(INDEXES, "Enter your Splunk index, if more than one, separate by comma. Ex: my_index_a, my_index_b", required = true),
override fun properties() = listOf(
ConfigurationProperty(BASE_URL, "Enter the Splunk base URL", required = true),
ConfigurationProperty(APP, "Enter your Splunk app name", required = true),
ConfigurationProperty(INDEXES, "Enter your Splunk index, if more than one, separate by comma. E.g. my_index_a, my_index_b", required = true),
//Optional answers from here on onwards:
Question(
ConfigurationProperty(
SEARCH_FIELD,
"Optional: Enter the field of the unique identifier. Ex: system_name, application_name or cf_app_name.\n" +
"This is handy in case you have one big index where all different sorts of applications/systems log to. Ex: my_awesome_microservice or my_linux_server_1",
"Optional: Enter the field of the unique identifier. E.g. system_name, application_name or cf_app_name.\n" +
"This is handy in case you have one big index where all different sorts of applications/systems log to. E.g. my_awesome_microservice or my_linux_server_1",
),
)

Expand Down
3 changes: 2 additions & 1 deletion splunk-plugin/src/main/resources/application.properties
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
plugin.name=splunk
quarkus.banner.enabled=false
quarkus.log.level=OFF
#quarkus.log.level=OFF
Loading