Skip to content

Commit

Permalink
Remove the BaseState system from run configs
Browse files Browse the repository at this point in the history
* It kept having aliasing issues related to editing a cloned run config
* It kept us from 192 due to NoSuchMethod error
  • Loading branch information
Austin Brooks committed Jun 28, 2019
1 parent e59f93b commit 9891f66
Show file tree
Hide file tree
Showing 9 changed files with 138 additions and 83 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"type" : "bugfix",
"description" : "Fix a cloned run config's changing altering the original as well"
}
Original file line number Diff line number Diff line change
Expand Up @@ -629,6 +629,24 @@ class LocalLambdaRunConfigurationTest {
}
}

@Test // https://github.com/aws/aws-toolkit-jetbrains/issues/1072
fun creatingACopyDoesNotAliasFields() {
runInEdtAndWait {
val runConfiguration = createHandlerBasedRunConfiguration(
project = projectRule.project,
credentialsProviderId = mockId,
input = "{}"
)

val clonedConfiguration= runConfiguration.clone() as LocalLambdaRunConfiguration
clonedConfiguration.name = "Cloned"

clonedConfiguration.useInputText("Changed input")

assertThat(clonedConfiguration.inputSource()).isNotEqualTo(runConfiguration.inputSource())
}
}

private fun getState(runConfiguration: LocalLambdaRunConfiguration): SamRunningState {
val executor = ExecutorRegistry.getInstance().getExecutorById(DefaultRunExecutor.EXECUTOR_ID)
val environmentMock = mock<ExecutionEnvironment> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,17 @@ package software.aws.toolkits.jetbrains.services.lambda.execution

import com.intellij.execution.configurations.ConfigurationFactory
import com.intellij.execution.configurations.LocatableConfigurationBase
import com.intellij.execution.configurations.LocatableRunConfigurationOptions
import com.intellij.execution.configurations.RunConfiguration
import com.intellij.execution.configurations.RunConfigurationWithSuppressedDefaultDebugAction
import com.intellij.execution.configurations.RuntimeConfigurationError
import com.intellij.execution.runners.RunConfigurationWithSuppressedDefaultRunAction
import com.intellij.openapi.components.BaseState
import com.intellij.openapi.fileEditor.FileDocumentManager
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.io.FileUtil
import com.intellij.openapi.vfs.LocalFileSystem
import com.intellij.util.xmlb.XmlSerializer
import com.intellij.util.xmlb.annotations.Property
import org.jdom.Element
import software.aws.toolkits.core.credentials.CredentialProviderNotFound
import software.aws.toolkits.jetbrains.core.credentials.CredentialManager
import software.aws.toolkits.jetbrains.core.region.AwsRegionProvider
Expand All @@ -23,29 +24,51 @@ import java.nio.charset.StandardCharsets

abstract class LambdaRunConfigurationBase<T : BaseLambdaOptions>(
project: Project,
factory: ConfigurationFactory,
private val configFactory: ConfigurationFactory,
id: String
) : LocatableConfigurationBase<T>(project, factory, id),
) : LocatableConfigurationBase<T>(project, configFactory, id),
RunConfigurationWithSuppressedDefaultRunAction,
RunConfigurationWithSuppressedDefaultDebugAction {

override fun getOptions() = super.getOptions() as BaseLambdaOptions
protected abstract val state: BaseLambdaOptions

final override fun readExternal(element: Element) {
super.readExternal(element)
XmlSerializer.deserializeInto(state, element)
}

final override fun writeExternal(element: Element) {
super.writeExternal(element)
XmlSerializer.serializeInto(state, element)
}

@Suppress("UNCHECKED_CAST")
final override fun clone(): RunConfiguration {
val element = Element("toClone")
writeExternal(element)

val copy = configFactory.createTemplateConfiguration(project) as LambdaRunConfigurationBase<*>
copy.name = name
copy.readExternal(element)

return copy
}

fun useInputFile(inputFile: String?) {
val inputOptions = options.inputOptions
val inputOptions = state.inputOptions
inputOptions.inputIsFile = true
inputOptions.input = inputFile
}

fun useInputText(input: String?) {
val inputOptions = options.inputOptions
val inputOptions = state.inputOptions
inputOptions.inputIsFile = false
inputOptions.input = input
}

fun isUsingInputFile() = options.inputOptions.inputIsFile
fun isUsingInputFile() = state.inputOptions.inputIsFile

fun inputSource() = options.inputOptions.input
fun inputSource() = state.inputOptions.input

protected fun checkInput() {
inputSource()?.let {
Expand Down Expand Up @@ -77,10 +100,10 @@ abstract class LambdaRunConfigurationBase<T : BaseLambdaOptions>(
}
} ?: throw RuntimeConfigurationError(message("lambda.run_configuration.no_input_specified"))

fun credentialProviderId() = options.accountOptions.credentialProviderId
fun credentialProviderId() = state.accountOptions.credentialProviderId

fun credentialProviderId(credentialsProviderId: String?) {
options.accountOptions.credentialProviderId = credentialsProviderId
state.accountOptions.credentialProviderId = credentialsProviderId
}

protected fun resolveCredentials() = credentialProviderId()?.let {
Expand All @@ -98,30 +121,30 @@ abstract class LambdaRunConfigurationBase<T : BaseLambdaOptions>(
}
} ?: throw RuntimeConfigurationError(message("lambda.run_configuration.no_credentials_specified"))

fun regionId() = options.accountOptions.regionId
fun regionId() = state.accountOptions.regionId

fun regionId(regionId: String?) {
options.accountOptions.regionId = regionId
state.accountOptions.regionId = regionId
}

protected fun resolveRegion() = regionId()?.let {
AwsRegionProvider.getInstance().regions()[it]
} ?: throw RuntimeConfigurationError(message("lambda.run_configuration.no_region_specified"))
}

open class BaseLambdaOptions : LocatableRunConfigurationOptions() {
open class BaseLambdaOptions {
@get:Property(flat = true) // flat for backwards compat
var accountOptions by property(AccountOptions())
var accountOptions = AccountOptions()
@get:Property(flat = true) // flat for backwards compat
var inputOptions by property(InputOptions())
var inputOptions = InputOptions()
}

class AccountOptions : BaseState() {
var credentialProviderId by property("")
var regionId by property("")
}
data class AccountOptions(
var credentialProviderId: String? = null,
var regionId: String? = null
)

class InputOptions : BaseState() {
var inputIsFile by property(false)
var input by string()
class InputOptions {
var inputIsFile = false
var input: String? = null
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,27 @@

package software.aws.toolkits.jetbrains.services.lambda.execution.local

import com.intellij.openapi.components.BaseState
import com.intellij.util.xmlb.annotations.OptionTag
import com.intellij.util.xmlb.annotations.Property
import com.intellij.util.xmlb.annotations.Tag
import software.aws.toolkits.jetbrains.services.lambda.execution.BaseLambdaOptions
import software.aws.toolkits.jetbrains.services.lambda.sam.SamOptions
import java.util.LinkedHashMap

@Tag("LocalLambdaOptions")
class LocalLambdaOptions : BaseLambdaOptions() {
@get:Property(flat = true) // flat for backwards compat
var functionOptions by property(FunctionOptions())
var functionOptions = FunctionOptions()
@get:Property(surroundWithTag = false)
var samOptions by property(SamOptions())
var samOptions = SamOptions()
}

class FunctionOptions : BaseState() {
var useTemplate by property(false)
var templateFile by string()
@Tag("FunctionOptions")
class FunctionOptions {
var useTemplate = false
var templateFile: String? = null
@get:OptionTag("logicalFunctionName")
var logicalId by string()
var runtime by string()
var handler by string()
var environmentVariables by map(LinkedHashMap<String, String>())
var logicalId: String? = null
var runtime: String? = null
var handler: String? = null
var environmentVariables: Map<String, String> = linkedMapOf<String, String>()
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import com.intellij.openapi.options.SettingsEditor
import com.intellij.openapi.options.SettingsEditorGroup
import com.intellij.openapi.options.ShowSettingsUtil
import com.intellij.openapi.project.Project
import com.intellij.openapi.vfs.LocalFileSystem
import com.intellij.openapi.vfs.VfsUtil
import com.intellij.psi.NavigatablePsiElement
import com.intellij.psi.PsiElement
import com.intellij.psi.util.PsiTreeUtil
Expand All @@ -35,21 +37,18 @@ import software.aws.toolkits.jetbrains.services.lambda.sam.SamTemplateUtils.find
import software.aws.toolkits.jetbrains.services.lambda.validOrNull
import software.aws.toolkits.jetbrains.settings.AwsSettingsConfigurable
import software.aws.toolkits.resources.message
import java.io.File
import java.nio.file.Path

class LocalLambdaRunConfigurationFactory(configuration: LambdaRunConfiguration) : ConfigurationFactory(configuration) {
override fun createTemplateConfiguration(project: Project) = LocalLambdaRunConfiguration(project, this)

override fun getName(): String = "Local"

override fun getOptionsClass() = LocalLambdaOptions::class.java
}

class LocalLambdaRunConfiguration(project: Project, factory: ConfigurationFactory) :
LambdaRunConfigurationBase<LocalLambdaOptions>(project, factory, "SAM CLI"),
RefactoringListenerProvider {
override fun getOptions() = super.getOptions() as LocalLambdaOptions
override val state = LocalLambdaOptions()

override fun getConfigurationEditor(): SettingsEditor<LocalLambdaRunConfiguration> {
val group = SettingsEditorGroup<LocalLambdaRunConfiguration>()
Expand Down Expand Up @@ -79,9 +78,6 @@ class LocalLambdaRunConfiguration(project: Project, factory: ConfigurationFactor
val psiElement = handlerPsiElement(handler, runtime)
?: throw RuntimeConfigurationError(message("lambda.run_configuration.handler_not_found", handler))

val samOptions = SamOptions()
samOptions.copyFrom(options.samOptions)

val samRunSettings = LocalLambdaSettings(
runtime,
handler,
Expand All @@ -91,7 +87,7 @@ class LocalLambdaRunConfiguration(project: Project, factory: ConfigurationFactor
resolveRegion(),
psiElement,
templateDetails,
samOptions
state.samOptions.copy()
)

return SamRunningState(environment, samRunSettings)
Expand All @@ -110,16 +106,16 @@ class LocalLambdaRunConfiguration(project: Project, factory: ConfigurationFactor

if (PsiTreeUtil.isAncestor(element, handlerPsi, false)) {
return object : RefactoringElementAdapter() {
private val originalHandler = options.functionOptions.handler
private val originalHandler = state.functionOptions.handler

override fun elementRenamedOrMoved(newElement: PsiElement) {
handlerResolver.determineHandler(handlerPsi)?.let { newHandler ->
options.functionOptions.handler = newHandler
state.functionOptions.handler = newHandler
}
}

override fun undoElementMovedOrRenamed(newElement: PsiElement, oldQualifiedName: String) {
options.functionOptions.handler = originalHandler
state.functionOptions.handler = originalHandler
}
}
}
Expand All @@ -128,7 +124,7 @@ class LocalLambdaRunConfiguration(project: Project, factory: ConfigurationFactor
}

fun useTemplate(templateLocation: String?, logicalId: String?) {
val functionOptions = options.functionOptions
val functionOptions = state.functionOptions
functionOptions.useTemplate = true

functionOptions.templateFile = templateLocation
Expand All @@ -139,7 +135,7 @@ class LocalLambdaRunConfiguration(project: Project, factory: ConfigurationFactor
}

fun useHandler(runtime: Runtime?, handler: String?) {
val functionOptions = options.functionOptions
val functionOptions = state.functionOptions
functionOptions.useTemplate = false

functionOptions.templateFile = null
Expand All @@ -149,63 +145,63 @@ class LocalLambdaRunConfiguration(project: Project, factory: ConfigurationFactor
functionOptions.runtime = runtime.toString()
}

fun isUsingTemplate() = options.functionOptions.useTemplate
fun isUsingTemplate() = state.functionOptions.useTemplate

fun templateFile() = options.functionOptions.templateFile
fun templateFile() = state.functionOptions.templateFile

fun logicalId() = options.functionOptions.logicalId
fun logicalId() = state.functionOptions.logicalId

fun handler() = options.functionOptions.handler
fun handler() = state.functionOptions.handler

fun runtime(): Runtime? = Runtime.fromValue(options.functionOptions.runtime)?.validOrNull
fun runtime(): Runtime? = Runtime.fromValue(state.functionOptions.runtime)?.validOrNull

fun environmentVariables() = options.functionOptions.environmentVariables
fun environmentVariables() = state.functionOptions.environmentVariables

fun environmentVariables(envVars: Map<String, String>) {
options.functionOptions.environmentVariables = envVars.toMutableMap()
state.functionOptions.environmentVariables = envVars
}

fun dockerNetwork(): String? = options.samOptions.dockerNetwork
fun dockerNetwork(): String? = state.samOptions.dockerNetwork

fun dockerNetwork(network: String?) {
options.samOptions.dockerNetwork = network
state.samOptions.dockerNetwork = network
}

fun skipPullImage(): Boolean = options.samOptions.skipImagePull
fun skipPullImage(): Boolean = state.samOptions.skipImagePull

fun skipPullImage(skip: Boolean) {
options.samOptions.skipImagePull = skip
state.samOptions.skipImagePull = skip
}

fun buildInContainer(): Boolean = options.samOptions.buildInContainer
fun buildInContainer(): Boolean = state.samOptions.buildInContainer

fun buildInContainer(useContainer: Boolean) {
options.samOptions.buildInContainer = useContainer
state.samOptions.buildInContainer = useContainer
}

override fun suggestedName(): String? {
val subName = options.functionOptions.logicalId ?: handlerDisplayName()
val subName = state.functionOptions.logicalId ?: handlerDisplayName()
return "[${message("lambda.run_configuration.local")}] $subName"
}

private fun handlerDisplayName(): String? {
val handler = options.functionOptions.handler ?: return null
val handler = state.functionOptions.handler ?: return null
return runtime()
?.runtimeGroup
?.let { LambdaHandlerResolver.getInstance(it) }
?.handlerDisplayName(handler) ?: handler
}

private fun resolveLambdaInfo() = if (isUsingTemplate()) {
val template = templateFile()?.takeUnless { it.isEmpty() }
val templateFile = templateFile()
?.takeUnless { it.isEmpty() }
?.let { LocalFileSystem.getInstance().findFileByPath(it) }
?: throw RuntimeConfigurationError(message("lambda.run_configuration.sam.no_template_specified"))

val functionName = logicalId() ?: throw RuntimeConfigurationError(
message("lambda.run_configuration.sam.no_function_specified")
)

val templateFile = File(template)

val function = findFunctionsFromTemplate(
project,
templateFile
Expand All @@ -214,7 +210,7 @@ class LocalLambdaRunConfiguration(project: Project, factory: ConfigurationFactor
message(
"lambda.run_configuration.sam.no_such_function",
functionName,
template
templateFile.path
)
)

Expand All @@ -223,7 +219,7 @@ class LocalLambdaRunConfiguration(project: Project, factory: ConfigurationFactor
val runtime = tryOrNull { Runtime.fromValue(function.runtime()).validOrNull }
?: throw RuntimeConfigurationError(message("lambda.run_configuration.no_runtime_specified"))

Triple(handler, runtime, SamTemplateDetails(templateFile.toPath(), functionName))
Triple(handler, runtime, SamTemplateDetails(VfsUtil.virtualToIoFile(templateFile).toPath(), functionName))
} else {
val handler = handler()
?: throw RuntimeConfigurationError(message("lambda.run_configuration.no_handler_specified"))
Expand Down
Loading

0 comments on commit 9891f66

Please sign in to comment.