Skip to content

Commit

Permalink
Azure Functions: Make Azurite a before launch task #674 (#675)
Browse files Browse the repository at this point in the history
  • Loading branch information
maartenba authored Mar 29, 2023
1 parent 72c3804 commit 2be850f
Show file tree
Hide file tree
Showing 10 changed files with 119 additions and 57 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<h4>Added:</h4>
<ul>
<li>Compatibility with Rider 2023.1 EAP9</li>
<li>Azure Functions: Option to auto-start Azurite with run configurations (<a href="https://github.com/JetBrains/azure-tools-for-intellij/issues/605">#605</a>)</li>
<li>Azure Functions: Option to auto-start Azurite with run configurations (<a href="https://github.com/JetBrains/azure-tools-for-intellij/issues/674">#674</a>)</li>
<li>Azure Functions: show a warning when Azurite is not running (<a href="https://github.com/JetBrains/azure-tools-for-intellij/issues/605">#650</a>)</li>
<li>Azure Functions: Support Windows arm64 Azure Core Tools (<a href="https://youtrack.jetbrains.com/issue/RIDER-85548">#RIDER-85548</a>)</li>
</ul>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
<p>[3.50.0-2023.1]</p>
<ul>
<li>Compatibility with Rider 2023.1 EAP9</li>
<li>Azure Functions: Option to auto-start Azurite with run configurations (<a href="https://github.com/JetBrains/azure-tools-for-intellij/issues/605">#605</a>)</li>
<li>Azure Functions: Option to auto-start Azurite with run configurations (<a href="https://github.com/JetBrains/azure-tools-for-intellij/issues/674">#674</a>)</li>
<li>Azure Functions: show a warning when Azurite is not running (<a href="https://github.com/JetBrains/azure-tools-for-intellij/issues/605">#650</a>)</li>
<li>Azure Functions: Support Windows arm64 Azure Core Tools (<a href="https://youtrack.jetbrains.com/issue/RIDER-85548">#RIDER-85548</a>)</li>
</ul>
Expand All @@ -40,7 +40,7 @@
<ul>
<li>Compatibility with Rider 2022.3</li>
<li>Azure Functions: Add F# item templates for isolated worker (<a href="https://github.com/JetBrains/azure-tools-for-intellij/issues/635">#635</a>)</li>
<li>Azure Functions: Option to auto-start Azurite with run configurations (<a href="https://github.com/JetBrains/azure-tools-for-intellij/issues/605">#605</a>)</li>
<li>Azure Functions: Option to auto-start Azurite with run configurations (<a href="https://github.com/JetBrains/azure-tools-for-intellij/issues/674">#674</a>)</li>
<li>Azure Functions: show a warning when Azurite is not running (<a href="https://github.com/JetBrains/azure-tools-for-intellij/issues/605">#650</a>)</li>
</ul>
<h4>Fixed bugs:</h4>
Expand Down Expand Up @@ -344,6 +344,7 @@
implementationClass="org.jetbrains.plugins.azure.functions.completion.csharp.TimerTriggerCompletionContributor" />

<serviceViewContributor implementation="org.jetbrains.plugins.azure.storage.azurite.AzuriteServiceViewContributor" />
<stepsBeforeRunProvider implementation="org.jetbrains.plugins.azure.storage.azurite.AzuriteBeforeRunTaskProvider"/>

<editorNotificationProvider implementation="org.jetbrains.plugins.azure.identity.ad.notifications.DefaultAzureAdApplicationRegistrationNotificationProvider"/>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,6 @@ settings.app_services.function_app.core_tools.download_path.invalid=Not a valid
settings.app_services.function_app.core_tools.download_path_description=Azure Functions Core Tools download path
settings.app_services.function_app.core_tools.description=Configure the Azure Functions Core Tools to be used for an Azure Functions version.

settings.app_services.function_app.azurite.group=Azurite (Storage Emulator)
settings.app_services.function_app.azurite.group.description=An Azure storage emulator required when running Azure Functions on your local machine.
settings.app_services.function_app.azurite.automaticstart=Automatically start Azurite on run/debug Azure Functions

settings.managedidentity.name=Service Authentication
settings.managedidentity.description=When logged in with Azure CLI, your apps can use your developer credentials\nto authenticate and access Azure resources when debugging.
settings.managedidentity.info.title=How does Azure service authentication work?
Expand Down Expand Up @@ -467,6 +463,8 @@ action.azurite.show_settings.name=Azurite Settings
action.azurite.show_settings.description=Show Azurite Settings
action.azurite.reattach.workspace=Workspace: {0}
action.azurite.reattach.finished=Attached to Azurite process.
run_config.azurite.before_run_tasks.run_azurite_name=Start Azurite
run_config.azurite.before_run_tasks.run_azurite_description=Start Azurite Storage Emulator

action.identity.ad.register_app.name=Register Azure AD application
action.identity.ad.register_app.description=Register application in Azure Active Directory
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,6 @@ object AzureRiderSettings {
@Deprecated("To be removed with 2022.3")
const val PROPERTY_FUNCTIONS_MIGRATE_CORETOOLS_PATH_NOTIFICATION = "AzureFunctionsCoreToolsPath_Migration_Notify"

const val PROPERTY_FUNCTIONS_AZURITE_AUTOSTART = "AzureFunctionsAzuriteAutoStart"

data class AzureCoreToolsPathEntry(var functionsVersion: String, var coreToolsPath: String) {

fun toStringEntry() = "$functionsVersion|$coreToolsPath"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -236,20 +236,6 @@ class AzureFunctionsConfigurationPanel(parentDisposable: Disposable)
}
}

group(message("settings.app_services.function_app.azurite.group")) {
row {
text(message("settings.app_services.function_app.azurite.group.description"))
}

row {
var value = properties.getBoolean(AzureRiderSettings.PROPERTY_FUNCTIONS_AZURITE_AUTOSTART, true)

checkBox(message("settings.app_services.function_app.azurite.automaticstart"))
.bindSelected(MutableProperty({ value }, { value = it }))
.onApply { properties.setValue(AzureRiderSettings.PROPERTY_FUNCTIONS_AZURITE_AUTOSTART, value) }
}
}

row {
placeholder()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,7 @@ class BuildFunctionsProjectBeforeRunTaskProvider : BeforeRunTaskProvider<BuildFu

override fun getDescription(task: BuildFunctionsProjectBeforeRunTask): String = message("run_config.run_function_app.form.function_app.before_run_tasks.build_function_project_description")

override fun isConfigurable(): Boolean {
return false
}
override fun isConfigurable() = false

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ import org.jetbrains.plugins.azure.functions.coreTools.FunctionsCoreToolsInfoPro
import org.jetbrains.plugins.azure.functions.coreTools.FunctionsCoreToolsMsBuild
import org.jetbrains.plugins.azure.functions.run.localsettings.FunctionLocalSettingsUtil
import org.jetbrains.plugins.azure.functions.run.localsettings.FunctionsWorkerRuntime
import org.jetbrains.plugins.azure.storage.azurite.Azurite
import java.io.File

class AzureFunctionsHostExecutorFactory(
Expand All @@ -47,9 +46,6 @@ class AzureFunctionsHostExecutorFactory(

override fun create(executorId: String, environment: ExecutionEnvironment): RunProfileState {

// Auto-start Azurite
Azurite.autoStartAzurite(parameters.project)

// Determine project kind
val projectKind = parameters.projectKind
logger.info("Project kind is $projectKind")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,17 +23,10 @@
package org.jetbrains.plugins.azure.storage.azurite

import com.intellij.ide.actions.ShowSettingsUtilImpl
import com.intellij.ide.util.PropertiesComponent
import com.intellij.javascript.nodejs.util.NodePackageDescriptor
import com.intellij.openapi.actionSystem.ActionPlaces
import com.intellij.openapi.actionSystem.ex.ActionUtil
import com.intellij.openapi.actionSystem.impl.SimpleDataContext
import com.intellij.openapi.project.Project
import com.intellij.util.application
import com.microsoft.intellij.AzureConfigurable
import com.microsoft.intellij.configuration.AzureRiderSettings
import org.jetbrains.plugins.azure.RiderAzureBundle
import org.jetbrains.plugins.azure.storage.azurite.actions.StartAzuriteAction

object Azurite {
const val PackageName = "azurite"
Expand All @@ -46,16 +39,4 @@ object Azurite {
// TODO: FIX_LOCALIZATION: Using displayName parameter here for Settings ID need to be fixed to use ID to avoid localization issues.
ShowSettingsUtilImpl.showSettingsDialog(project, AzureConfigurable.AZURE_CONFIGURABLE_PREFIX + RiderAzureBundle.message("settings.azurite.name"), "")
}

fun autoStartAzurite(project: Project) {
val properties = PropertiesComponent.getInstance()
if (properties.getBoolean(AzureRiderSettings.PROPERTY_FUNCTIONS_AZURITE_AUTOSTART, true)) {
ActionUtil.invokeAction(
StartAzuriteAction(),
SimpleDataContext.getProjectContext(project),
ActionPlaces.INTENTION_MENU,
null,
null)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/**
* Copyright (c) 2023 JetBrains s.r.o.
*
* All rights reserved.
*
* MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
* documentation files (the "Software"), to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and
* to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of
* the Software.
*
* THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
* THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/

package org.jetbrains.plugins.azure.storage.azurite

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.openapi.actionSystem.ActionPlaces
import com.intellij.openapi.actionSystem.DataContext
import com.intellij.openapi.actionSystem.ex.ActionUtil
import com.intellij.openapi.actionSystem.impl.SimpleDataContext
import com.intellij.openapi.util.Key
import icons.CommonIcons
import org.jetbrains.plugins.azure.RiderAzureBundle
import org.jetbrains.plugins.azure.functions.run.AzureFunctionsHostConfiguration
import org.jetbrains.plugins.azure.storage.azurite.actions.StartAzuriteAction
import javax.swing.Icon

class AzuriteBeforeRunTask : BeforeRunTask<AzuriteBeforeRunTask>(AzuriteBeforeRunTaskProvider.providerId)

class AzuriteBeforeRunTaskProvider : BeforeRunTaskProvider<AzuriteBeforeRunTask>() {
companion object {
val providerId = Key.create<AzuriteBeforeRunTask>("RunAzuriteTask")
}

override fun getId(): Key<AzuriteBeforeRunTask>? = providerId

override fun getName(): String? = RiderAzureBundle.message("run_config.azurite.before_run_tasks.run_azurite_name")


override fun isConfigurable() = false

override fun getIcon(): Icon = CommonIcons.Azurite

private fun shouldCreateBuildBeforeRunTaskByDefault(runConfiguration: RunConfiguration): Boolean {
return runConfiguration is AzureFunctionsHostConfiguration
}

override fun createTask(runConfiguration: RunConfiguration): AzuriteBeforeRunTask? {
if (!shouldCreateBuildBeforeRunTaskByDefault(runConfiguration)) return null
val task = AzuriteBeforeRunTask()
task.isEnabled = true
return task
}

override fun executeTask(context: DataContext,
configuration: RunConfiguration,
env: ExecutionEnvironment,
task: AzuriteBeforeRunTask): Boolean {

val project = configuration.project

ActionUtil.invokeAction(
StartAzuriteAction(),
SimpleDataContext.getProjectContext(project),
ActionPlaces.INTENTION_MENU,
null,
null)

return true
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2020-2022 JetBrains s.r.o.
* Copyright (c) 2020-2023 JetBrains s.r.o.
*
* All rights reserved.
*
Expand Down Expand Up @@ -37,6 +37,7 @@ import org.jetbrains.plugins.azure.functions.buildTasks.BuildFunctionsProjectBef
import org.jetbrains.plugins.azure.functions.daemon.AzureRunnableProjectKinds
import org.jetbrains.plugins.azure.functions.run.AzureFunctionsHostConfiguration
import org.jetbrains.plugins.azure.functions.run.AzureFunctionsHostConfigurationType
import org.jetbrains.plugins.azure.storage.azurite.AzuriteBeforeRunTaskProvider
import org.testng.annotations.Test
import kotlin.test.fail

Expand Down Expand Up @@ -70,21 +71,36 @@ abstract class FunctionHostConfigurationTestBase(
//region Before Run Tasks

@Test(description = "Verifies Function Host run configuration has Build Function App before run task by default.")
fun testFunctionHost_BeforeRunTasks_Defaults() {
fun testFunctionHost_BeforeRunTasks_Defaults_BuildFunctionsProject() {
val configuration = createRunConfiguration(
name = "Run Function App: ${project.name}",
configurationType = AzureFunctionsHostConfigurationType::class.java
) as AzureFunctionsHostConfiguration

configuration.name.shouldBe("Run Function App: $projectName")

configuration.beforeRunTasks.size.shouldBe(1)
configuration.beforeRunTasks[0].providerId.shouldBe(BuildFunctionsProjectBeforeRunTaskProvider.providerId)
configuration.beforeRunTasks.shouldContains { beforeRunTask ->
beforeRunTask.providerId == BuildFunctionsProjectBeforeRunTaskProvider.providerId
}
}

@Test(description = "Verifies Function Host run configuration has Start Azurite before run task by default.")
fun testFunctionHost_BeforeRunTasks_Defaults_AzuriteBeforeRunTask() {
val configuration = createRunConfiguration(
name = "Run Function App: ${project.name}",
configurationType = AzureFunctionsHostConfigurationType::class.java
) as AzureFunctionsHostConfiguration

configuration.name.shouldBe("Run Function App: $projectName")

configuration.beforeRunTasks.shouldContains { beforeRunTask ->
beforeRunTask.providerId == AzuriteBeforeRunTaskProvider.providerId
}
}

// TODO: Need to make sure function core tools is available on a server before enable.
@Test(enabled = false, description = "Check before run task that should build a function app project.")
fun testFunctionHost_BeforeRunTasks_BuildFunctionProject() {
@Test(enabled = false, description = "Check before run task that should build a function app project and start Azurite.")
fun testFunctionHost_BeforeRunTasks_BuildFunctionProject_StartAzurite() {
addProject(arrayOf(projectName), "ConsoleApp1", ProjectTemplateIds.core(coreVersion).consoleApplication)

val configuration = createRunConfiguration(
Expand All @@ -102,8 +118,13 @@ abstract class FunctionHostConfigurationTestBase(
configuration.parameters.exePath = projectToRun.projectOutputs.first().exePath
configuration.parameters.projectTfm = projectToRun.projectOutputs.first().tfm?.presentableName ?: ""

configuration.beforeRunTasks.size.shouldBe(1)
configuration.beforeRunTasks[0].providerId.shouldBe(BuildFunctionsProjectBeforeRunTaskProvider.providerId)
configuration.beforeRunTasks.shouldContains { beforeRunTask ->
beforeRunTask.providerId == BuildFunctionsProjectBeforeRunTaskProvider.providerId
}

configuration.beforeRunTasks.shouldContains { beforeRunTask ->
beforeRunTask.providerId == AzuriteBeforeRunTaskProvider.providerId
}

checkCanExecuteRunConfiguration(configuration)

Expand Down

0 comments on commit 2be850f

Please sign in to comment.