Skip to content

Commit

Permalink
Merge pull request #743 from JetBrains/develop.ra/function-runner
Browse files Browse the repository at this point in the history
Function local run
  • Loading branch information
rafaelldi committed Nov 24, 2023
2 parents d93fd6a + 4f3c804 commit e3204a1
Show file tree
Hide file tree
Showing 26 changed files with 1,995 additions and 41 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
* Copyright 2018-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the MIT license.
*/

package com.microsoft.azure.toolkit.intellij.legacy.function.actions

import com.intellij.httpClient.RestClientIcons
import com.intellij.httpClient.http.request.HttpRequestLanguage
import com.intellij.ide.fileTemplates.FileTemplateManager
import com.intellij.ide.scratch.ScratchFileService
import com.intellij.ide.scratch.ScratchRootType
import com.intellij.openapi.actionSystem.AnAction
import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.diagnostic.logger
import com.intellij.openapi.fileEditor.FileEditorManager
import com.intellij.openapi.project.Project
import java.io.IOException

class TriggerAzureFunctionAction(private val functionName: String) : AnAction(
"Trigger '$functionName'",
"Create trigger function HTTP request...",
RestClientIcons.Http_requests_filetype
) {
companion object {
private val LOG = logger<TriggerAzureFunctionAction>()
}

override fun actionPerformed(e: AnActionEvent) {
val project = e.project ?: return

val scratchFileName = "Trigger_$functionName.http"
val scratchFileService = ScratchFileService.getInstance()
val scratchRootType = ScratchRootType.getInstance()

val scratchFile =
scratchFileService.findFile(scratchRootType, scratchFileName, ScratchFileService.Option.existing_only)
?: scratchRootType.createScratchFile(
project,
scratchFileName,
HttpRequestLanguage.INSTANCE,
createContentFromTemplate(
project,
"Trigger Azure Function",
functionName
) ?: "",
ScratchFileService.Option.create_if_missing
)

if (scratchFile != null) {
FileEditorManager.getInstance(project).openFile(scratchFile, true)
}
}

private fun createContentFromTemplate(project: Project, templateName: String, functionName: String): String? {
val template = FileTemplateManager.getInstance(project).findInternalTemplate(templateName)
if (template != null) {
try {
val properties = FileTemplateManager.getInstance(project).defaultProperties
properties.setProperty("functionName", functionName)
return template.getText(properties)
} catch (e: IOException) {
LOG.warn(e)
}
}
return null
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
* Copyright 2018-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the MIT license.
*/

package com.microsoft.azure.toolkit.intellij.legacy.function.buildTasks

import com.intellij.execution.BeforeRunTask
import com.intellij.execution.BeforeRunTaskProvider
import com.intellij.execution.configurations.RunConfiguration
import com.intellij.execution.runners.ExecutionEnvironment
import com.intellij.icons.AllIcons
import com.intellij.openapi.actionSystem.DataContext
import com.intellij.openapi.util.Key
import com.jetbrains.rider.build.BuildHost
import com.jetbrains.rider.build.BuildParameters
import com.jetbrains.rider.build.tasks.BuildTaskThrottler
import com.jetbrains.rider.model.BuildTarget
import com.microsoft.azure.toolkit.intellij.legacy.function.runner.localRun.FunctionRunConfiguration
import javax.swing.Icon

class BuildFunctionsProjectBeforeRunTask :
BeforeRunTask<BuildFunctionsProjectBeforeRunTask>(BuildFunctionsProjectBeforeRunTaskProvider.ID)

class BuildFunctionsProjectBeforeRunTaskProvider : BeforeRunTaskProvider<BuildFunctionsProjectBeforeRunTask>() {
companion object {
val ID = Key.create<BuildFunctionsProjectBeforeRunTask>("BuildFunctionsProject")
}

override fun getId() = ID

override fun getName() = "Build Functions Project"

override fun getDescription(task: BuildFunctionsProjectBeforeRunTask) = "Build Functions project"

override fun isConfigurable() = false

override fun getIcon(): Icon = AllIcons.Actions.Compile

override fun createTask(runConfiguration: RunConfiguration): BuildFunctionsProjectBeforeRunTask? {
if (runConfiguration !is FunctionRunConfiguration) return null

return BuildFunctionsProjectBeforeRunTask().apply { isEnabled = true }
}

override fun canExecuteTask(configuration: RunConfiguration, task: BuildFunctionsProjectBeforeRunTask): Boolean {
return BuildHost.getInstance(configuration.project).ready.value
}

override fun executeTask(
context: DataContext,
configuration: RunConfiguration,
env: ExecutionEnvironment,
task: BuildFunctionsProjectBeforeRunTask
): Boolean {
val project = configuration.project
val buildHost = BuildHost.getInstance(project)
val selectedProjectsForBuild = when (configuration) {
is FunctionRunConfiguration -> listOf(configuration.parameters.projectFilePath)
else -> emptyList()
}

if (!buildHost.ready.value) return false

val throttler = BuildTaskThrottler.getInstance(project)
return throttler.buildSequentiallySync(BuildParameters(BuildTarget(), selectedProjectsForBuild)).msBuildStatus
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,9 @@ import com.intellij.psi.xml.XmlElementType
import com.intellij.psi.xml.XmlTag
import com.intellij.xml.util.XmlUtil
import com.microsoft.azure.toolkit.intellij.legacy.function.FUNCTIONS_CORE_TOOLS_KNOWN_SUPPORTED_VERSIONS
import com.microsoft.azure.toolkit.intellij.legacy.function.coreTools.FunctionCoreToolsMsBuildService.Companion.PROPERTY_AZURE_FUNCTIONS_VERSION

class AzureFunctionsVersionInspection : XmlSuppressableInspectionTool() {
companion object {
const val PROPERTY_AZURE_FUNCTIONS_VERSION = "AzureFunctionsVersion"
}

override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean) = object : XmlElementVisitor() {
override fun visitXmlTag(tag: XmlTag) {
val tagName = tag.name.lowercase()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,16 @@
* Copyright 2018-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the MIT license.
*/

@file:Suppress("UnstableApiUsage")

package com.microsoft.azure.toolkit.intellij.legacy.function.coreTools

import com.intellij.openapi.components.Service
import com.intellij.openapi.components.service
import com.intellij.openapi.diagnostic.logger
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.SystemInfo
import com.intellij.openapi.util.registry.Registry
import com.intellij.util.application
import com.intellij.util.concurrency.ThreadingAssertions
import com.microsoft.azure.toolkit.intellij.legacy.function.coreTools.FunctionCoreToolsMsBuildService.Companion.PROPERTY_AZURE_FUNCTIONS_VERSION
import com.microsoft.azure.toolkit.intellij.legacy.function.settings.AzureFunctionSettings
import com.microsoft.azure.toolkit.lib.appservice.utils.FunctionCliResolver
import java.io.File
Expand All @@ -24,11 +24,29 @@ class FunctionCoreToolsInfoProvider {
private val LOG = logger<FunctionCoreToolsInfoProvider>()
}

suspend fun retrieveForProject(
project: Project,
projectFilePath: String,
allowDownload: Boolean
): FunctionCoreToolsInfo? {
val azureFunctionsVersion = FunctionCoreToolsMsBuildService.getInstance()
.requestAzureFunctionsVersion(project, projectFilePath)

if (azureFunctionsVersion == null) {
LOG.error("Could not determine project MSBuild property '${PROPERTY_AZURE_FUNCTIONS_VERSION}'.")
return null
}

LOG.info("MSBuild property ${PROPERTY_AZURE_FUNCTIONS_VERSION}: $azureFunctionsVersion")

return retrieveForVersion(azureFunctionsVersion, allowDownload)
}

suspend fun retrieveForVersion(
azureFunctionsVersion: String,
allowDownload: Boolean
): FunctionCoreToolsInfo? {
application.assertIsNonDispatchThread()
ThreadingAssertions.assertBackgroundThread()

val coreToolsFromConfiguration = retrieveFromConfiguration(azureFunctionsVersion)
if (coreToolsFromConfiguration != null) return coreToolsFromConfiguration
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import com.jetbrains.rider.projectView.solution
class FunctionCoreToolsMsBuildService {
companion object {
fun getInstance(): FunctionCoreToolsMsBuildService = service()
const val PROPERTY_AZURE_FUNCTIONS_VERSION = "AzureFunctionsVersion"
}

suspend fun requestAzureFunctionsVersion(project: Project, projectFilePath: String) =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,29 @@
package com.microsoft.azure.toolkit.intellij.legacy.function.daemon

import com.intellij.execution.Executor
import com.intellij.execution.ProgramRunnerUtil
import com.intellij.execution.RunManager
import com.intellij.execution.RunnerAndConfigurationSettings
import com.intellij.execution.configurations.ConfigurationTypeUtil
import com.intellij.execution.executors.DefaultDebugExecutor
import com.intellij.execution.executors.DefaultRunExecutor
import com.intellij.openapi.actionSystem.ActionPlaces
import com.intellij.openapi.actionSystem.ex.ActionUtil
import com.intellij.openapi.actionSystem.impl.SimpleDataContext
import com.intellij.openapi.client.ClientProjectSession
import com.intellij.openapi.project.Project
import com.intellij.util.execution.ParametersListUtil
import com.jetbrains.rd.protocol.SolutionExtListener
import com.jetbrains.rd.ui.bedsl.extensions.valueOrEmpty
import com.jetbrains.rd.util.lifetime.Lifetime
import com.jetbrains.rider.azure.model.FunctionAppDaemonModel
import com.jetbrains.rider.model.RunnableProject
import com.jetbrains.rider.model.runnableProjectsModel
import com.jetbrains.rider.projectView.solution
import com.microsoft.azure.toolkit.intellij.legacy.function.actions.TriggerAzureFunctionAction
import com.microsoft.azure.toolkit.intellij.legacy.function.runner.localRun.FunctionRunConfiguration
import com.microsoft.azure.toolkit.intellij.legacy.function.runner.localRun.FunctionRunConfigurationFactory
import com.microsoft.azure.toolkit.intellij.legacy.function.runner.localRun.FunctionRunConfigurationType

class FunctionAppSolutionExtListener : SolutionExtListener<FunctionAppDaemonModel> {
override fun extensionCreated(lifetime: Lifetime, session: ClientProjectSession, model: FunctionAppDaemonModel) {
Expand All @@ -40,15 +50,14 @@ class FunctionAppSolutionExtListener : SolutionExtListener<FunctionAppDaemonMode
)
}
model.triggerFunctionApp.advise(lifetime) {
// val triggerAction = TriggerAzureFunctionAction(functionName = triggerFunctionRequest.functionName)
//
// ActionUtil.invokeAction(
// triggerAction,
// SimpleDataContext.getProjectContext(project),
// ActionPlaces.EDITOR_GUTTER_POPUP,
// null,
// null
// )
val triggerAction = TriggerAzureFunctionAction(functionName = it.functionName ?: "")
ActionUtil.invokeAction(
triggerAction,
SimpleDataContext.getProjectContext(session.project),
ActionPlaces.EDITOR_GUTTER_POPUP,
null,
null
)
}
}

Expand All @@ -70,14 +79,83 @@ class FunctionAppSolutionExtListener : SolutionExtListener<FunctionAppDaemonMode
project: Project
) {
val runManager = RunManager.getInstance(project)
val existingSettings = findExistingConfigurationSettings(functionName, runnableProject.projectFilePath)
val existingSettings = findExistingConfigurationSettings(functionName, runnableProject.projectFilePath, project)

val settings = existingSettings ?: let {
val configuration = createFunctionAppRunConfiguration(project, functionName, runnableProject)
val newSettings =
runManager.createConfiguration(configuration, configuration.factory as FunctionRunConfigurationFactory)

runManager.setTemporaryConfiguration(newSettings)
runManager.addConfiguration(newSettings)

newSettings
}

runManager.selectedConfiguration = settings
ProgramRunnerUtil.executeConfiguration(settings, executor)
}

private fun findExistingConfigurationSettings(
functionName: String?,
projectFilePath: String
projectFilePath: String,
project: Project
): RunnerAndConfigurationSettings? {
return null
val runManager = RunManager.getInstance(project)

val configurationType = ConfigurationTypeUtil.findConfigurationType(FunctionRunConfigurationType::class.java)
val runConfigurations = runManager.getConfigurationsList(configurationType)

return runConfigurations.filterIsInstance<FunctionRunConfiguration>().firstOrNull { configuration ->
configuration.parameters.projectFilePath == projectFilePath &&
((functionName.isNullOrEmpty() && configuration.parameters.functionNames.isEmpty()) ||
configuration.parameters.functionNames == functionName)
}?.let { configuration ->
runManager.findSettings(configuration)
}
}

private fun createFunctionAppRunConfiguration(
project: Project,
functionName: String?,
runnableProject: RunnableProject
): FunctionRunConfiguration {
val runManager = RunManager.getInstance(project)
val configurationType = ConfigurationTypeUtil.findConfigurationType(FunctionRunConfigurationType::class.java)

val factory = configurationType.factory
val template = runManager.getConfigurationTemplate(factory)

val configuration = template.configuration as FunctionRunConfiguration
patchConfigurationParameters(configuration, runnableProject, functionName)

return configuration
}

private fun patchConfigurationParameters(
configuration: FunctionRunConfiguration,
runnableProject: RunnableProject,
functionName: String?
) {
val projectOutput = runnableProject.projectOutputs.singleOrNull()

if (functionName.isNullOrEmpty()) {
// All functions
configuration.name = runnableProject.name
configuration.parameters.functionNames = ""
} else {
// Specific function
configuration.name = "$functionName (${runnableProject.fullName})"
configuration.parameters.functionNames = functionName
}

configuration.parameters.projectFilePath = runnableProject.projectFilePath
configuration.parameters.projectKind = runnableProject.kind
configuration.parameters.projectTfm = projectOutput?.tfm?.presentableName ?: ""
configuration.parameters.exePath = projectOutput?.exePath ?: ""
configuration.parameters.programParameters = ParametersListUtil.join(
projectOutput?.defaultArguments ?: listOf()
)
configuration.parameters.workingDirectory = projectOutput?.workingDirectory ?: ""
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,17 @@
* Copyright 2018-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the MIT license.
*/

package com.microsoft.azure.toolkit.intellij.legacy.function.runner
package com.microsoft.azure.toolkit.intellij.legacy.function.runner.deploy

import com.intellij.execution.configurations.ConfigurationTypeBase
import com.microsoft.azure.toolkit.ide.common.icon.AzureIcons
import com.microsoft.azure.toolkit.intellij.common.IntelliJAzureIcons
import com.microsoft.azure.toolkit.intellij.legacy.function.runner.deploy.FunctionDeploymentConfigurationFactory

class AzureFunctionConfigurationType : ConfigurationTypeBase(
class FunctionDeploymentConfigurationType : ConfigurationTypeBase(
"AzureFunctionAppPublish",
"Azure - Function App",
"Azure Publish Function App configuration",
IntelliJAzureIcons.getIcon(AzureIcons.FunctionApp.MODULE)
IntelliJAzureIcons.getIcon(AzureIcons.FunctionApp.DEPLOY)
) {
init {
addFactory(FunctionDeploymentConfigurationFactory(this))
Expand Down
Loading

0 comments on commit e3204a1

Please sign in to comment.