diff --git a/plugins/core/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/FeatureDevSessionContext.kt b/plugins/core/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/FeatureDevSessionContext.kt index 307c5c42e23..b5f4e84532d 100644 --- a/plugins/core/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/FeatureDevSessionContext.kt +++ b/plugins/core/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/FeatureDevSessionContext.kt @@ -20,6 +20,7 @@ import software.aws.toolkits.core.utils.outputStream import software.aws.toolkits.core.utils.putNextEntry import software.aws.toolkits.jetbrains.core.coroutines.EDT import software.aws.toolkits.jetbrains.core.coroutines.getCoroutineBgContext +import software.aws.toolkits.jetbrains.services.telemetry.ALLOWED_CODE_EXTENSIONS import software.aws.toolkits.resources.AwsCoreBundle import software.aws.toolkits.telemetry.AmazonqTelemetry import java.io.File @@ -94,7 +95,7 @@ class FeatureDevSessionContext(val project: Project, val maxProjectSizeBytes: Lo if (file.isDirectory) return true val extension = file.extension ?: return false - return FeatureDevBundleConfig.ALLOWED_CODE_EXTENSIONS.contains(extension) + return ALLOWED_CODE_EXTENSIONS.contains(extension) } private fun ignoreFileByExtension(file: VirtualFile) = diff --git a/plugins/core/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/QConstants.kt b/plugins/core/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/QConstants.kt index 8d4f41fe5a8..3cc2bf81741 100644 --- a/plugins/core/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/QConstants.kt +++ b/plugins/core/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/QConstants.kt @@ -7,204 +7,3 @@ object QConstants { const val Q_MARKETPLACE_URI = "https://aws.amazon.com/q/developer/" const val CODEWHISPERER_LOGIN_HELP_URI = "https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/auth-access.html" } - -// List from Stack Overflow 2023 survey: https://survey.stackoverflow.co/2023/#technology -object FeatureDevBundleConfig { - val ALLOWED_CODE_EXTENSIONS = setOf( - "abap", - "ada", - "adb", - "ads", - "apl", - "asm", - "awk", - "b", - "bas", - "bash", - "bat", - "boo", - "c", - "cbl", - "cc", - "cfc", - "cfm", - "cjs", - "clj", - "cljc", - "cljs", - "cls", - "cmake", - "cob", - "cobra", - "coffee", - "cpp", - "cpy", - "cr", - "cs", - "css", - "csx", - "cxx", - "d", - "dart", - "dfm", - "dpr", - "e", - "el", - "elm", - "erl", - "ex", - "exs", - "f", - "f03", - "f08", - "f77", - "f90", - "f95", - "flow", - "for", - "fs", - "fsi", - "fsx", - "gd", - "go", - "gql", - "graphql", - "groovy", - "gs", - "gsp", - "gst", - "gsx", - "gvy", - "h", - "hack", - "hh", - "hpp", - "hrl", - "hs", - "htm", - "html", - "hy", - "idl", - "io", - "jar", - "java", - "jl", - "js", - "json", - "jsx", - "kt", - "kts", - "lean", - "lgt", - "lhs", - "lisp", - "logtalk", - "lsp", - "lua", - "m", - "ma", - "mak", - "makefile", - "md", - "mjs", - "ml", - "mli", - "mpl", - "ms", - "mu", - "mv", - "n", - "nb", - "nim", - "nix", - "oot", - "oz", - "pas", - "pasm", - "perl", - "php", - "phtml", - "pike", - "pir", - "pl", - "pm", - "pmod", - "pp", - "pro", - "prolog", - "ps1", - "psd1", - "psm1", - "purs", - "py", - "pyw", - "qs", - "r", - "raku", - "rakumod", - "rakutest", - "rb", - "rbw", - "rdata", - "re", - "red", - "reds", - "res", - "rex", - "rexx", - "ring", - "rkt", - "rktl", - "rlib", - "rm", - "rmd", - "roff", - "ron", - "rs", - "ruby", - "s", - "sas", - "sb", - "sb2", - "sb3", - "sc", - "scala", - "scd", - "scm", - "scss", - "sass", - "sh", - "shen", - "sig", - "sml", - "sol", - "sql", - "ss", - "st", - "sv", - "swift", - "t", - "tcl", - "tf", - "trigger", - "ts", - "tsx", - "tu", - "v", - "vala", - "vapi", - "vb", - "vba", - "vbx", - "vhd", - "vhdl", - "vue", - "x", - "xc", - "xi", - "xml", - "yaml", - "yml", - "zig" - ) -} diff --git a/plugins/core/jetbrains-community/src/software/aws/toolkits/jetbrains/services/telemetry/OpenedFileTypesMetrics.kt b/plugins/core/jetbrains-community/src/software/aws/toolkits/jetbrains/services/telemetry/OpenedFileTypesMetrics.kt new file mode 100644 index 00000000000..43065a43a5a --- /dev/null +++ b/plugins/core/jetbrains-community/src/software/aws/toolkits/jetbrains/services/telemetry/OpenedFileTypesMetrics.kt @@ -0,0 +1,65 @@ +// Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package software.aws.toolkits.jetbrains.services.telemetry + +import com.intellij.openapi.Disposable +import com.intellij.openapi.application.ApplicationManager +import com.intellij.openapi.components.Service +import com.intellij.openapi.components.service +import com.intellij.openapi.fileEditor.FileEditorManager +import com.intellij.openapi.fileEditor.FileEditorManagerListener +import com.intellij.openapi.vfs.VirtualFile +import com.intellij.util.Alarm +import org.jetbrains.annotations.TestOnly +import software.aws.toolkits.telemetry.IdeTelemetry + +class OpenedFileTypesMetricsListener : FileEditorManagerListener { + override fun fileOpened(source: FileEditorManager, file: VirtualFile) { + val extension = file.extension ?: return + source.project.service().addToExistingTelemetryBatch(extension) + } +} + +@Service(Service.Level.PROJECT) +class OpenedFileTypesMetricsService : Disposable { + private val currentOpenedFileTypes = mutableSetOf() + private val alarm = Alarm(Alarm.ThreadToUse.POOLED_THREAD, this) + override fun dispose() {} + + init { + scheduleNextMetricEvent() + } + + private fun scheduleNextMetricEvent() { + alarm.addRequest(this::emitFileTypeMetric, INTERVAL_BETWEEN_METRICS) + } + + @Synchronized + fun emitFileTypeMetric() { + currentOpenedFileTypes.forEach { + emitMetric(it) + } + currentOpenedFileTypes.clear() + if (!ApplicationManager.getApplication().isUnitTestMode) { + scheduleNextMetricEvent() + } + } + + @TestOnly + fun getOpenedFileTypes(): Set = currentOpenedFileTypes + + @Synchronized + fun addToExistingTelemetryBatch(fileExt: String) { + if (fileExt in ALLOWED_CODE_EXTENSIONS) { + currentOpenedFileTypes.add(fileExt) + } + } + + private fun emitMetric(openFileExtension: String) = + IdeTelemetry.editCodeFile(project = null, filenameExt = openFileExtension) + + companion object { + private const val INTERVAL_BETWEEN_METRICS = 30 * 60 * 1000 + } +} diff --git a/plugins/core/jetbrains-community/src/software/aws/toolkits/jetbrains/services/telemetry/TelemetryUtils.kt b/plugins/core/jetbrains-community/src/software/aws/toolkits/jetbrains/services/telemetry/TelemetryUtils.kt new file mode 100644 index 00000000000..32e5164eefe --- /dev/null +++ b/plugins/core/jetbrains-community/src/software/aws/toolkits/jetbrains/services/telemetry/TelemetryUtils.kt @@ -0,0 +1,202 @@ +// Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package software.aws.toolkits.jetbrains.services.telemetry + +val ALLOWED_CODE_EXTENSIONS = setOf( + "abap", + "ada", + "adb", + "ads", + "apl", + "asm", + "awk", + "b", + "bas", + "bash", + "bat", + "boo", + "c", + "cbl", + "cc", + "cfc", + "cfm", + "cjs", + "clj", + "cljc", + "cljs", + "cls", + "cmake", + "cob", + "cobra", + "coffee", + "cpp", + "cpy", + "cr", + "cs", + "css", + "csx", + "cxx", + "d", + "dart", + "dfm", + "dpr", + "e", + "el", + "elm", + "erl", + "ex", + "exs", + "f", + "f03", + "f08", + "f77", + "f90", + "f95", + "flow", + "for", + "fs", + "fsi", + "fsx", + "gd", + "go", + "gql", + "graphql", + "groovy", + "gs", + "gsp", + "gst", + "gsx", + "gvy", + "h", + "hack", + "hh", + "hpp", + "hrl", + "hs", + "htm", + "html", + "hy", + "idl", + "io", + "jar", + "java", + "jl", + "js", + "json", + "jsx", + "kt", + "kts", + "lean", + "lgt", + "lhs", + "lisp", + "logtalk", + "lsp", + "lua", + "m", + "ma", + "mak", + "makefile", + "md", + "mjs", + "ml", + "mli", + "mpl", + "ms", + "mu", + "mv", + "n", + "nb", + "nim", + "nix", + "oot", + "oz", + "pas", + "pasm", + "perl", + "php", + "phtml", + "pike", + "pir", + "pl", + "pm", + "pmod", + "pp", + "pro", + "prolog", + "ps1", + "psd1", + "psm1", + "purs", + "py", + "pyw", + "qs", + "r", + "raku", + "rakumod", + "rakutest", + "rb", + "rbw", + "rdata", + "re", + "red", + "reds", + "res", + "rex", + "rexx", + "ring", + "rkt", + "rktl", + "rlib", + "rm", + "rmd", + "roff", + "ron", + "rs", + "ruby", + "s", + "sas", + "sb", + "sb2", + "sb3", + "sc", + "scala", + "scd", + "scm", + "scss", + "sass", + "sh", + "shen", + "sig", + "sml", + "sol", + "sql", + "ss", + "st", + "sv", + "swift", + "t", + "tcl", + "tf", + "trigger", + "ts", + "tsx", + "tu", + "v", + "vala", + "vapi", + "vb", + "vba", + "vbx", + "vhd", + "vhdl", + "vue", + "x", + "xc", + "xi", + "xml", + "yaml", + "yml", + "zig" +) diff --git a/plugins/core/jetbrains-community/tst/software/aws/toolkits/jetbrains/services/telemetry/OpenedFileTypeMetricsTest.kt b/plugins/core/jetbrains-community/tst/software/aws/toolkits/jetbrains/services/telemetry/OpenedFileTypeMetricsTest.kt new file mode 100644 index 00000000000..21aa8bd481c --- /dev/null +++ b/plugins/core/jetbrains-community/tst/software/aws/toolkits/jetbrains/services/telemetry/OpenedFileTypeMetricsTest.kt @@ -0,0 +1,36 @@ +// Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package software.aws.toolkits.jetbrains.services.telemetry + +import org.junit.After +import org.junit.Before +import org.junit.Test +import org.mockito.kotlin.times + +class OpenedFileTypeMetricsTest { + + private lateinit var service: OpenedFileTypesMetricsService + + @Before + fun setup() { + service = OpenedFileTypesMetricsService() + } + + @After + fun teardown() { + service.dispose() + } + + @Test + fun `test addToExistingTelemetryBatch with allowed extension`() { + service.addToExistingTelemetryBatch("kt") + assert(service.getOpenedFileTypes().contains("kt")) + } + + @Test + fun `test addToExistingTelemetryBatch with disallowed extension`() { + service.addToExistingTelemetryBatch("txt") + assert(service.getOpenedFileTypes().isEmpty()) + } +} diff --git a/plugins/core/src/main/resources/META-INF/plugin.xml b/plugins/core/src/main/resources/META-INF/plugin.xml index 10e35898377..dbc7e6b3972 100644 --- a/plugins/core/src/main/resources/META-INF/plugin.xml +++ b/plugins/core/src/main/resources/META-INF/plugin.xml @@ -23,4 +23,7 @@ + + +