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

chore: Include proKey and vaadiner information in statistic requests #303

Merged
merged 2 commits into from
Feb 11, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
43 changes: 34 additions & 9 deletions src/main/kotlin/com/amplitude/ampli/Ampli.kt
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,10 @@ class ManualCopilotRestart private constructor(eventProperties: Map<String, Any?
* [View in Tracking Plan](https://data.amplitude.com/vaadin/IDE%20Plugins/events/main/latest/ManualCopilotRestart)
*
* Event has no description in tracking plan.
*
* @param vaadiner Property has no description provided in tracking plan.
*/
constructor() : this(null, null as EventOptions?)
constructor(vaadiner: Boolean) : this(mapOf("Vaadiner" to vaadiner), null as EventOptions?)
}

class PluginInitialized private constructor(eventProperties: Map<String, Any?>?, options: EventOptions? = null) :
Expand All @@ -108,8 +110,16 @@ class PluginInitialized private constructor(eventProperties: Map<String, Any?>?,
* [View in Tracking Plan](https://data.amplitude.com/vaadin/IDE%20Plugins/events/main/latest/PluginInitialized)
*
* Event has no description in tracking plan.
*
* @param vaadiner Property has no description provided in tracking plan.
* @param proKey Property has no description provided in tracking plan.
*/
constructor() : this(null, null as EventOptions?)
constructor(
vaadiner: Boolean,
proKey: String? = null
) : this(
mapOf(*(if (proKey != null) arrayOf("ProKey" to proKey) else arrayOf()), "Vaadiner" to vaadiner),
null as EventOptions?)
}

class ProjectCreated private constructor(eventProperties: Map<String, Any?>?, options: EventOptions? = null) :
Expand All @@ -121,12 +131,15 @@ class ProjectCreated private constructor(eventProperties: Map<String, Any?>?, op
*
* Event has no description in tracking plan.
*
* @param vaadiner Property has no description provided in tracking plan.
* @param downloadUrl Property has no description provided in tracking plan.
*/
constructor(
vaadiner: Boolean,
downloadUrl: String? = null
) : this(
mapOf(*(if (downloadUrl != null) arrayOf("downloadUrl" to downloadUrl) else arrayOf())), null as EventOptions?)
mapOf(*(if (downloadUrl != null) arrayOf("downloadUrl" to downloadUrl) else arrayOf()), "Vaadiner" to vaadiner),
null as EventOptions?)
}

val ampli = Ampli()
Expand Down Expand Up @@ -249,11 +262,17 @@ open class Ampli {
* Event has no description in tracking plan.
*
* @param userId The user's ID
* @param event The event
* @param options Amplitude event options
* @param extra Extra untyped parameters for use in middleware
*/
fun manualCopilotRestart(userId: String?, options: EventOptions? = null, extra: MiddlewareExtra? = null) {
this.track(userId, ManualCopilotRestart(), options, extra)
fun manualCopilotRestart(
userId: String?,
event: ManualCopilotRestart,
options: EventOptions? = null,
extra: MiddlewareExtra? = null
) {
this.track(userId, event, options, extra)
}

/**
Expand All @@ -264,11 +283,17 @@ open class Ampli {
* Event has no description in tracking plan.
*
* @param userId The user's ID
* @param event The event
* @param options Amplitude event options
* @param extra Extra untyped parameters for use in middleware
*/
fun pluginInitialized(userId: String?, options: EventOptions? = null, extra: MiddlewareExtra? = null) {
this.track(userId, PluginInitialized(), options, extra)
fun pluginInitialized(
userId: String?,
event: PluginInitialized,
options: EventOptions? = null,
extra: MiddlewareExtra? = null
) {
this.track(userId, event, options, extra)
}

/**
Expand All @@ -285,11 +310,11 @@ open class Ampli {
*/
fun projectCreated(
userId: String?,
event: ProjectCreated? = null,
event: ProjectCreated,
options: EventOptions? = null,
extra: MiddlewareExtra? = null
) {
this.track(userId, event ?: ProjectCreated(), options, extra)
this.track(userId, event, options, extra)
}

private fun createAmplitudeEvent(
Expand Down
7 changes: 1 addition & 6 deletions src/main/kotlin/com/vaadin/plugin/VaadinProjectDetector.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import com.intellij.openapi.project.Project
import com.intellij.openapi.roots.ModuleRootEvent
import com.intellij.openapi.roots.ModuleRootListener
import com.intellij.openapi.startup.ProjectActivity
import com.vaadin.plugin.listeners.VaadinProjectListener
import com.vaadin.plugin.utils.doNotifyAboutVaadinProject
import com.vaadin.plugin.utils.hasVaadin

class VaadinProjectDetector : ModuleRootListener, ProjectActivity {
Expand All @@ -30,9 +30,4 @@ class VaadinProjectDetector : ModuleRootListener, ProjectActivity {
}
}
}

private fun doNotifyAboutVaadinProject(project: Project) {
val publisher: VaadinProjectListener = project.messageBus.syncPublisher(VaadinProjectListener.TOPIC)
publisher.vaadinProjectDetected(project)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,26 +11,28 @@ import com.vaadin.plugin.ui.VaadinStatusBarWidget

class CopilotVaadinProjectListener : VaadinProjectListener {

private var triggered = false
private var listenerRegistered = false

override fun vaadinProjectDetected(project: Project) {
if (!triggered && !project.isDisposed) {
triggered = true
if (!project.isDisposed) {
saveDotFile(project)
removeDotFileOnExit(project)
DumbService.getInstance(project).smartInvokeLater { VaadinStatusBarWidget.update(project) }
}
}

private fun removeDotFileOnExit(project: Project) {
ProjectManager.getInstance()
.addProjectManagerListener(
project,
object : ProjectManagerListener {
override fun projectClosing(project: Project) {
CopilotPluginUtil.removeDotFile(project)
}
},
)
if (!listenerRegistered) {
listenerRegistered = true
ProjectManager.getInstance()
.addProjectManagerListener(
project,
object : ProjectManagerListener {
override fun projectClosing(project: Project) {
CopilotPluginUtil.removeDotFile(project)
}
},
)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ import com.intellij.util.ui.JBEmptyBorder
import com.intellij.util.ui.JBFont
import com.intellij.util.ui.UIUtil
import com.vaadin.plugin.copilot.CopilotPluginUtil
import com.vaadin.plugin.utils.doNotifyAboutVaadinProject
import com.vaadin.plugin.utils.hasEndpoints
import com.vaadin.plugin.utils.trackManualCopilotRestart
import java.awt.Component
import javax.swing.JButton
import javax.swing.JComponent
Expand Down Expand Up @@ -59,9 +59,7 @@ class VaadinStatusBarInfoPopupPanel(private val project: Project) : JPanel() {
} else {
val restart = JButton(AllIcons.Actions.Restart)
restart.addActionListener {
CopilotPluginUtil.removeDotFile(project)
CopilotPluginUtil.saveDotFile(project)
trackManualCopilotRestart()
doNotifyAboutVaadinProject(project)
DumbService.getInstance(project).smartInvokeLater {
VaadinStatusBarWidget.update(project)
afterRestart?.invoke()
Expand Down
2 changes: 2 additions & 0 deletions src/main/kotlin/com/vaadin/plugin/ui/VaadinStatusBarWidget.kt
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import com.intellij.util.ui.UIUtil
import com.vaadin.plugin.copilot.CopilotPluginUtil
import com.vaadin.plugin.utils.VaadinIcons
import com.vaadin.plugin.utils.hasEndpoints
import com.vaadin.plugin.utils.trackManualCopilotRestart
import java.awt.Point
import java.awt.event.MouseEvent
import javax.swing.Icon
Expand Down Expand Up @@ -68,6 +69,7 @@ class VaadinStatusBarWidget(private val project: Project) : StatusBarWidget, Sta
panel.afterRestart = {
popup.cancel()
showPopup(e)
trackManualCopilotRestart()
}
val dimension = popup.content.preferredSize
val at = Point(0, -dimension.height)
Expand Down
53 changes: 37 additions & 16 deletions src/main/kotlin/com/vaadin/plugin/utils/AmpliUtil.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,18 @@ import ai.grazie.utils.mpp.UUID
import com.amplitude.ampli.Ampli
import com.amplitude.ampli.EventOptions
import com.amplitude.ampli.LoadOptions
import com.amplitude.ampli.ManualCopilotRestart
import com.amplitude.ampli.PluginInitialized
import com.amplitude.ampli.ProjectCreated
import com.amplitude.ampli.ampli
import com.intellij.openapi.application.ApplicationInfo
import com.intellij.util.io.DigestUtil
import com.vaadin.plugin.copilot.CopilotPluginUtil
import com.vaadin.plugin.ui.settings.VaadinSettings
import com.vaadin.pro.licensechecker.LocalProKey
import com.vaadin.pro.licensechecker.ProKey
import elemental.json.Json
import java.io.IOException
import java.nio.charset.Charset
import java.util.Objects

private val eventOptions =
EventOptions(
Expand All @@ -28,42 +30,61 @@ private val eventOptions =

private var userId: String? = null

private fun getUserId(): String? {
private var vaadiner: Boolean? = null

private fun getUserId(): String {
if (userId == null) {
val proKey: ProKey? = LocalProKey.get()
userId =
if (proKey != null) {
"pro-${DigestUtil.sha256Hex(proKey.proKey.toByteArray(Charset.defaultCharset()))}"
} else {
val state: VaadinSettings.State = Objects.requireNonNull(VaadinSettings.instance.state)
if (state.userId == null) {
state.userId = "user-${UUID.random().text}"
}
state.userId
try {
VaadinHomeUtil.getUserKey()
} catch (e: IOException) {
"user-" + UUID.random().text
}
ampli.load(LoadOptions(Ampli.Environment.IDEPLUGINS))
ampli.identify(userId, eventOptions)
}
return userId
return userId!!
}

private fun isVaadiner(): Boolean {
if (vaadiner == null) {
val proKey = LocalProKey.get()
if (proKey != null) {
val json = Json.parse(proKey.toJson())
vaadiner = if (json.hasKey("username")) json.getString("username").endsWith("@vaadin.com") else false
} else {
vaadiner = false
}
}
return vaadiner!!
}

private fun getProKeyDigest(): String? {
val proKey = LocalProKey.get()
return if (proKey != null) {
DigestUtil.sha256Hex(proKey.proKey.toByteArray(Charset.defaultCharset()))
} else {
null
}
}

private val enabled: Boolean
get() = VaadinSettings.instance.state.sendUsageStatistics

internal fun trackPluginInitialized() {
if (enabled) {
ampli.pluginInitialized(getUserId())
ampli.pluginInitialized(getUserId(), PluginInitialized(isVaadiner(), getProKeyDigest()))
}
}

internal fun trackProjectCreated(downloadUrl: String) {
if (enabled) {
ampli.projectCreated(getUserId(), ProjectCreated(downloadUrl))
ampli.projectCreated(getUserId(), ProjectCreated(isVaadiner(), downloadUrl))
}
}

internal fun trackManualCopilotRestart() {
if (enabled) {
ampli.manualCopilotRestart(getUserId())
ampli.manualCopilotRestart(getUserId(), ManualCopilotRestart(isVaadiner()))
}
}
17 changes: 17 additions & 0 deletions src/main/kotlin/com/vaadin/plugin/utils/VaadinHomeUtil.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@ package com.vaadin.plugin.utils

import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.util.io.FileUtil
import elemental.json.Json
import java.io.File
import java.io.IOException
import java.nio.charset.Charset
import java.nio.file.Files
import java.util.*
import java.util.jar.JarFile

Expand All @@ -23,6 +26,20 @@ object VaadinHomeUtil {
return File(userHome, VAADIN_FOLDER_NAME)
}

@Throws(IOException::class)
fun getUserKey(): String {
val vaadinHome = resolveVaadinHomeDirectory()
val userKeyFile = File(vaadinHome, "userKey")
if (userKeyFile.exists()) {
val content = Files.readString(userKeyFile.toPath())
return Json.parse(content).getString("key")
} else {
val key = "user-${UUID.randomUUID()}"
Files.write(userKeyFile.toPath(), key.toByteArray(Charset.defaultCharset()))
return key
}
}

/**
* Gets the hotswap-agent.jar location in ~/.vaadin.
*
Expand Down
6 changes: 6 additions & 0 deletions src/main/kotlin/com/vaadin/plugin/utils/VaadinProjectUtil.kt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import com.intellij.openapi.util.io.FileUtil
import com.intellij.openapi.vfs.VirtualFileManager
import com.intellij.util.download.DownloadableFileService
import com.intellij.util.io.ZipUtil
import com.vaadin.plugin.listeners.VaadinProjectListener
import com.vaadin.plugin.starter.DownloadableModel
import java.io.File
import java.io.IOException
Expand Down Expand Up @@ -80,6 +81,11 @@ class VaadinProjectUtil {
}
}

internal fun doNotifyAboutVaadinProject(project: Project) {
val publisher: VaadinProjectListener = project.messageBus.syncPublisher(VaadinProjectListener.TOPIC)
publisher.vaadinProjectDetected(project)
}

internal fun hasVaadin(project: Project): Boolean = hasLibraryClass(project, VAADIN_SERVICE)

internal fun hasVaadin(module: com.intellij.openapi.module.Module): Boolean = hasLibraryClass(module, VAADIN_SERVICE)
Expand Down
Loading