diff --git a/jetbrains-core/resources/META-INF/plugin.xml b/jetbrains-core/resources/META-INF/plugin.xml index 3ababf8d7c..3698fbd3ef 100644 --- a/jetbrains-core/resources/META-INF/plugin.xml +++ b/jetbrains-core/resources/META-INF/plugin.xml @@ -553,10 +553,6 @@ with what features/services are supported. class="software.aws.toolkits.jetbrains.services.lambda.actions.DeleteFunctionAction"/> - - - - @@ -671,7 +667,6 @@ with what features/services are supported. - diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/core/tools/ToolConfigurable.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/core/tools/ToolConfigurable.kt index 5539e3451f..2299f72c56 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/core/tools/ToolConfigurable.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/core/tools/ToolConfigurable.kt @@ -11,7 +11,6 @@ import com.intellij.ui.components.JBTextField import com.intellij.ui.dsl.builder.Align import com.intellij.ui.dsl.builder.bindText import com.intellij.ui.dsl.builder.panel -import com.intellij.ui.layout.applyToComponent import software.aws.toolkits.resources.message import java.nio.file.Path diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/apprunner/ui/CreationPanel.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/apprunner/ui/CreationPanel.kt index 7ee26bda43..f678c96e4a 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/apprunner/ui/CreationPanel.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/apprunner/ui/CreationPanel.kt @@ -29,11 +29,9 @@ import software.aws.toolkits.jetbrains.services.iam.IamResources import software.aws.toolkits.jetbrains.services.iam.IamRole import software.aws.toolkits.jetbrains.ui.KeyValueTextField import software.aws.toolkits.jetbrains.ui.ResourceSelector -import software.aws.toolkits.jetbrains.ui.intTextField import software.aws.toolkits.jetbrains.utils.toHumanReadable import software.aws.toolkits.jetbrains.utils.ui.installOnParent import software.aws.toolkits.jetbrains.utils.ui.selected -import software.aws.toolkits.jetbrains.utils.ui.visibleIf import software.aws.toolkits.resources.message import javax.swing.DefaultComboBoxModel diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/ecr/actions/PushToRepositoryAction.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/ecr/actions/PushToRepositoryAction.kt index f53d8a9703..29dd28e653 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/ecr/actions/PushToRepositoryAction.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/ecr/actions/PushToRepositoryAction.kt @@ -33,7 +33,6 @@ import com.intellij.ui.dsl.builder.columns import com.intellij.ui.dsl.builder.panel import com.intellij.ui.dsl.builder.toMutableProperty import com.intellij.ui.dsl.builder.toNullableProperty -import com.intellij.ui.layout.applyToComponent import com.intellij.ui.layout.listCellRenderer import com.intellij.ui.layout.selected import com.intellij.util.text.nullize diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/ecs/exec/OpenShellInContainerDialog.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/ecs/exec/OpenShellInContainerDialog.kt index 05c3adc7bc..af2fe8f472 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/ecs/exec/OpenShellInContainerDialog.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/ecs/exec/OpenShellInContainerDialog.kt @@ -14,7 +14,6 @@ import com.intellij.ui.dsl.builder.bindItem import com.intellij.ui.dsl.builder.columns import com.intellij.ui.dsl.builder.panel import com.intellij.ui.dsl.builder.toNullableProperty -import com.intellij.ui.layout.applyToComponent import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import org.jetbrains.plugins.terminal.TerminalTabState diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/ecs/exec/RunCommandDialog.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/ecs/exec/RunCommandDialog.kt index 2233214aa1..366a9e0de0 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/ecs/exec/RunCommandDialog.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/ecs/exec/RunCommandDialog.kt @@ -14,7 +14,6 @@ import com.intellij.ui.dsl.builder.bindItem import com.intellij.ui.dsl.builder.columns import com.intellij.ui.dsl.builder.panel import com.intellij.ui.dsl.builder.toNullableProperty -import com.intellij.ui.layout.applyToComponent import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import software.aws.toolkits.core.ConnectionSettings diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/actions/DeployServerlessApplicationAction.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/actions/DeployServerlessApplicationAction.kt deleted file mode 100644 index f3c284a76b..0000000000 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/actions/DeployServerlessApplicationAction.kt +++ /dev/null @@ -1,202 +0,0 @@ -// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -package software.aws.toolkits.jetbrains.services.lambda.actions - -import com.intellij.openapi.actionSystem.AnAction -import com.intellij.openapi.actionSystem.AnActionEvent -import com.intellij.openapi.actionSystem.PlatformDataKeys -import com.intellij.openapi.application.ApplicationManager -import com.intellij.openapi.application.runInEdt -import com.intellij.openapi.fileEditor.FileDocumentManager -import com.intellij.openapi.module.ModuleUtil -import com.intellij.openapi.project.Project -import com.intellij.openapi.vfs.VirtualFile -import icons.AwsIcons -import kotlinx.coroutines.runBlocking -import kotlinx.coroutines.withContext -import software.amazon.awssdk.services.cloudformation.CloudFormationClient -import software.aws.toolkits.jetbrains.core.awsClient -import software.aws.toolkits.jetbrains.core.coroutines.getCoroutineUiContext -import software.aws.toolkits.jetbrains.core.credentials.AwsConnectionManager -import software.aws.toolkits.jetbrains.core.executables.ExecutableInstance -import software.aws.toolkits.jetbrains.core.executables.ExecutableManager -import software.aws.toolkits.jetbrains.core.executables.getExecutable -import software.aws.toolkits.jetbrains.core.explorer.refreshAwsTree -import software.aws.toolkits.jetbrains.services.cloudformation.describeStack -import software.aws.toolkits.jetbrains.services.cloudformation.executeChangeSetAndWait -import software.aws.toolkits.jetbrains.services.cloudformation.stack.StackWindowManager -import software.aws.toolkits.jetbrains.services.lambda.deploy.DeployServerlessApplicationDialog -import software.aws.toolkits.jetbrains.services.lambda.deploy.DeployServerlessApplicationSettings -import software.aws.toolkits.jetbrains.services.lambda.sam.SamCommon -import software.aws.toolkits.jetbrains.services.lambda.sam.SamExecutable -import software.aws.toolkits.jetbrains.services.lambda.sam.SamTemplateFileUtils -import software.aws.toolkits.jetbrains.services.lambda.sam.SamTemplateFileUtils.validateTemplateFile -import software.aws.toolkits.jetbrains.services.lambda.steps.DeployLambda -import software.aws.toolkits.jetbrains.services.lambda.steps.createDeployWorkflow -import software.aws.toolkits.jetbrains.services.lambda.upload.UploadFunctionContinueDialog -import software.aws.toolkits.jetbrains.settings.DeploySettings -import software.aws.toolkits.jetbrains.settings.relativeSamPath -import software.aws.toolkits.jetbrains.utils.execution.steps.BuildViewWorkflowEmitter -import software.aws.toolkits.jetbrains.utils.execution.steps.StepExecutor -import software.aws.toolkits.jetbrains.utils.notifyError -import software.aws.toolkits.jetbrains.utils.notifyInfo -import software.aws.toolkits.jetbrains.utils.notifyNoActiveCredentialsError -import software.aws.toolkits.jetbrains.utils.notifySamCliNotValidError -import software.aws.toolkits.resources.message -import software.aws.toolkits.telemetry.Result -import software.aws.toolkits.telemetry.SamTelemetry - -class DeployServerlessApplicationAction : AnAction( - message("serverless.application.deploy"), - null, - AwsIcons.Resources.SERVERLESS_APP -) { - private val edtContext = getCoroutineUiContext() - - override fun actionPerformed(e: AnActionEvent) { - val project = e.getRequiredData(PlatformDataKeys.PROJECT) - - if (!AwsConnectionManager.getInstance(project).isValidConnectionSettings()) { - notifyNoActiveCredentialsError(project = project) - return - } - - ExecutableManager.getInstance().getExecutable().thenAccept { samExecutable -> - if (samExecutable is ExecutableInstance.InvalidExecutable || samExecutable is ExecutableInstance.UnresolvedExecutable) { - notifySamCliNotValidError( - project = project, - content = (samExecutable as ExecutableInstance.BadExecutable).validationError - ) - return@thenAccept - } - - val templateFile = SamTemplateFileUtils.retrieveSamTemplate(e, project) ?: return@thenAccept - - validateTemplateFile(project, templateFile)?.let { - notifyError(content = it, project = project) - return@thenAccept - } - - runInEdt { - // Force save before we deploy - FileDocumentManager.getInstance().saveAllDocuments() - - val stackDialog = DeployServerlessApplicationDialog(project, templateFile) - if (!stackDialog.showAndGet()) { - SamTelemetry.deploy( - project = project, - version = SamCommon.getVersionString(), - result = Result.Cancelled - ) - return@runInEdt - } - - val settings = stackDialog.settings() - saveSettings(project, templateFile, settings) - - continueDeployment(project, templateFile, settings) - } - } - } - - private fun continueDeployment(project: Project, templateFile: VirtualFile, settings: DeployServerlessApplicationSettings) { - val stackName = settings.stackName - val emitter = BuildViewWorkflowEmitter.createEmitter( - project, - message("serverless.application.deploy_in_progress.title", stackName), - stackName - ) - val workflow = StepExecutor( - project, - createDeployWorkflow( - project, - templateFile, - settings - ), - emitter - ) - - workflow.onSuccess = { - runBlocking { - val changeSetArn = it.getRequiredAttribute(DeployLambda.CHANGE_SET_ARN) - - if (!settings.autoExecute) { - val response = withContext(edtContext) { UploadFunctionContinueDialog(project, changeSetArn).showAndGet() } - if (!response) { - // TODO this telemetry needs to be improved. The user can finish the deployment later so we do not know if - // it is actually cancelled or not - SamTelemetry.deploy(project = project, version = SamCommon.getVersionString(), result = Result.Cancelled) - return@runBlocking - } - } - - val cfnClient = project.awsClient() - - cfnClient.describeStack(stackName) { - it?.run { - runInEdt { - StackWindowManager.getInstance(project).openStack(stackName(), stackId()) - } - } - } - ApplicationManager.getApplication().executeOnPooledThread { - try { - cfnClient.executeChangeSetAndWait(stackName, changeSetArn) - notifyInfo( - message("cloudformation.service_name"), - message("cloudformation.execute_change_set.success", stackName), - project - ) - SamTelemetry.deploy( - project = project, - version = SamCommon.getVersionString(), - result = Result.Succeeded - ) - // Since we could update anything, do a full refresh of the resource cache and explorer - project.refreshAwsTree() - } catch (e: Exception) { - e.notifyError(message("cloudformation.execute_change_set.failed", stackName), project) - SamTelemetry.deploy( - project = project, - version = SamCommon.getVersionString(), - result = Result.Failed - ) - } - } - } - } - - workflow.onError = { - it.notifyError(project = project, title = message("cloudformation.service_name")) - SamTelemetry.deploy( - project = project, - version = SamCommon.getVersionString(), - result = Result.Failed - ) - } - - workflow.startExecution() - } - - override fun update(e: AnActionEvent) { - super.update(e) - - e.presentation.isVisible = false - } - - private fun saveSettings(project: Project, templateFile: VirtualFile, settings: DeployServerlessApplicationSettings) { - ModuleUtil.findModuleForFile(templateFile, project)?.let { module -> - relativeSamPath(module, templateFile)?.let { samPath -> - DeploySettings.getInstance(module)?.apply { - setSamStackName(samPath, settings.stackName) - setSamBucketName(samPath, settings.bucket) - setSamEcrRepoUri(samPath, settings.ecrRepo) - setSamAutoExecute(samPath, settings.autoExecute) - setSamUseContainer(samPath, settings.useContainer) - setEnabledCapabilities(samPath, settings.capabilities) - } - } - } - } -} diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/deploy/DeployServerlessApplicationDialog.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/deploy/DeployServerlessApplicationDialog.kt deleted file mode 100644 index 764fac6013..0000000000 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/deploy/DeployServerlessApplicationDialog.kt +++ /dev/null @@ -1,446 +0,0 @@ -// Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -package software.aws.toolkits.jetbrains.services.lambda.deploy - -import com.intellij.openapi.application.ModalityState -import com.intellij.openapi.application.runInEdt -import com.intellij.openapi.module.ModuleUtilCore -import com.intellij.openapi.project.Project -import com.intellij.openapi.ui.DialogPanel -import com.intellij.openapi.ui.DialogWrapper -import com.intellij.openapi.vfs.VirtualFile -import com.intellij.ui.MutableCollectionComboBoxModel -import com.intellij.ui.SimpleListCellRenderer -import com.intellij.ui.layout.applyToComponent -import com.intellij.ui.layout.panel -import com.intellij.ui.layout.selected -import com.intellij.ui.layout.toBinding -import com.intellij.util.text.nullize -import org.jetbrains.annotations.TestOnly -import software.amazon.awssdk.services.cloudformation.CloudFormationClient -import software.amazon.awssdk.services.cloudformation.model.StackSummary -import software.amazon.awssdk.services.cloudformation.model.Tag -import software.amazon.awssdk.services.ecr.EcrClient -import software.amazon.awssdk.services.lambda.model.PackageType -import software.amazon.awssdk.services.s3.S3Client -import software.aws.toolkits.jetbrains.core.awsClient -import software.aws.toolkits.jetbrains.core.help.HelpIds -import software.aws.toolkits.jetbrains.core.map -import software.aws.toolkits.jetbrains.services.cloudformation.CloudFormationTemplate -import software.aws.toolkits.jetbrains.services.cloudformation.Parameter -import software.aws.toolkits.jetbrains.services.cloudformation.SamFunction -import software.aws.toolkits.jetbrains.services.cloudformation.describeStack -import software.aws.toolkits.jetbrains.services.cloudformation.mergeRemoteParameters -import software.aws.toolkits.jetbrains.services.cloudformation.resources.CloudFormationResources -import software.aws.toolkits.jetbrains.services.ecr.CreateEcrRepoDialog -import software.aws.toolkits.jetbrains.services.ecr.resources.EcrResources -import software.aws.toolkits.jetbrains.services.ecr.resources.Repository -import software.aws.toolkits.jetbrains.services.lambda.sam.SamTemplateUtils -import software.aws.toolkits.jetbrains.services.lambda.sam.ValidateSamParameters.validateParameters -import software.aws.toolkits.jetbrains.services.lambda.sam.ValidateSamParameters.validateStackName -import software.aws.toolkits.jetbrains.services.s3.CreateS3BucketDialog -import software.aws.toolkits.jetbrains.services.s3.resources.S3Resources -import software.aws.toolkits.jetbrains.settings.DeploySettings -import software.aws.toolkits.jetbrains.settings.relativeSamPath -import software.aws.toolkits.jetbrains.ui.KeyValueTextField -import software.aws.toolkits.jetbrains.ui.ResourceSelector -import software.aws.toolkits.jetbrains.utils.ui.bindValueToProperty -import software.aws.toolkits.jetbrains.utils.ui.installOnParent -import software.aws.toolkits.jetbrains.utils.ui.toolTipText -import software.aws.toolkits.jetbrains.utils.ui.validationInfo -import software.aws.toolkits.jetbrains.utils.ui.withBinding -import software.aws.toolkits.resources.message -import java.awt.Component - -data class DeployServerlessApplicationSettings( - val stackName: String, - val bucket: String, - val ecrRepo: String?, - val autoExecute: Boolean, - val parameters: Map, - val tags: Map, - val useContainer: Boolean, - val capabilities: List -) - -class DeployServerlessApplicationDialog( - private val project: Project, - private val templateFile: VirtualFile, - private val loadResourcesOnCreate: Boolean = true -) : DialogWrapper(project) { - var useContainer: Boolean = false - var newStackName: String = "" - var requireReview: Boolean = false - var deployType: DeployType = DeployType.CREATE - var templateParameters: Map = emptyMap() - var tags: Map = emptyMap() - var showImageOptions: Boolean = false - - // non-dsl components - private val stackSelector = ResourceSelector.builder() - .resource(CloudFormationResources.ACTIVE_STACKS) - .awsConnection(project) - .customRenderer(SimpleListCellRenderer.create("", StackSummary::stackName)) - .apply { - if (!loadResourcesOnCreate) { - disableAutomaticLoading() - } - } - .build() - - private val s3BucketSelector = ResourceSelector.builder() - .resource(S3Resources.LIST_BUCKETS.map { it.name() }) - .awsConnection(project) - .apply { - if (!loadResourcesOnCreate) { - disableAutomaticLoading() - } - } - .build() - - private val ecrRepoSelector = ResourceSelector.builder() - .resource(EcrResources.LIST_REPOS) - .awsConnection(project) - .customRenderer(SimpleListCellRenderer.create("", Repository::repositoryName)) - .apply { - if (!loadResourcesOnCreate) { - disableAutomaticLoading() - } - } - .build() - - private val parametersField = KeyValueTextField() - private val tagsField = KeyValueTextField(message("tags.title")) - private val capabilitiesSelector = CapabilitiesEnumCheckBoxes() - - private var templateFileParameters = CloudFormationTemplate.parse(project, templateFile).parameters().toList() - private val module = ModuleUtilCore.findModuleForFile(templateFile, project) - private val settings: DeploySettings? = module?.let { DeploySettings.getInstance(it) } - private val samPath: String = module?.let { relativeSamPath(it, templateFile) } ?: templateFile.name - private val templateFunctions = SamTemplateUtils.findFunctionsFromTemplate(project, templateFile) - private val hasImageFunctions: Boolean = templateFunctions.any { (it as? SamFunction)?.packageType() == PackageType.IMAGE } - - private val s3Client: S3Client = project.awsClient() - private val ecrClient: EcrClient = project.awsClient() - private val cloudFormationClient: CloudFormationClient = project.awsClient() - - init { - title = message("serverless.application.deploy.title") - setOKButtonText(message("serverless.application.deploy.action.name")) - setOKButtonTooltip(message("serverless.application.deploy.action.description")) - - // populate dialog before init - showImageOptions = hasImageFunctions - settings?.samStackName(samPath)?.let { stackName -> - // If the module has been deployed once, select updateStack - deployType = DeployType.UPDATE - stackSelector.selectedItem { it.stackName() == stackName } - // async populate parameters from remote - refreshTemplateParametersAndTags(stackName) - } ?: refreshTemplateParametersAndTags() - - s3BucketSelector.selectedItem = settings?.samBucketName(samPath) - requireReview = !(settings?.samAutoExecute(samPath) ?: true) - useContainer = (settings?.samUseContainer(samPath) ?: false) - capabilitiesSelector.selected = settings?.enabledCapabilities(samPath) - ?: CreateCapabilities.values().filter { it.defaultEnabled } - - init() - } - - fun settings() = DeployServerlessApplicationSettings( - // fields should have been validated at this point - stackName = if (deployType == DeployType.CREATE) { - newStackName.nullize() - } else { - stackSelector.selected()?.stackName() - } ?: throw RuntimeException(message("serverless.application.deploy.validation.stack.missing")), - bucket = s3BucketSelector.selected() ?: throw RuntimeException("s3 bucket selected was null"), - ecrRepo = if (hasImageFunctions) { - ecrRepoSelector.selected()?.repositoryUri - } else { - null - }, - autoExecute = !requireReview, - parameters = templateParameters, - tags = tags, - useContainer = useContainer, - capabilities = capabilitiesSelector.selected - ) - - override fun getHelpId(): String = HelpIds.DEPLOY_SERVERLESS_APPLICATION_DIALOG.id - - override fun createCenterPanel() = buildPanel() - - internal fun buildPanel() = - panel { - val wideInputSizeGroup = "wideInputSizeGroup" - // create stack - buttonGroup { - row { - val createStackButton = radioButton( - message("serverless.application.deploy.label.stack.new") - ) - .bindValueToProperty(::deployType.toBinding(), DeployType.CREATE) - .toolTipText(message("serverless.application.deploy.tooltip.createStack")) - - createStackButton.selected.addListener { - if (it && deployType != DeployType.CREATE) { - deployType = DeployType.CREATE - refreshTemplateParametersAndTags() - } - } - - textField(::newStackName) - .sizeGroup(wideInputSizeGroup) - .constraints(growX) - .enableIf(createStackButton.selected) - .toolTipText(message("serverless.application.deploy.tooltip.createStack")) - .withValidationOnApply { field -> - if (!field.isEnabled) { - null - } else { - validateStackName(field.text, stackSelector)?.let { field.validationInfo(it) } - } - } - } - - // update stack - row { - val updateStackButton = radioButton( - message("serverless.application.deploy.label.stack.select"), - ) - .bindValueToProperty(::deployType.toBinding(), DeployType.UPDATE) - .toolTipText(message("serverless.application.deploy.tooltip.updateStack")) - - updateStackButton.selected.addListener { - if (it && deployType != DeployType.UPDATE) { - deployType = DeployType.UPDATE - refreshTemplateParametersAndTags() - } - } - - stackSelector() - .sizeGroup(wideInputSizeGroup) - .constraints(growX) - .enableIf(updateStackButton.selected) - .withErrorOnApplyIf(message("serverless.application.deploy.validation.stack.missing")) { - it.isEnabled && (it.isLoading || it.selected() == null) - } - .toolTipText(message("serverless.application.deploy.tooltip.updateStack")) - }.largeGapAfter() - } - - // stack parameters - row(message("serverless.application.deploy.template.parameters")) { - parametersField() - .withBinding(::templateParameters.toBinding()) - .toolTipText(message("serverless.application.deploy.tooltip.template.parameters")) - .withValidationOnApply { validateParameters(it, templateFileParameters) } - } - - // deploy tags - val tagsString = message("tags.title") - row(tagsString) { - tagsField() - .withBinding(::tags.toBinding()) - } - - // s3 bucket - row(message("serverless.application.deploy.label.bucket")) { - s3BucketSelector() - .constraints(growX) - .withErrorOnApplyIf(message("serverless.application.deploy.validation.s3.bucket.empty")) { it.isLoading || it.selected() == null } - .toolTipText(message("serverless.application.deploy.tooltip.s3Bucket")) - - button(message("serverless.application.deploy.button.bucket.create")) { - val bucketDialog = CreateS3BucketDialog( - project = project, - s3Client = s3Client, - parent = it.source as? Component - ) - - if (bucketDialog.showAndGet()) { - bucketDialog.bucketName().let { - s3BucketSelector.reload(forceFetch = true) - s3BucketSelector.selectedItem = it - } - } - } - } - - // ecr repo - val ecrSelectorPanel = panel { - row(message("serverless.application.deploy.label.repo")) { - ecrRepoSelector() - .constraints(growX) - .withErrorOnApplyIf(message("serverless.application.deploy.validation.ecr.repo.empty")) { - it.isLoading || it.selected() == null - } - .toolTipText(message("serverless.application.deploy.tooltip.ecrRepo")) - - button(message("serverless.application.deploy.button.bucket.create")) { - val ecrDialog = CreateEcrRepoDialog( - project = project, - ecrClient = ecrClient, - parent = it.source as? Component - ) - - if (ecrDialog.showAndGet()) { - ecrRepoSelector.reload(forceFetch = true) - ecrRepoSelector.selectedItem { it.repositoryName == ecrDialog.repoName } - } - } - } - } - row { - ecrSelectorPanel(grow) - .installOnParent { showImageOptions } - .applyToComponent { - isVisible = showImageOptions - } - } - - // cfn caps - row { - label(message("cloudformation.capabilities")) - .toolTipText(message("cloudformation.capabilities.toolTipText")) - - cell(isFullWidth = true) { - capabilitiesSelector.checkboxes.forEach { - it() - } - } - } - - // confirmation - row { - cell(isFullWidth = true) { - checkBox(message("serverless.application.deploy.review_required"), ::requireReview) - .toolTipText(message("serverless.application.deploy.tooltip.deploymentConfirmation")) - } - } - - // in container - row { - cell(isFullWidth = true) { - checkBox(message("serverless.application.deploy.use_container"), ::useContainer) - .toolTipText(message("lambda.sam.buildInContainer.tooltip")) - } - } - } - - private fun refreshTemplateParametersAndTags(stackName: String? = null) { - when (deployType.name) { - DeployType.CREATE.name -> { - populateParameters(templateFileParameters) - } - - DeployType.UPDATE.name -> { - val selectedStackName = stackName ?: stackSelector.selected()?.stackName() - if (selectedStackName == null) { - populateParameters(emptyList()) - } else { - cloudFormationClient.describeStack(selectedStackName) { - it?.let { - runInEdt(ModalityState.any()) { - // This check is here in-case createStack was selected before we got this update back - // TODO: should really create a queuing pattern here so we can cancel on user-action - if (deployType == DeployType.UPDATE) { - populateParameters(templateFileParameters.mergeRemoteParameters(it.parameters())) - populateTags(it.tags()) - } - } - } ?: populateParameters(templateFileParameters) - } - } - } - } - } - - @TestOnly - fun forceUi( - panel: DialogPanel, - isCreateStack: Boolean? = null, - hasImageFunctions: Boolean? = null, - stacks: List? = null, - buckets: List? = null, - ecrRepos: List? = null, - forceStackName: Boolean = false, - stackName: String? = null, - forceBucket: Boolean = false, - bucket: String? = null, - forceEcrRepo: Boolean = false, - ecrRepo: String? = null, - autoExecute: Boolean? = null, - useContainer: Boolean? = null - ) { - if (stacks != null) { - stackSelector.model = MutableCollectionComboBoxModel(stacks) - stackSelector.forceLoaded() - } - - if (isCreateStack == true) { - deployType = DeployType.CREATE - } else if (isCreateStack == false) { - deployType = DeployType.UPDATE - } - - if (forceStackName || stackName != null) { - if (deployType == DeployType.CREATE) { - newStackName = stackName ?: "" - } else { - stackSelector.selectedItem = stacks?.first { it.stackName() == stackName } - } - } - - if (buckets != null) { - s3BucketSelector.model = MutableCollectionComboBoxModel(buckets) - s3BucketSelector.forceLoaded() - } - - if (forceBucket || bucket != null) { - s3BucketSelector.selectedItem = bucket - } - - if (ecrRepos != null) { - ecrRepoSelector.model = MutableCollectionComboBoxModel(ecrRepos) - ecrRepoSelector.forceLoaded() - } - - if (forceEcrRepo || ecrRepo != null) { - ecrRepoSelector.selectedItem = ecrRepo - } - - if (hasImageFunctions != null) { - showImageOptions = hasImageFunctions - } - - if (autoExecute != null) { - requireReview = autoExecute - } - - if (useContainer != null) { - this.useContainer = useContainer - } - - panel.reset() - } - - // visible for testing - internal fun populateParameters(parameters: List, templateFileDeclarationOverrides: List? = null) { - // TODO: would be nice to be able to pipe through the description - parametersField.envVars = parameters.associate { it.logicalName to (it.defaultValue() ?: "") } - templateFileParameters = templateFileDeclarationOverrides ?: CloudFormationTemplate.parse(project, templateFile).parameters().toList() - } - - private fun populateTags(tags: List) { - tagsField.envVars = tags.associate { it.key() to it.value() } - } - - enum class DeployType { - CREATE, - UPDATE - } -} diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/deploy/DeployServerlessApplicationSettings.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/deploy/DeployServerlessApplicationSettings.kt new file mode 100644 index 0000000000..e4a2d6acc8 --- /dev/null +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/deploy/DeployServerlessApplicationSettings.kt @@ -0,0 +1,15 @@ +// Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package software.aws.toolkits.jetbrains.services.lambda.deploy + +data class DeployServerlessApplicationSettings( + val stackName: String, + val bucket: String, + val ecrRepo: String?, + val autoExecute: Boolean, + val parameters: Map, + val tags: Map, + val useContainer: Boolean, + val capabilities: List +) diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/ui/CredentialIdentifierSelector.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/ui/CredentialIdentifierSelector.kt index d6ac35545d..6b3aa3c3a2 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/ui/CredentialIdentifierSelector.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/ui/CredentialIdentifierSelector.kt @@ -9,11 +9,7 @@ import com.intellij.ui.CollectionComboBoxModel import com.intellij.ui.ColoredListCellRenderer import com.intellij.ui.ComboboxSpeedSearch import com.intellij.ui.SimpleTextAttributes -import com.intellij.ui.layout.CellBuilder -import com.intellij.ui.layout.PropertyBinding -import com.intellij.ui.layout.Row import com.intellij.ui.layout.ValidationInfoBuilder -import com.intellij.ui.layout.applyToComponent import software.aws.toolkits.core.ConnectionSettings import software.aws.toolkits.core.credentials.CredentialIdentifier import software.aws.toolkits.core.credentials.CredentialType @@ -21,7 +17,6 @@ import software.aws.toolkits.core.region.AwsRegion import software.aws.toolkits.jetbrains.core.credentials.CredentialManager import software.aws.toolkits.resources.message import javax.swing.JList -import kotlin.reflect.KMutableProperty0 /** * Combo box used to select a credential identifier @@ -91,54 +86,12 @@ class CredentialIdentifierSelector(identifiers: List = Cre } companion object { - fun Row.credentialSelector( - prop: KMutableProperty0, - identifiers: List = CredentialManager.getInstance().getCredentialIdentifiers() - ): CellBuilder { - val binding = PropertyBinding( - get = { prop.get() }, - set = { prop.set(it) } - ) - return createSelector(binding, identifiers) - } - - /** - * Credential selector that forces the selection to be valid - * - * Note: Initial property may be a lateinit property, in that case there will be no default selection - */ - fun Row.validCredentialSelector( - prop: KMutableProperty0, - identifiers: List = CredentialManager.getInstance().getCredentialIdentifiers() - ): CellBuilder { - val binding = PropertyBinding( - get = { runCatching { prop.get() }.getOrNull() }, - set = { prop.set(it!!) /* Guarded by apply check */ } - ) - return createSelector(binding, identifiers) - .withValidationOnApply { validateSelection(it) } - .withValidationOnInput { validateSelection(it) } - } fun ValidationInfoBuilder.validateSelection(selector: CredentialIdentifierSelector): ValidationInfo? = if (!selector.isSelectionValid()) { error(message("credentials.invalid.invalid_selection")) } else { null } - - private fun Row.createSelector( - binding: PropertyBinding, - identifiers: List - ): CellBuilder { - val credentialProviderSelector = CredentialIdentifierSelector(identifiers) - return component(credentialProviderSelector) - .applyToComponent { selectedItem = binding.get() } - .withBinding( - { component -> component.getSelectedValidCredentialIdentifier() }, - { component, value -> component.setSelectedCredentialIdentifierId(value?.id) }, - binding - ) - } } } diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/ui/DeprecatedUtils.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/ui/DeprecatedUtils.kt deleted file mode 100644 index 3c9b7a5d63..0000000000 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/ui/DeprecatedUtils.kt +++ /dev/null @@ -1,27 +0,0 @@ -@file:Suppress("AbsentOrWrongFileLicense") -// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. -package software.aws.toolkits.jetbrains.ui - -import com.intellij.ui.UIBundle -import com.intellij.ui.components.JBTextField -import com.intellij.ui.layout.CellBuilder -import com.intellij.ui.layout.Row -import com.intellij.ui.layout.toBinding -import kotlin.reflect.KMutableProperty0 - -// https://github.com/JetBrains/intellij-community/blob/e0a2caa682d9d853b2736f0cc0b303ea1936a3c3/platform/platform-impl/src/com/intellij/ui/layout/Cell.kt#L506 -fun Row.intTextField(prop: KMutableProperty0, columns: Int? = null, range: IntRange? = null): CellBuilder { - val binding = prop.toBinding() - return textField( - { binding.get().toString() }, - { value -> value.toIntOrNull()?.let { intValue -> binding.set(range?.let { intValue.coerceIn(it.first, it.last) } ?: intValue) } }, - columns - ).withValidationOnInput { - val value = it.text.toIntOrNull() - when { - value == null -> error(UIBundle.message("please.enter.a.number")) - range != null && value !in range -> error(UIBundle.message("please.enter.a.number.from.0.to.1", range.first, range.last)) - else -> null - } - } -} diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/ui/RegionSelector.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/ui/RegionSelector.kt index d7451373ef..9a0d17133f 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/ui/RegionSelector.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/ui/RegionSelector.kt @@ -7,16 +7,8 @@ import com.intellij.openapi.ui.ComboBox import com.intellij.ui.CollectionComboBoxModel import com.intellij.ui.ComboboxSpeedSearch import com.intellij.ui.SimpleListCellRenderer -import com.intellij.ui.layout.Cell -import com.intellij.ui.layout.CellBuilder -import com.intellij.ui.layout.PropertyBinding -import com.intellij.ui.layout.applyToComponent -import com.intellij.ui.layout.toBinding -import com.intellij.ui.layout.toNullable import software.aws.toolkits.core.region.AwsRegion -import software.aws.toolkits.jetbrains.core.region.AwsRegionProvider import software.aws.toolkits.jetbrains.utils.ui.selected -import kotlin.reflect.KMutableProperty0 /** * Combo box used to select a region @@ -49,23 +41,5 @@ class RegionSelector : ComboBox() { private val RENDERER = SimpleListCellRenderer.create("") { it.displayName } - - /** - * @param serviceId If specified, will filter the list of regions down to only the regions that support the specified service - */ - fun Cell.regionSelector(prop: KMutableProperty0, serviceId: String? = null): CellBuilder> = - regionSelector(prop.toBinding(), serviceId) - - fun Cell.regionSelector(binding: PropertyBinding, serviceId: String? = null): CellBuilder> { - val regionProvider = AwsRegionProvider.getInstance() - val regions = when { - serviceId != null -> regionProvider.allRegionsForService(serviceId).values.toMutableList() - else -> regionProvider.allRegions().values.toMutableList() - } - val model = CollectionComboBoxModel(regions) - return comboBox(model, binding.toNullable(), RENDERER).applyToComponent { - ComboboxSpeedSearch(this) - } - } } } diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/ui/ResourceSelector.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/ui/ResourceSelector.kt index ca683e1441..5514dc4d7c 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/ui/ResourceSelector.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/ui/ResourceSelector.kt @@ -11,8 +11,6 @@ import com.intellij.openapi.ui.ComponentValidator import com.intellij.openapi.ui.ValidationInfo import com.intellij.ui.ComboboxSpeedSearch import com.intellij.ui.MutableCollectionComboBoxModel -import com.intellij.ui.layout.Cell -import com.intellij.ui.layout.PropertyBinding import com.intellij.util.ExceptionUtil import org.jetbrains.annotations.TestOnly import software.aws.toolkits.core.ConnectionSettings @@ -28,7 +26,6 @@ import software.aws.toolkits.resources.message import java.util.concurrent.CancellationException import java.util.concurrent.CompletableFuture import javax.swing.ListCellRenderer -import kotlin.reflect.KMutableProperty0 private typealias Selector = Either Boolean> @@ -266,10 +263,3 @@ class ResourceSelector private constructor( ) } } - -inline fun Cell.resourceSelector(resource: Resource>, connectionSettings: ConnectionSettings, prop: KMutableProperty0) = - component(ResourceSelector.builder().resource(resource).awsConnection(connectionSettings).build()).withBinding( - { it.selected() }, - { component, value -> component.selectedItem = value }, - PropertyBinding(prop::get, prop::set) - ) diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/ui/connection/SonoLoginOverlay.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/ui/connection/SonoLoginOverlay.kt index 9e7235c09b..43a242ce19 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/ui/connection/SonoLoginOverlay.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/ui/connection/SonoLoginOverlay.kt @@ -15,7 +15,6 @@ import com.intellij.ui.AppIcon import com.intellij.ui.components.panels.NonOpaquePanel import com.intellij.ui.components.panels.Wrapper import com.intellij.ui.dsl.builder.panel -import com.intellij.ui.layout.applyToComponent import com.intellij.util.ui.JBFont import icons.AwsIcons import software.aws.toolkits.core.ClientConnectionSettings diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/utils/ui/UiUtils.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/utils/ui/UiUtils.kt index b7ecd9d8a4..a9b7663b30 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/utils/ui/UiUtils.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/utils/ui/UiUtils.kt @@ -4,8 +4,6 @@ package software.aws.toolkits.jetbrains.utils.ui -import com.intellij.icons.AllIcons -import com.intellij.ide.HelpTooltip import com.intellij.lang.Language import com.intellij.openapi.application.runWriteAction import com.intellij.openapi.command.CommandProcessor @@ -17,18 +15,8 @@ import com.intellij.ui.ClickListener import com.intellij.ui.EditorTextField import com.intellij.ui.JBColor import com.intellij.ui.JreHiDpiUtil -import com.intellij.ui.components.JBLabel -import com.intellij.ui.components.JBRadioButton import com.intellij.ui.components.JBTextArea import com.intellij.ui.dsl.builder.MutableProperty -import com.intellij.ui.layout.Cell -import com.intellij.ui.layout.CellBuilder -import com.intellij.ui.layout.ComponentPredicate -import com.intellij.ui.layout.PropertyBinding -import com.intellij.ui.layout.Row -import com.intellij.ui.layout.applyToComponent -import com.intellij.ui.layout.toBinding -import com.intellij.ui.layout.withSelectedBinding import com.intellij.ui.paint.LinePainter2D import com.intellij.ui.speedSearch.SpeedSearchSupply import com.intellij.util.text.DateFormatUtil @@ -36,7 +24,6 @@ import com.intellij.util.text.SyncDateFormat import com.intellij.util.ui.GraphicsUtil import com.intellij.util.ui.JBUI import com.intellij.util.ui.UIUtil -import org.jetbrains.annotations.Nls import software.aws.toolkits.jetbrains.ui.KeyValueTextField import software.aws.toolkits.jetbrains.utils.formatText import java.awt.AlphaComposite @@ -49,7 +36,6 @@ import java.awt.event.MouseEvent import java.awt.geom.RoundRectangle2D import java.text.SimpleDateFormat import javax.swing.AbstractButton -import javax.swing.JButton import javax.swing.JComboBox import javax.swing.JComponent import javax.swing.JTable @@ -59,7 +45,6 @@ import javax.swing.ListModel import javax.swing.table.TableCellRenderer import javax.swing.text.Highlighter import javax.swing.text.JTextComponent -import kotlin.reflect.KMutableProperty0 import com.intellij.ui.dsl.builder.Cell as Cell2 fun JTextField?.blankAsNull(): String? = if (this?.text?.isNotBlank() == true) { @@ -251,12 +236,6 @@ class ResizingTextColumnRenderer : ResizingColumnRenderer() { * the child panels. This function makes it so the validation callbacks and apply are actually called. * @param applies An additional function that allows control based on visibility of other components or other factors */ -fun CellBuilder.installOnParent(applies: () -> Boolean = { true }): CellBuilder { - withValidationOnApply { - validate(applies, it) - } - return this -} fun Cell2.installOnParent(applies: () -> Boolean = { true }): Cell2 { validationOnApply { @@ -276,38 +255,9 @@ private inline fun validate(applies: () -> Boolean, component: DialogPanel): Val errors.firstOrNull() } -// backport since removed in 223 -fun CellBuilder.bindValueToProperty(prop: PropertyBinding, value: T): CellBuilder = apply { - component.isSelected = prop.get() == value - onApply { if (component.isSelected) prop.set(value) } - onReset { component.isSelected = prop.get() == value } - onIsModified { component.isSelected != (prop.get() == value) } -} - -fun Row.visibleIf(predicate: ComponentPredicate): Row { - visible = predicate() - predicate.addListener { visible = it } - return this -} - /** * Add a contextual help icon component */ -fun Cell.contextualHelp(description: String): CellBuilder { - val l = JBLabel(AllIcons.General.ContextHelp) - HelpTooltip().apply { - setDescription(description) - installOn(l) - } - return component(l) -} - -fun CellBuilder.withBinding(binding: PropertyBinding>) = - this.withBinding( - componentGet = { component -> component.envVars }, - componentSet = { component, value -> component.envVars = value }, - binding - ) fun com.intellij.ui.dsl.builder.Cell.withBinding(binding: MutableProperty>) = this.bind( @@ -315,38 +265,3 @@ fun com.intellij.ui.dsl.builder.Cell.withBinding(binding: Mut componentSet = { component, value -> component.envVars = value }, binding ) - -fun Row.keyValueTextField(title: String? = null, property: KMutableProperty0>): CellBuilder { - val field = if (title == null) { - KeyValueTextField() - } else { - KeyValueTextField(title) - } - - return component(field) - .withBinding(property.toBinding()) -} - -fun CellBuilder.toolTipText(@Nls text: String): CellBuilder { - applyToComponent { - toolTipText = text - } - - return this -} - -fun JButton.setEnabledAndVisible(state: Boolean) { - isEnabled = state - isVisible = state -} - -fun CellBuilder.enumBinding(property: KMutableProperty0, buttonValue: T) = this - .withSelectedBinding( - PropertyBinding( - get = { property.get() == buttonValue }, - set = { if (it) property.set(buttonValue) } - ) - ) - .applyToComponent { - isSelected = property.get() == buttonValue - } diff --git a/jetbrains-core/tst/software/aws/toolkits/jetbrains/services/lambda/deploy/DeploySamApplicationValidatorTest.kt b/jetbrains-core/tst/software/aws/toolkits/jetbrains/services/lambda/deploy/DeploySamApplicationValidatorTest.kt deleted file mode 100644 index 922c6b67cd..0000000000 --- a/jetbrains-core/tst/software/aws/toolkits/jetbrains/services/lambda/deploy/DeploySamApplicationValidatorTest.kt +++ /dev/null @@ -1,431 +0,0 @@ -// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -package software.aws.toolkits.jetbrains.services.lambda.deploy - -import com.intellij.openapi.ui.DialogPanel -import com.intellij.openapi.ui.ValidationInfo -import com.intellij.openapi.vfs.VfsUtil -import com.intellij.testFramework.EdtRule -import com.intellij.testFramework.RuleChain -import com.intellij.testFramework.RunsInEdt -import com.intellij.testFramework.TemporaryDirectory -import com.intellij.testFramework.runInEdtAndWait -import com.intellij.util.io.writeChild -import org.assertj.core.api.Assertions.assertThat -import org.jetbrains.yaml.psi.YAMLSequence -import org.junit.Before -import org.junit.Rule -import org.junit.Test -import software.amazon.awssdk.services.cloudformation.CloudFormationClient -import software.amazon.awssdk.services.cloudformation.model.StackSummary -import software.amazon.awssdk.services.ecr.EcrClient -import software.amazon.awssdk.services.s3.S3Client -import software.aws.toolkits.jetbrains.core.MockClientManagerRule -import software.aws.toolkits.jetbrains.services.cloudformation.Parameter -import software.aws.toolkits.jetbrains.services.ecr.resources.Repository -import software.aws.toolkits.jetbrains.services.lambda.sam.ValidateSamParameters -import software.aws.toolkits.jetbrains.utils.rules.JavaCodeInsightTestFixtureRule -import software.aws.toolkits.resources.message -import java.nio.file.Files - -@RunsInEdt -class DeploySamApplicationValidatorTest { - private val projectRule = JavaCodeInsightTestFixtureRule() - - @Rule - @JvmField - val ruleChain = RuleChain(projectRule, EdtRule()) - - @Rule - @JvmField - val tempDir = TemporaryDirectory() - - @JvmField - @Rule - val mockClientManagerRule = MockClientManagerRule() - - private lateinit var sut: DeployServerlessApplicationDialog - private lateinit var sutPanel: DialogPanel - - private val parameters = listOf( - TestParameter(logicalName = "param1", type = "String", defaultValue = "value1"), - TestParameter(logicalName = "param2", type = "String", defaultValue = "value2") - ) - - @Before - fun wireMocksTogetherWithValidOptions() { - mockClientManagerRule.apply { - create() - create() - create() - } - - val dir = Files.createDirectory(tempDir.newPath()).toAbsolutePath() - - runInEdtAndWait { - sut = DeployServerlessApplicationDialog( - projectRule.project, - VfsUtil.findFileByIoFile(dir.writeChild("path.yaml", byteArrayOf()).toFile(), true)!!, - loadResourcesOnCreate = false - ) - sutPanel = sut.buildPanel() - } - - val repo = Repository("repoName", "arn", "repositoryuri") - sut.forceUi( - sutPanel, - isCreateStack = false, - hasImageFunctions = false, - stacks = listOf(StackSummary.builder().stackName("stack123").build()), - buckets = listOf("bucket123"), - ecrRepos = listOf(repo), - stackName = "stack123", - bucket = "bucket123", - ecrRepo = repo.repositoryName, - autoExecute = false, - useContainer = true - ) - sut.populateParameters(parameters, parameters) - } - - @Test - fun validInputsReturnsNull() { - assertThat(validateAll()).isEmpty() - } - - @Test - fun validInputsNoRepoReturnsNull() { - sut.forceUi(sutPanel, forceEcrRepo = true, ecrRepo = null) - assertThat(validateAll()).isEmpty() - } - - @Test - fun validInputsWithNewStackReturnsNull() { - sut.forceUi(sutPanel, isCreateStack = true, stackName = "createStack") - assertThat(validateAll()).isEmpty() - - sut.forceUi(sutPanel, stackName = "n") - assertThat(validateAll()).isEmpty() - - sut.forceUi(sutPanel, stackName = "n1") - assertThat(validateAll()).isEmpty() - } - - @Test - fun validInputsWithImageReturnsNull() { - sut.forceUi(sutPanel, hasImageFunctions = true) - assertThat(validateAll()).isEmpty() - } - - @Test - fun stackMustBeSelected() { - sut.forceUi(sutPanel, isCreateStack = false, forceStackName = true, stackName = null) - assertThat(validateAll()).singleElement() - .matches { it.message.contains(message("serverless.application.deploy.validation.stack.missing")) } - } - - @Test - fun newStackNameMustBeSpecified() { - sut.forceUi(sutPanel, isCreateStack = true, forceStackName = true, stackName = null) - assertThat(validateAll()).singleElement() - .matches { it.message.contains(message("serverless.application.deploy.validation.new.stack.name.missing")) } - } - - @Test - fun invalidStackName_TooLong() { - val maxLength = ValidateSamParameters.MAX_STACK_NAME_LENGTH - sut.forceUi(sutPanel, isCreateStack = true, stackName = "x".repeat(maxLength + 1)) - - assertThat(validateAll()).singleElement() - .matches { it.message.contains(message("serverless.application.deploy.validation.new.stack.name.too.long", maxLength)) } - } - - @Test - fun invalidStackName_Duplicate() { - sut.forceUi( - sutPanel, - isCreateStack = true, - stackName = "bar", - stacks = listOf( - StackSummary.builder().stackName("foo").build(), - StackSummary.builder().stackName("bar").build(), - StackSummary.builder().stackName("baz").build() - ) - ) - - assertThat(validateAll()).singleElement() - .matches { it.message.contains(message("serverless.application.deploy.validation.new.stack.name.duplicate")) } - } - - @Test - fun invalidStackName_InvalidChars() { - val invalid = listOf( - "stack_1", - "stack#1", - "1stack", - " stack", - "stack!@#$%^&*()_+-=" - ) - invalid.forEach { - sut.forceUi(sutPanel, isCreateStack = true, stackName = it) - assertThat(validateAll()) - .singleElement() - .matches({ it.message.contains(message("serverless.application.deploy.validation.new.stack.name.invalid")) }, "for input $it") - } - } - - @Test - fun templateParameterAllTypesValid_hasValues() { - val parameters = listOf( - TestParameter(logicalName = "param1", type = "String", defaultValue = "value1"), - TestParameter(logicalName = "param2", type = "Number", defaultValue = "1"), - TestParameter(logicalName = "param3", type = "Number", defaultValue = "1.2"), - TestParameter(logicalName = "param4", type = "List", defaultValue = "10,20,1.2"), - TestParameter(logicalName = "param5", type = "CommaDelimitedList", defaultValue = "param1,param2"), - TestParameter(logicalName = "param6", type = "AWS::EC2::AvailabilityZone::Name", defaultValue = "us-fake-1"), - TestParameter(logicalName = "param7", type = "List", defaultValue = "us-fake-1a,us-fake-1b"), - TestParameter(logicalName = "param8", type = "AWS::SSM::Parameter::Value", defaultValue = "something"), - - ) - sut.populateParameters(parameters, parameters) - - assertThat(validateAll()).isEmpty() - } - - @Test - fun templateParameterAllTypesValid_noValues() { - val parameters = listOf( - TestParameter(logicalName = "param1", type = "String", defaultValue = ""), - TestParameter(logicalName = "param4", type = "List", defaultValue = ""), - TestParameter(logicalName = "param5", type = "CommaDelimitedList", defaultValue = ""), - TestParameter(logicalName = "param6", type = "AWS::EC2::AvailabilityZone::Name", defaultValue = ""), - TestParameter(logicalName = "param7", type = "List", defaultValue = ""), - TestParameter(logicalName = "param8", type = "AWS::SSM::Parameter::Value", defaultValue = ""), - ) - sut.populateParameters(parameters, parameters) - - assertThat(validateAll()).isEmpty() - } - - @Test - fun templateParameter_stringRegex() { - val parameters = listOf( - TestParameter( - logicalName = "goodRegex", - type = "String", - defaultValue = "example@example.com", - additionalProperties = mapOf( - "AllowedPattern" to "^[_A-Za-z0-9-\\+]+(\\.[_A-Za-z0-9-]+)*@[A-Za-z0-9-]+(\\.[A-Za-z0-9]+)*(\\.[A-Za-z]{2,})$" - ) - ) - ) - sut.populateParameters(parameters, parameters) - - assertThat(validateAll()).isEmpty() - } - - @Test - fun templateParameter_stringTooShort() { - val parameters = listOf( - TestParameter( - logicalName = "tooShort", - type = "String", - defaultValue = "", - additionalProperties = mapOf( - "MinLength" to "1", - "MaxLength" to "5" - ) - ) - ) - sut.populateParameters(parameters, parameters) - - assertThat(validateAll()).singleElement() - .matches { it.message.contains("tooShort does not meet MinLength") } - } - - @Test - fun templateParameter_stringTooLong() { - val parameters = listOf( - TestParameter( - logicalName = "tooLong", - type = "String", - defaultValue = "aaaaaaaaaa", - additionalProperties = mapOf( - "MinLength" to "1", - "MaxLength" to "5" - ) - ) - ) - sut.populateParameters(parameters, parameters) - - assertThat(validateAll()).singleElement() - .matches { it.message.contains("tooLong exceeds MaxLength") } - } - - @Test - fun templateParameter_stringFailsRegex() { - val parameters = listOf( - TestParameter( - logicalName = "regexFail", - type = "String", - defaultValue = "aaaaaaaaaa", - additionalProperties = mapOf( - "AllowedPattern" to "b*" - - ) - ) - ) - sut.populateParameters(parameters, parameters) - - assertThat(validateAll()).singleElement() - .matches { it.message.contains("regexFail does not match AllowedPattern") } - } - - @Test - fun templateParameter_stringConstraintsInvalid() { - val parameters = listOf( - TestParameter( - logicalName = "badRegex", - type = "String", - defaultValue = "", - additionalProperties = mapOf( - "AllowedPattern" to ")]]]]]totallyValidRegex([[[[" - ) - ), - TestParameter( - logicalName = "badLengthConstraints", - type = "String", - defaultValue = "", - additionalProperties = mapOf( - "MinLength" to "-42", - "MaxLength" to "3.14" - ) - ) - ) - sut.populateParameters(parameters, parameters) - - assertThat(validateAll()).singleElement() - .matches { it.message.contains("AllowedPattern for badRegex is not valid") } - } - - @Test - fun templateParameter_numberInvalid() { - val parameters = listOf( - TestParameter(logicalName = "notANumber", type = "Number", defaultValue = "f"), - TestParameter(logicalName = "notANumber2", type = "Number", defaultValue = "") - ) - sut.populateParameters(parameters, parameters) - - assertThat(validateAll()).singleElement() - .matches { it.message.contains("not a number") } - } - - @Test - fun templateParameter_numberTooSmall() { - val parameters = listOf( - TestParameter( - logicalName = "tooSmall", - type = "Number", - defaultValue = "0", - additionalProperties = mapOf( - "MinValue" to "0.1", - "MaxValue" to "5" - ) - ) - ) - sut.populateParameters(parameters, parameters) - - assertThat(validateAll()).singleElement() - .matches { it.message.contains("tooSmall is smaller than MinValue") } - } - - @Test - fun templateParameter_numberTooBig() { - val parameters = listOf( - TestParameter( - logicalName = "tooBig", - type = "Number", - defaultValue = "${Float.MAX_VALUE}", - additionalProperties = mapOf( - "MinValue" to "0.1", - "MaxValue" to "5" - ) - ) - ) - sut.populateParameters(parameters, parameters) - - assertThat(validateAll()).singleElement() - .matches { it.message.contains("tooBig is larger than MaxValue") } - } - - @Test - fun templateParameter_numberConstraintsInvalid() { - val parameters = listOf( - TestParameter( - logicalName = "badValueConstraints", - type = "Number", - defaultValue = "0", - additionalProperties = mapOf( - "MinValue" to "--3", - "MaxValue" to "++3" - ) - ) - ) - sut.populateParameters(parameters, parameters) - - assertThat(validateAll()).isEmpty() - } - - @Test - fun s3BucketMustBeSpecified() { - sut.forceUi(sutPanel, forceBucket = true, bucket = null) - assertThat(validateAll()).singleElement() - .matches { it.message.contains(message("serverless.application.deploy.validation.s3.bucket.empty")) } - } - - @Test - fun ecrRepoMustBeSpecifiedWithImages() { - sut.forceUi(sutPanel, hasImageFunctions = true, forceEcrRepo = true, ecrRepo = null) - - assertThat(validateAll()).singleElement() - .matches { it.message.contains(message("serverless.application.deploy.validation.ecr.repo.empty")) } - } - - private fun validateAll(): List = - sutPanel.validateCallbacks.mapNotNull { it.invoke() } - - private class TestParameter( - override val logicalName: String, - private val type: String, - private val defaultValue: String?, - private val additionalProperties: Map = emptyMap() - ) : Parameter { - override fun getScalarProperty(key: String): String = getOptionalScalarProperty(key)!! - - override fun getOptionalScalarProperty(key: String): String? { - if (key == "Type") { - return type - } - return additionalProperties.get(key) - } - - override fun setScalarProperty(key: String, value: String) { - throw NotImplementedError() - } - - override fun getSequenceProperty(key: String): YAMLSequence { - throw NotImplementedError() - } - - override fun getOptionalSequenceProperty(key: String): YAMLSequence? { - throw NotImplementedError() - } - - override fun defaultValue(): String? = defaultValue - - override fun description(): String? = null - - override fun constraintDescription(): String? = null - } -} diff --git a/jetbrains-rider/resources/META-INF/ext-rider.xml b/jetbrains-rider/resources/META-INF/ext-rider.xml index 3b9aa32f78..ace541cdb5 100644 --- a/jetbrains-rider/resources/META-INF/ext-rider.xml +++ b/jetbrains-rider/resources/META-INF/ext-rider.xml @@ -39,15 +39,10 @@ - - - - -