Skip to content

Commit

Permalink
Add status service
Browse files Browse the repository at this point in the history
  • Loading branch information
AaronFeledy committed Jun 16, 2024
1 parent 597fc63 commit 8e889b1
Show file tree
Hide file tree
Showing 7 changed files with 279 additions and 158 deletions.
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
package com.github.aaronfeledy.landointellijplugin

import com.github.aaronfeledy.landointellijplugin.services.LandoAppService
import com.github.aaronfeledy.landointellijplugin.services.LandoProjectService
import com.intellij.execution.process.ProcessHandler
import com.intellij.execution.ui.ConsoleViewContentType
import com.github.aaronfeledy.landointellijplugin.ui.console.JeditermConsoleView
import com.intellij.openapi.project.Project
import java.io.File
import java.io.OutputStream

Expand All @@ -20,6 +21,8 @@ class LandoExec(private val command: String) {

private var attachToConsole: Boolean = true

var project: Project? = null

/**
* Sets the directory for the Lando command.
* @param path The directory path.
Expand Down Expand Up @@ -60,9 +63,11 @@ class LandoExec(private val command: String) {
// Get the LandoAppService instance
val appService = LandoAppService.getInstance()

// If the directory is not set, use the root directory of the Lando application
if (directory.isEmpty()) {
directory = appService.appRoot?.path!!
// Default to the project root as working directory if none is set
if (directory.isEmpty() && project != null) {
directory = LandoProjectService.getInstance(project!!).appRoot?.path!!
} else if (directory.isEmpty()) {
directory = System.getProperty("user.dir")
}
// Set the directory for the process builder
processBuilder.directory(File(directory))
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
package com.github.aaronfeledy.landointellijplugin.listeners

import com.intellij.openapi.components.service
import com.github.aaronfeledy.landointellijplugin.services.LandoProjectService
import com.intellij.openapi.project.Project
import com.intellij.openapi.project.ProjectManagerListener
import com.github.aaronfeledy.landointellijplugin.services.LandoProjectService

internal class LandoManagerListener : ProjectManagerListener {

class LandoProjectManagerListener : ProjectManagerListener {
override fun projectOpened(project: Project) {
project.service<LandoProjectService>()
LandoProjectService.getInstance(project)
}
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
package com.github.aaronfeledy.landointellijplugin.listeners

interface LandoAppServiceListener {
interface LandoStatusListener {
fun statusChanged()
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,29 +1,53 @@
package com.github.aaronfeledy.landointellijplugin.services

import com.intellij.openapi.project.Project
import com.github.aaronfeledy.landointellijplugin.LandoExec
import com.github.aaronfeledy.landointellijplugin.ServiceData
import com.github.aaronfeledy.landointellijplugin.listeners.LandoProjectManagerListener
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import com.intellij.execution.process.ProcessHandler
import com.intellij.openapi.Disposable
import com.intellij.openapi.components.Service
import com.intellij.openapi.components.service
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.project.Project
import com.intellij.openapi.project.guessProjectDir
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.util.ConcurrencyUtil
import com.intellij.util.messages.Topic
import java.util.*
import java.util.concurrent.ScheduledExecutorService
import java.util.concurrent.TimeUnit

// The name of the Lando configuration file
const val landoFileName = ".lando.yml"
val logger = Logger.getInstance(LandoProjectService::class.java)

/**
* Service for managing Lando project-specific data and operations.
*
* This service is scoped at the Project level in the IntelliJ Platform, meaning a separate instance is created for each open Project.
* It's responsible for managing the Lando configuration file (.lando.yml) and providing utility methods related to the Lando project.
*
* @property project The IntelliJ Project this service is associated with.
* Service class for managing Lando applications.
* This class is responsible for fetching and maintaining the status of Lando applications.
* @param project The IntelliJ Project this service is associated with.
*/
@Service(Service.Level.PROJECT)
class LandoProjectService(val project: Project) {
class LandoProjectService(val project: Project) : Disposable {
companion object {
const val LANDOFILE_NAME = ".lando.yml"

val TOPIC = Topic.create("LandoServiceTopic", LandoProjectManagerListener::class.java)

/**
* Retrieves the [LandoProjectService] instance associated with the given Project.
*
* @param project The IntelliJ Project this service is associated with.
* @return The [LandoProjectService] instance associated with the given Project.
*/
@JvmStatic
fun getInstance(project: Project): LandoProjectService = project.getService(LandoProjectService::class.java)
}

// The root directory of the project. It's where we expect to find the Lando configuration file.
var projectDir: VirtualFile? = project.guessProjectDir()

// The Lando configuration file in the project directory. It's presence determines whether the project uses Lando.
var landoFile: VirtualFile? = projectDir?.findChild(landoFileName)
var landoFile: VirtualFile? = projectDir?.findChild(LANDOFILE_NAME)

/**
* Checks if the current project uses Lando.
Expand All @@ -33,21 +57,83 @@ class LandoProjectService(val project: Project) {
*
* @return True if the project uses Lando, false otherwise.
*/
fun usesLando(): Boolean {
fun projectUsesLando(): Boolean {
return landoFile?.exists()!!
}

companion object {
/**
* Retrieves the LandoProjectService instance associated with the given Project.
*
* This static method is a convenience for getting the LandoProjectService instance without having to manually fetch it from the ServiceManager.
* It's used throughout the codebase whenever access to the LandoProjectService is needed.
*
* @param project The Project for which to retrieve the LandoProjectService.
* @return The LandoProjectService instance associated with the given Project.
*/
@JvmStatic
fun getInstance(project: Project): LandoProjectService = project.service()
// ScheduledExecutorService for periodically checking the status of the Lando application
private val statusWatcher: ScheduledExecutorService = ConcurrencyUtil.newSingleScheduledThreadExecutor("Lando Status Watcher")
.apply { scheduleWithFixedDelay({ fetchInfo() }, 0, 2000, TimeUnit.MILLISECONDS) }

// Root directory of the Lando application
var appRoot: VirtualFile? = this.projectDir

// Map for storing the status of the services in the Lando application
var services: MutableMap<String, ServiceData> = HashMap()

// Indicate whether the Lando application has been started
var started: Boolean = false
set(value) {
field = value
notifyListeners()
}
get() {
return services.isNotEmpty()
}

init {
if (appRoot == null) {
logger.debug("Could not find project root directory")
}
}

/**
* Fetches the information of the services in the Lando application.
* @return [ProcessHandler] for the process fetching the service information.
*/
private fun fetchLandoServiceInfo(): ProcessHandler {
logger.debug("Fetching Lando service info...")
val exec = LandoExec("info")
exec.project = project
return exec.fetchJson().run()
}

/**
* Checks the status of the Lando application and updates the services map.
*/
private fun fetchInfo() {
logger.debug("Checking Lando status...")
val serviceInfoHandler = fetchLandoServiceInfo()
serviceInfoHandler.waitFor(30000)
if (!serviceInfoHandler.isProcessTerminated) {
serviceInfoHandler.destroyProcess()
logger.warn("Timeout while fetching Lando service info")
}
val serviceInfoJson = serviceInfoHandler.processInput.toString()
val serviceData = parseServiceData(serviceInfoJson)

services = serviceData.associateBy { it.service }.toMutableMap()
}

/**
* Parses the JSON data of the service information into a list of ServiceData objects.
* @param jsonData JSON data of the service information.
* @return List of ServiceData objects.
*/
private fun parseServiceData(jsonData: String): List<ServiceData> {
val gson = Gson()
val serviceListType = object : TypeToken<List<ServiceData>>() {}.type
return gson.fromJson(jsonData, serviceListType)
}

private fun notifyListeners() {
ApplicationManager.getApplication().messageBus.syncPublisher(TOPIC)
}

/**
* Disposes the LandoAppService, shutting down the status watcher.
*/
override fun dispose() {
statusWatcher.shutdown()
}
}
Loading

0 comments on commit 8e889b1

Please sign in to comment.