From 41bc893d7c193e783ddac44063944d854a63edcd Mon Sep 17 00:00:00 2001 From: ranyoo2367 Date: Thu, 9 Jul 2020 11:50:11 -0400 Subject: [PATCH 0001/1546] Added SQS to the AWS Explorer --- gradle.properties | 2 +- jetbrains-core/build.gradle | 1 + jetbrains-core/resources/META-INF/plugin.xml | 1 + .../explorer/nodes/SqsExplorerRootNode.kt | 15 +++++ .../toolkits/jetbrains/services/sqs/Queue.kt | 31 +++++++++ .../services/sqs/SqsExplorerNodes.kt | 47 +++++++++++++ .../services/sqs/resources/SqsResources.kt | 14 ++++ .../jetbrains/services/sqs/QueueTest.kt | 58 ++++++++++++++++ .../services/sqs/SqsServiceNodeTest.kt | 67 +++++++++++++++++++ .../resources/localized_messages.properties | 2 + 10 files changed, 237 insertions(+), 1 deletion(-) create mode 100644 jetbrains-core/src/software/aws/toolkits/jetbrains/core/explorer/nodes/SqsExplorerRootNode.kt create mode 100644 jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/Queue.kt create mode 100644 jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/SqsExplorerNodes.kt create mode 100644 jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/resources/SqsResources.kt create mode 100644 jetbrains-core/tst/software/aws/toolkits/jetbrains/services/sqs/QueueTest.kt create mode 100644 jetbrains-core/tst/software/aws/toolkits/jetbrains/services/sqs/SqsServiceNodeTest.kt diff --git a/gradle.properties b/gradle.properties index 7f197c4333..28381cea1f 100644 --- a/gradle.properties +++ b/gradle.properties @@ -11,7 +11,7 @@ publishChannel= # Common dependencies ideProfileName=2019.3 kotlinVersion=1.3.70 -awsSdkVersion=2.11.9 +awsSdkVersion=2.13.52 coroutinesVersion=1.3.3 ideaPluginVersion=0.4.20 ktlintVersion=0.36.0 diff --git a/jetbrains-core/build.gradle b/jetbrains-core/build.gradle index 7a5daeaa50..805c6eafa8 100644 --- a/jetbrains-core/build.gradle +++ b/jetbrains-core/build.gradle @@ -78,6 +78,7 @@ dependencies { api("software.amazon.awssdk:rds:$awsSdkVersion") api("software.amazon.awssdk:redshift:$awsSdkVersion") api("software.amazon.awssdk:secretsmanager:$awsSdkVersion") + api("software.amazon.awssdk:sqs:$awsSdkVersion") testImplementation project(path: ":core", configuration: 'testArtifacts') testImplementation('com.github.tomakehurst:wiremock-jre8:2.26.0') diff --git a/jetbrains-core/resources/META-INF/plugin.xml b/jetbrains-core/resources/META-INF/plugin.xml index 1e707a757d..ff29b534fe 100644 --- a/jetbrains-core/resources/META-INF/plugin.xml +++ b/jetbrains-core/resources/META-INF/plugin.xml @@ -247,6 +247,7 @@ + diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/core/explorer/nodes/SqsExplorerRootNode.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/core/explorer/nodes/SqsExplorerRootNode.kt new file mode 100644 index 0000000000..a8de0e6a66 --- /dev/null +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/core/explorer/nodes/SqsExplorerRootNode.kt @@ -0,0 +1,15 @@ +// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +package software.aws.toolkits.jetbrains.core.explorer.nodes + +import com.intellij.openapi.project.Project +import software.amazon.awssdk.services.sqs.SqsClient +import software.aws.toolkits.jetbrains.services.sqs.SqsServiceNode +import software.aws.toolkits.resources.message + +class SqsExplorerRootNode : AwsExplorerServiceNode { + override val serviceId: String = SqsClient.SERVICE_NAME + override val displayName: String = message("explorer.node.sqs") + + override fun buildServiceRootNode(project: Project) = SqsServiceNode(project, this) +} diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/Queue.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/Queue.kt new file mode 100644 index 0000000000..dbaf621318 --- /dev/null +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/Queue.kt @@ -0,0 +1,31 @@ +// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package software.aws.toolkits.jetbrains.services.sqs + +import software.aws.toolkits.core.region.AwsRegion +import java.lang.IllegalArgumentException +import software.aws.toolkits.resources.message + +/*This does not support FIPS*/ +class Queue(val queueUrl: String, val region: AwsRegion) { + val accountId: String by lazy { + val id = queueUrl.substringAfter("${region.id}").substringAfter("/").substringBefore("/") + if ((id == queueUrl) || (id.length != 12) || id.isBlank()) { + throw IllegalArgumentException(message("sqs.url.parse_error")) + } else { + id + } + } + + val queueName: String by lazy { + val name = queueUrl.substringAfter("$accountId/") + if (name == queueUrl || name.isBlank()) { + throw IllegalArgumentException(message("sqs.url.parse_error")) + } else { + name + } + } + + val arn = "arn:${region.partitionId}:sqs:${region.id}:$accountId:$queueName" +} diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/SqsExplorerNodes.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/SqsExplorerNodes.kt new file mode 100644 index 0000000000..1196bb57d9 --- /dev/null +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/SqsExplorerNodes.kt @@ -0,0 +1,47 @@ +// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package software.aws.toolkits.jetbrains.services.sqs + +import com.intellij.openapi.project.Project +import com.intellij.icons.AllIcons +import software.amazon.awssdk.services.sqs.SqsClient +import software.aws.toolkits.jetbrains.core.credentials.activeRegion +import software.aws.toolkits.jetbrains.core.explorer.nodes.AwsExplorerNode +import software.aws.toolkits.jetbrains.core.explorer.nodes.AwsExplorerResourceNode +import software.aws.toolkits.jetbrains.core.explorer.nodes.AwsExplorerServiceNode +import software.aws.toolkits.jetbrains.core.explorer.nodes.CacheBackedAwsExplorerServiceRootNode +import software.aws.toolkits.jetbrains.services.sqs.resources.SqsResources + +class SqsServiceNode( + project: Project, + service: AwsExplorerServiceNode +) : CacheBackedAwsExplorerServiceRootNode( + project, + service, + SqsResources.LIST_QUEUE_URLS +) { + override fun toNode(child: String): AwsExplorerNode<*> = SqsQueueNode(nodeProject, child) +} + +class SqsQueueNode( + project: Project, + val queueUrl: String +) : AwsExplorerResourceNode( + project, + SqsClient.SERVICE_NAME, + queueUrl, + AllIcons.Nodes.EmptyNode // TODO: Get & change icons +) { + private val queue = Queue(queueUrl, nodeProject.activeRegion()) + + override fun resourceType() = "queue" + + override fun resourceArn(): String = queue.arn + + override fun displayName(): String = queue.queueName + + override fun onDoubleClick() { + // TODO: create SQS tool window + } +} diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/resources/SqsResources.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/resources/SqsResources.kt new file mode 100644 index 0000000000..12fe9dbb09 --- /dev/null +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/resources/SqsResources.kt @@ -0,0 +1,14 @@ +// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package software.aws.toolkits.jetbrains.services.sqs.resources + +import software.amazon.awssdk.services.sqs.SqsClient +import software.aws.toolkits.jetbrains.core.ClientBackedCachedResource +import software.aws.toolkits.jetbrains.core.Resource + +object SqsResources { + val LIST_QUEUE_URLS: Resource.Cached> = ClientBackedCachedResource(SqsClient::class, "sqs.list_queues") { + listQueuesPaginator().queueUrls().toList() + } +} diff --git a/jetbrains-core/tst/software/aws/toolkits/jetbrains/services/sqs/QueueTest.kt b/jetbrains-core/tst/software/aws/toolkits/jetbrains/services/sqs/QueueTest.kt new file mode 100644 index 0000000000..cd0f16c14c --- /dev/null +++ b/jetbrains-core/tst/software/aws/toolkits/jetbrains/services/sqs/QueueTest.kt @@ -0,0 +1,58 @@ +// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package software.aws.toolkits.jetbrains.services.sqs + +import org.assertj.core.api.Assertions.assertThat +import org.junit.Test +import software.aws.toolkits.core.region.AwsRegion +import kotlin.test.assertFailsWith + +class QueueTest { + private val defaultRegion = AwsRegion("us-east-1", "US East (N. Virginia)", "aws") + + @Test + fun `China region endpoint parsed`() { + val queueRegion = AwsRegion("cn-northwest-1", "China (Ningxia)", "aws-cn") + val queue = Queue("https://sqs.cn-northwest-1.amazonaws.com.cn/123456789012/test-1", queueRegion) + + assertThat(queue.arn).isEqualTo("arn:aws-cn:sqs:cn-northwest-1:123456789012:test-1") + assertThat(queue.accountId).isEqualTo("123456789012") + assertThat(queue.queueName).isEqualTo("test-1") + } + + @Test + fun `GovCloud region endpoint parsed`() { + val queueRegion = AwsRegion("us-gov-east-1", "AWS GovCloud (US-East)", "aws-us-gov") + val queue = Queue("https://sqs.us-gov-east-1.amazonaws.com/123456789012/test-2", queueRegion) + + assertThat(queue.arn).isEqualTo("arn:aws-us-gov:sqs:us-gov-east-1:123456789012:test-2") + assertThat(queue.accountId).isEqualTo("123456789012") + assertThat(queue.queueName).isEqualTo("test-2") + } + + @Test + fun `AWS region endpoint parsed`() { + val queueRegion = AwsRegion("us-west-2", "US West (Oregon)", "aws") + val queue = Queue("https://sqs.us-west-2.amazonaws.com/123456789012/test-3", queueRegion) + + assertThat(queue.arn).isEqualTo("arn:aws:sqs:us-west-2:123456789012:test-3") + assertThat(queue.accountId).isEqualTo("123456789012") + assertThat(queue.queueName).isEqualTo("test-3") + } + + @Test + fun `Throw exception with non-url`() { + assertFailsWith { Queue("Not a URL", defaultRegion) } + } + + @Test + fun `Throw exception with no name`() { + assertFailsWith { Queue("https://sqs.us-east-1.amazonaws.com/123456789012/", defaultRegion) } + } + + @Test + fun `Throw exception with invalid account ID`() { + assertFailsWith { Queue("https://sqs.us-east-1.amazonaws.com/123/test-4", defaultRegion) } + } +} diff --git a/jetbrains-core/tst/software/aws/toolkits/jetbrains/services/sqs/SqsServiceNodeTest.kt b/jetbrains-core/tst/software/aws/toolkits/jetbrains/services/sqs/SqsServiceNodeTest.kt new file mode 100644 index 0000000000..ac8903e22d --- /dev/null +++ b/jetbrains-core/tst/software/aws/toolkits/jetbrains/services/sqs/SqsServiceNodeTest.kt @@ -0,0 +1,67 @@ +// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package software.aws.toolkits.jetbrains.services.sqs + +import com.intellij.testFramework.ProjectRule +import org.assertj.core.api.Assertions.assertThat +import org.junit.Rule +import org.junit.Test +import software.aws.toolkits.jetbrains.core.MockResourceCacheRule +import software.aws.toolkits.jetbrains.core.explorer.nodes.AwsExplorerEmptyNode +import software.aws.toolkits.jetbrains.core.explorer.nodes.AwsExplorerErrorNode +import software.aws.toolkits.jetbrains.core.explorer.nodes.SqsExplorerRootNode +import software.aws.toolkits.jetbrains.services.sqs.resources.SqsResources +import software.amazon.awssdk.utils.CompletableFutureUtils + +class SqsServiceNodeTest { + @JvmField + @Rule + val projectRule = ProjectRule() + + @JvmField + @Rule + val resourceCache = MockResourceCacheRule(projectRule) + + @Test + fun `Sqs queues are listed`() { + resourceCache.get().addEntry( + SqsResources.LIST_QUEUE_URLS, + listOf( + "https://sqs.us-east-1.amazonaws.com/123456789012/test2", + "https://sqs.us-east-1.amazonaws.com/123456789012/test4", + "https://sqs.us-east-1.amazonaws.com/123456789012/test3", + "https://sqs.us-east-1.amazonaws.com/123456789012/test1" + ) + ) + + val children = SqsServiceNode(projectRule.project, SQS_EXPLORER_NODE).children + + assertThat(children).allMatch { it is SqsQueueNode } + assertThat(children.filterIsInstance().map { it.displayName() }).containsExactlyInAnyOrder("test4", "test3", "test2", "test1") + assertThat(children.filterIsInstance().map { it.resourceArn() }).containsExactlyInAnyOrder( + "arn:aws:sqs:us-east-1:123456789012:test1", + "arn:aws:sqs:us-east-1:123456789012:test2", + "arn:aws:sqs:us-east-1:123456789012:test3", + "arn:aws:sqs:us-east-1:123456789012:test4" + ) + } + + @Test + fun `No queues listed`() { + resourceCache.get().addEntry(SqsResources.LIST_QUEUE_URLS, listOf()) + val children = SqsServiceNode(projectRule.project, SQS_EXPLORER_NODE).children + assertThat(children).hasOnlyOneElementSatisfying { it is AwsExplorerEmptyNode } + } + + @Test + fun `Error loading queues`() { + resourceCache.get().addEntry(SqsResources.LIST_QUEUE_URLS, CompletableFutureUtils.failedFuture(RuntimeException("Simulated error"))) + val children = SqsServiceNode(projectRule.project, SQS_EXPLORER_NODE).children + assertThat(children).hasOnlyOneElementSatisfying { it is AwsExplorerErrorNode } + } + + private companion object { + val SQS_EXPLORER_NODE = SqsExplorerRootNode() + } +} diff --git a/resources/resources/software/aws/toolkits/resources/localized_messages.properties b/resources/resources/software/aws/toolkits/resources/localized_messages.properties index d8ab128a6c..9dd9b1dbf4 100644 --- a/resources/resources/software/aws/toolkits/resources/localized_messages.properties +++ b/resources/resources/software/aws/toolkits/resources/localized_messages.properties @@ -317,6 +317,7 @@ explorer.node.rds=RDS explorer.node.redshift=Redshift explorer.node.s3=S3 explorer.node.schemas=Schemas +explorer.node.sqs=SQS explorer.refresh.description=Refresh explorer resources explorer.registry.no.schema.resources=Registry has no Schemas explorer.results_truncated=Results truncated, double click to load more @@ -667,3 +668,4 @@ settings.regions.none_selected=No region selected settings.regions.recent=Recent Regions settings.regions.region_sub_menu=All Regions settings.title=AWS Connection Settings +sqs.url.parse_error=Error parsing queue URL From 0ef07f0a44abcb962c18db4a3350be0d3e6c5cec Mon Sep 17 00:00:00 2001 From: Ran Yoo <30319724+ranyoo2367@users.noreply.github.com> Date: Thu, 16 Jul 2020 20:53:17 -0400 Subject: [PATCH 0002/1546] Add SQS icons (#1924) * added icons * light and dark switched * pixels adjusted * temp commit * revert --- .../resources/icons/resources/sqs/SqsQueue.svg | 13 +++++++++++++ .../resources/icons/resources/sqs/SqsQueue_dark.svg | 13 +++++++++++++ .../resources/icons/resources/sqs/SqsToolWindow.svg | 13 +++++++++++++ .../icons/resources/sqs/SqsToolWindow_dark.svg | 13 +++++++++++++ jetbrains-core/src/icons/AwsIcons.kt | 4 ++++ .../jetbrains/services/sqs/SqsExplorerNodes.kt | 4 ++-- 6 files changed, 58 insertions(+), 2 deletions(-) create mode 100644 jetbrains-core/resources/icons/resources/sqs/SqsQueue.svg create mode 100644 jetbrains-core/resources/icons/resources/sqs/SqsQueue_dark.svg create mode 100644 jetbrains-core/resources/icons/resources/sqs/SqsToolWindow.svg create mode 100644 jetbrains-core/resources/icons/resources/sqs/SqsToolWindow_dark.svg diff --git a/jetbrains-core/resources/icons/resources/sqs/SqsQueue.svg b/jetbrains-core/resources/icons/resources/sqs/SqsQueue.svg new file mode 100644 index 0000000000..01a0a80202 --- /dev/null +++ b/jetbrains-core/resources/icons/resources/sqs/SqsQueue.svg @@ -0,0 +1,13 @@ + + + Amazon simple queue service (SQS) - dark + + + + + + + + + + diff --git a/jetbrains-core/resources/icons/resources/sqs/SqsQueue_dark.svg b/jetbrains-core/resources/icons/resources/sqs/SqsQueue_dark.svg new file mode 100644 index 0000000000..8c89760c8f --- /dev/null +++ b/jetbrains-core/resources/icons/resources/sqs/SqsQueue_dark.svg @@ -0,0 +1,13 @@ + + + Amazon simple queue service (SQS) - light + + + + + + + + + + diff --git a/jetbrains-core/resources/icons/resources/sqs/SqsToolWindow.svg b/jetbrains-core/resources/icons/resources/sqs/SqsToolWindow.svg new file mode 100644 index 0000000000..9e5df0cc26 --- /dev/null +++ b/jetbrains-core/resources/icons/resources/sqs/SqsToolWindow.svg @@ -0,0 +1,13 @@ + + + Amazon simple queue service (SQS) - dark + + + + + + + + + + diff --git a/jetbrains-core/resources/icons/resources/sqs/SqsToolWindow_dark.svg b/jetbrains-core/resources/icons/resources/sqs/SqsToolWindow_dark.svg new file mode 100644 index 0000000000..ecb6efcf70 --- /dev/null +++ b/jetbrains-core/resources/icons/resources/sqs/SqsToolWindow_dark.svg @@ -0,0 +1,13 @@ + + + Amazon simple queue service (SQS) - light + + + + + + + + + + diff --git a/jetbrains-core/src/icons/AwsIcons.kt b/jetbrains-core/src/icons/AwsIcons.kt index fea8843fc3..f0276028b2 100644 --- a/jetbrains-core/src/icons/AwsIcons.kt +++ b/jetbrains-core/src/icons/AwsIcons.kt @@ -47,6 +47,10 @@ object AwsIcons { @JvmField val MYSQL = IconLoader.getIcon("/icons/resources/rds/Mysql.svg") // 16x16 @JvmField val POSTGRES = IconLoader.getIcon("/icons/resources/rds/Postgres.svg") // 16x16 } + object Sqs { + @JvmField val SQS_QUEUE = IconLoader.getIcon("/icons/resources/sqs/SqsQueue.svg") // 16x16 + @JvmField val SQS_TOOL_WINDOW = IconLoader.getIcon("/icons/resources/sqs/SqsToolWindow.svg") // 13x13 + } } object Actions { diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/SqsExplorerNodes.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/SqsExplorerNodes.kt index 1196bb57d9..5c7f091f19 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/SqsExplorerNodes.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/SqsExplorerNodes.kt @@ -4,7 +4,7 @@ package software.aws.toolkits.jetbrains.services.sqs import com.intellij.openapi.project.Project -import com.intellij.icons.AllIcons +import icons.AwsIcons import software.amazon.awssdk.services.sqs.SqsClient import software.aws.toolkits.jetbrains.core.credentials.activeRegion import software.aws.toolkits.jetbrains.core.explorer.nodes.AwsExplorerNode @@ -31,7 +31,7 @@ class SqsQueueNode( project, SqsClient.SERVICE_NAME, queueUrl, - AllIcons.Nodes.EmptyNode // TODO: Get & change icons + AwsIcons.Resources.Sqs.SQS_QUEUE ) { private val queue = Queue(queueUrl, nodeProject.activeRegion()) From 9bb9d2d5fd06fc75cb3c5ef1f69edfb54922470f Mon Sep 17 00:00:00 2001 From: Ran Yoo <30319724+ranyoo2367@users.noreply.github.com> Date: Fri, 17 Jul 2020 13:42:37 -0400 Subject: [PATCH 0003/1546] Added SQS tool window and actions (#1919) * add tool window and actions * toolwindow tab temp fix * added forms and SqsWindowUI file * small changes * style fix * PR changes * form files fixed --- jetbrains-core/resources/META-INF/plugin.xml | 10 +++ .../services/sqs/SqsExplorerNodes.kt | 5 +- .../services/sqs/actions/PollMessageAction.kt | 17 ++++++ .../services/sqs/actions/SendMessageAction.kt | 17 ++++++ .../sqs/toolwindow/PollMessagePane.form | 12 ++++ .../sqs/toolwindow/PollMessagePane.kt | 9 +++ .../sqs/toolwindow/SendMessagePane.form | 12 ++++ .../sqs/toolwindow/SendMessagePane.kt | 9 +++ .../services/sqs/toolwindow/SqsWindow.kt | 61 +++++++++++++++++++ .../services/sqs/toolwindow/SqsWindowUI.kt | 30 +++++++++ .../resources/localized_messages.properties | 4 ++ 11 files changed, 184 insertions(+), 2 deletions(-) create mode 100644 jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/actions/PollMessageAction.kt create mode 100644 jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/actions/SendMessageAction.kt create mode 100644 jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/toolwindow/PollMessagePane.form create mode 100644 jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/toolwindow/PollMessagePane.kt create mode 100644 jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/toolwindow/SendMessagePane.form create mode 100644 jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/toolwindow/SendMessagePane.kt create mode 100644 jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/toolwindow/SqsWindow.kt create mode 100644 jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/toolwindow/SqsWindowUI.kt diff --git a/jetbrains-core/resources/META-INF/plugin.xml b/jetbrains-core/resources/META-INF/plugin.xml index ff29b534fe..0290a2524d 100644 --- a/jetbrains-core/resources/META-INF/plugin.xml +++ b/jetbrains-core/resources/META-INF/plugin.xml @@ -190,6 +190,7 @@ + @@ -336,6 +337,15 @@ class="software.aws.toolkits.jetbrains.services.schemas.code.DownloadCodeForSchemaAction"/> + + + + + + + diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/SqsExplorerNodes.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/SqsExplorerNodes.kt index 5c7f091f19..106bf720c2 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/SqsExplorerNodes.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/SqsExplorerNodes.kt @@ -12,6 +12,7 @@ import software.aws.toolkits.jetbrains.core.explorer.nodes.AwsExplorerResourceNo import software.aws.toolkits.jetbrains.core.explorer.nodes.AwsExplorerServiceNode import software.aws.toolkits.jetbrains.core.explorer.nodes.CacheBackedAwsExplorerServiceRootNode import software.aws.toolkits.jetbrains.services.sqs.resources.SqsResources +import software.aws.toolkits.jetbrains.services.sqs.toolwindow.SqsWindow class SqsServiceNode( project: Project, @@ -33,7 +34,7 @@ class SqsQueueNode( queueUrl, AwsIcons.Resources.Sqs.SQS_QUEUE ) { - private val queue = Queue(queueUrl, nodeProject.activeRegion()) + val queue = Queue(queueUrl, nodeProject.activeRegion()) override fun resourceType() = "queue" @@ -42,6 +43,6 @@ class SqsQueueNode( override fun displayName(): String = queue.queueName override fun onDoubleClick() { - // TODO: create SQS tool window + SqsWindow.getInstance(nodeProject)?.pollMessage(queue) } } diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/actions/PollMessageAction.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/actions/PollMessageAction.kt new file mode 100644 index 0000000000..810f6d9d30 --- /dev/null +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/actions/PollMessageAction.kt @@ -0,0 +1,17 @@ +// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package software.aws.toolkits.jetbrains.services.sqs.actions + +import com.intellij.openapi.actionSystem.AnActionEvent +import com.intellij.openapi.project.DumbAware +import software.aws.toolkits.jetbrains.core.explorer.actions.SingleResourceNodeAction +import software.aws.toolkits.jetbrains.services.sqs.SqsQueueNode +import software.aws.toolkits.jetbrains.services.sqs.toolwindow.SqsWindow +import software.aws.toolkits.resources.message + +class PollMessageAction : SingleResourceNodeAction(message("sqs.poll.message")), DumbAware { + override fun actionPerformed(selected: SqsQueueNode, e: AnActionEvent) { + SqsWindow.getInstance(selected.nodeProject)?.pollMessage(selected.queue) + } +} diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/actions/SendMessageAction.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/actions/SendMessageAction.kt new file mode 100644 index 0000000000..c5bcc473eb --- /dev/null +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/actions/SendMessageAction.kt @@ -0,0 +1,17 @@ +// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package software.aws.toolkits.jetbrains.services.sqs.actions + +import com.intellij.openapi.actionSystem.AnActionEvent +import com.intellij.openapi.project.DumbAware +import software.aws.toolkits.jetbrains.core.explorer.actions.SingleResourceNodeAction +import software.aws.toolkits.jetbrains.services.sqs.SqsQueueNode +import software.aws.toolkits.jetbrains.services.sqs.toolwindow.SqsWindow +import software.aws.toolkits.resources.message + +class SendMessageAction : SingleResourceNodeAction(message("sqs.send.message")), DumbAware { + override fun actionPerformed(selected: SqsQueueNode, e: AnActionEvent) { + SqsWindow.getInstance(selected.nodeProject)?.sendMessage(selected.queue) + } +} diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/toolwindow/PollMessagePane.form b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/toolwindow/PollMessagePane.form new file mode 100644 index 0000000000..df67b5fe83 --- /dev/null +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/toolwindow/PollMessagePane.form @@ -0,0 +1,12 @@ + +
+ + + + + + + + + +
diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/toolwindow/PollMessagePane.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/toolwindow/PollMessagePane.kt new file mode 100644 index 0000000000..7f91aec188 --- /dev/null +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/toolwindow/PollMessagePane.kt @@ -0,0 +1,9 @@ +// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +package software.aws.toolkits.jetbrains.services.sqs.toolwindow + +import javax.swing.JPanel + +class PollMessagePane { + lateinit var component: JPanel +} diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/toolwindow/SendMessagePane.form b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/toolwindow/SendMessagePane.form new file mode 100644 index 0000000000..2c6afb6d35 --- /dev/null +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/toolwindow/SendMessagePane.form @@ -0,0 +1,12 @@ + +
+ + + + + + + + + +
diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/toolwindow/SendMessagePane.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/toolwindow/SendMessagePane.kt new file mode 100644 index 0000000000..0145d706b1 --- /dev/null +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/toolwindow/SendMessagePane.kt @@ -0,0 +1,9 @@ +// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +package software.aws.toolkits.jetbrains.services.sqs.toolwindow + +import javax.swing.JPanel + +class SendMessagePane { + lateinit var component: JPanel +} diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/toolwindow/SqsWindow.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/toolwindow/SqsWindow.kt new file mode 100644 index 0000000000..cccaf3614b --- /dev/null +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/toolwindow/SqsWindow.kt @@ -0,0 +1,61 @@ +// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package software.aws.toolkits.jetbrains.services.sqs.toolwindow + +import com.intellij.openapi.components.ServiceManager +import com.intellij.openapi.project.Project +import icons.AwsIcons +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.withContext +import kotlinx.coroutines.launch +import software.aws.toolkits.core.utils.error +import software.aws.toolkits.core.utils.getLogger +import software.aws.toolkits.jetbrains.core.toolwindow.ToolkitToolWindowManager +import software.aws.toolkits.jetbrains.core.toolwindow.ToolkitToolWindowType +import software.aws.toolkits.jetbrains.services.sqs.Queue +import software.aws.toolkits.jetbrains.utils.ApplicationThreadPoolScope +import software.aws.toolkits.jetbrains.utils.getCoroutineUiContext +import software.aws.toolkits.resources.message + +class SqsWindow(private val project: Project) : CoroutineScope by ApplicationThreadPoolScope("SqsWindow") { + private val toolWindow = ToolkitToolWindowManager.getInstance(project, SQS_TOOL_WINDOW) + private val edtContext = getCoroutineUiContext() + + fun pollMessage(queue: Queue) { + showQueue(queue, SqsWindowUI(project, queue).apply { pollMessage() }) + } + + fun sendMessage(queue: Queue) { + showQueue(queue, SqsWindowUI(project, queue).apply { sendMessage() }) + } + + private fun showQueue(queue: Queue, component: SqsWindowUI) = launch { + try { + val existingWindow = toolWindow.find(queue.queueUrl) + if (existingWindow != null) { + withContext(edtContext) { + existingWindow.dispose() + } + } + + withContext(edtContext) { + toolWindow.addTab(queue.queueName, component.mainPanel, activate = true, id = queue.queueUrl) + } + } catch (e: Exception) { + LOG.error(e) { "Exception thrown while trying to open queue '${queue.queueName}'" } + throw e + } + } + + companion object { + internal val SQS_TOOL_WINDOW = ToolkitToolWindowType( + "AWS.Sqs", + message("sqs.toolwindow"), + AwsIcons.Resources.Sqs.SQS_TOOL_WINDOW + ) + + fun getInstance(project: Project) = ServiceManager.getService(project, SqsWindow::class.java) + private val LOG = getLogger() + } +} diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/toolwindow/SqsWindowUI.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/toolwindow/SqsWindowUI.kt new file mode 100644 index 0000000000..cc67119790 --- /dev/null +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/toolwindow/SqsWindowUI.kt @@ -0,0 +1,30 @@ +// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package software.aws.toolkits.jetbrains.services.sqs.toolwindow + +import com.intellij.openapi.project.Project +import com.intellij.ui.components.JBTabbedPane +import software.aws.toolkits.jetbrains.services.sqs.Queue +import software.aws.toolkits.resources.message + +// Will add more parameters once window is populated +class SqsWindowUI(private val project: Project, val queue: Queue) { + val mainPanel = JBTabbedPane().apply { + this.add(message("sqs.queue.polled.messages"), PollMessagePane().component) + this.add(message("sqs.send.message"), SendMessagePane().component) + } + + fun pollMessage() { + mainPanel.selectedIndex = POLL_MESSAGE_PANE + } + + fun sendMessage() { + mainPanel.selectedIndex = SEND_MESSAGE_PANE + } + + companion object { + const val POLL_MESSAGE_PANE = 0 + const val SEND_MESSAGE_PANE = 1 + } +} diff --git a/resources/resources/software/aws/toolkits/resources/localized_messages.properties b/resources/resources/software/aws/toolkits/resources/localized_messages.properties index 9dd9b1dbf4..310b50733e 100644 --- a/resources/resources/software/aws/toolkits/resources/localized_messages.properties +++ b/resources/resources/software/aws/toolkits/resources/localized_messages.properties @@ -668,4 +668,8 @@ settings.regions.none_selected=No region selected settings.regions.recent=Recent Regions settings.regions.region_sub_menu=All Regions settings.title=AWS Connection Settings +sqs.poll.message=Poll for Messages +sqs.queue.polled.messages=Polled Messages +sqs.send.message=Send a Message +sqs.toolwindow=SQS sqs.url.parse_error=Error parsing queue URL From 7f542ce8db208fd75cc70547f1dc32b93b6b6e2a Mon Sep 17 00:00:00 2001 From: Ran Yoo <30319724+ranyoo2367@users.noreply.github.com> Date: Thu, 30 Jul 2020 12:36:45 -0400 Subject: [PATCH 0004/1546] SQS: Added Polled Messages Pane (#1950) * implemented poll message form * cell rendering, message truncating, tests added * added exceptions * UI bug fix * small fixes * localization file fix * pr comments pt 1 * Uiutil changes * PR changes 2 * Added new abstract class for column rendering * Added extra set up for poll messages pane * Pane test edited * New RuleUtils.randomname and warning page * PR changes 3 * RuleUtils fix --- .../aws/toolkits/core/utils/RuleUtils.kt | 3 +- .../cloudwatch/logs/editor/TableUtils.kt | 46 +------ .../jetbrains/services/sqs/SqsUtils.kt | 7 ++ .../services/sqs/toolwindow/MessagesTable.kt | 55 ++++++++ .../sqs/toolwindow/PollMessagePane.form | 53 +++++++- .../sqs/toolwindow/PollMessagePane.kt | 88 ++++++++++++- .../sqs/toolwindow/PollMessageUtils.kt | 50 ++++++++ .../services/sqs/toolwindow/PollWarning.form | 32 +++++ .../services/sqs/toolwindow/PollWarning.kt | 21 ++++ .../services/sqs/toolwindow/SqsWindow.kt | 7 +- .../services/sqs/toolwindow/SqsWindowUI.kt | 14 ++- .../utils/ui/ResizingColumnRenderer.kt | 47 +++++++ .../toolkits/jetbrains/utils/ui/UiUtils.kt | 22 ++++ .../sqs/toolwindow/PollMessagePaneTest.kt | 119 ++++++++++++++++++ .../sqs/toolwindow/PollMessageUtilsTest.kt | 59 +++++++++ .../resources/localized_messages.properties | 9 ++ 16 files changed, 576 insertions(+), 56 deletions(-) create mode 100644 jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/SqsUtils.kt create mode 100644 jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/toolwindow/MessagesTable.kt create mode 100644 jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/toolwindow/PollMessageUtils.kt create mode 100644 jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/toolwindow/PollWarning.form create mode 100644 jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/toolwindow/PollWarning.kt create mode 100644 jetbrains-core/src/software/aws/toolkits/jetbrains/utils/ui/ResizingColumnRenderer.kt create mode 100644 jetbrains-core/tst/software/aws/toolkits/jetbrains/services/sqs/toolwindow/PollMessagePaneTest.kt create mode 100644 jetbrains-core/tst/software/aws/toolkits/jetbrains/services/sqs/toolwindow/PollMessageUtilsTest.kt diff --git a/core/tst/software/aws/toolkits/core/utils/RuleUtils.kt b/core/tst/software/aws/toolkits/core/utils/RuleUtils.kt index 3cb636a72c..44ac0ba900 100644 --- a/core/tst/software/aws/toolkits/core/utils/RuleUtils.kt +++ b/core/tst/software/aws/toolkits/core/utils/RuleUtils.kt @@ -7,8 +7,9 @@ import java.util.Random object RuleUtils { fun randomName(prefix: String = "a", length: Int = 63): String { + val characters = ('0'..'9') + ('A'..'Z') + ('a'..'Z') val userName = System.getProperty("user.name", "unknown") - return "${prefix.toLowerCase()}-${userName.toLowerCase()}-${Random().nextInt(10000)}".take(length) + return "${prefix.toLowerCase()}-${userName.toLowerCase()}-${List(length) { characters.random() }.joinToString("")}".take(length) } fun prefixFromCallingClass(): String { diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/editor/TableUtils.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/editor/TableUtils.kt index dbce163a09..60480374c1 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/editor/TableUtils.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/editor/TableUtils.kt @@ -5,24 +5,17 @@ package software.aws.toolkits.jetbrains.services.cloudwatch.logs.editor import com.intellij.ui.SimpleColoredComponent import com.intellij.ui.speedSearch.SpeedSearchUtil -import com.intellij.util.text.DateFormatUtil -import com.intellij.util.text.SyncDateFormat import com.intellij.util.ui.ColumnInfo import com.intellij.util.ui.ListTableModel import software.amazon.awssdk.services.cloudwatchlogs.model.LogStream import software.aws.toolkits.jetbrains.services.cloudwatch.logs.LogStreamEntry import software.aws.toolkits.jetbrains.utils.ui.WrappingCellRenderer import software.aws.toolkits.jetbrains.utils.ui.setSelectionHighlighting +import software.aws.toolkits.jetbrains.utils.ui.ResizingDateColumnRenderer import software.aws.toolkits.resources.message -import java.awt.BorderLayout import java.awt.Component -import java.text.SimpleDateFormat -import javax.swing.JLabel -import javax.swing.JPanel import javax.swing.JTable import javax.swing.SortOrder -import javax.swing.border.CompoundBorder -import javax.swing.table.DefaultTableCellRenderer import javax.swing.table.TableCellRenderer import javax.swing.table.TableRowSorter @@ -94,40 +87,3 @@ class LogStreamMessageColumn : ColumnInfo(message("gener override fun isCellEditable(item: LogStreamEntry?): Boolean = false override fun getRenderer(item: LogStreamEntry?): TableCellRenderer? = renderer } - -private class ResizingDateColumnRenderer(showSeconds: Boolean) : TableCellRenderer { - private val defaultRenderer = DefaultTableCellRenderer() - private val formatter: SyncDateFormat = if (showSeconds) { - SyncDateFormat(SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS")) - } else { - DateFormatUtil.getDateTimeFormat() - } - - override fun getTableCellRendererComponent(table: JTable?, value: Any?, isSelected: Boolean, hasFocus: Boolean, row: Int, column: Int): Component { - // This wrapper will let us force the component to be at the top instead of in the middle for linewraps - val wrapper = JPanel(BorderLayout()) - val defaultComponent = defaultRenderer.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column) - if (table == null) { - return defaultComponent - } - val component = defaultComponent as? JLabel ?: return defaultComponent - component.text = (value as? String)?.toLongOrNull()?.let { - formatter.format(it) - } - if (component.preferredSize.width > table.columnModel.getColumn(column).preferredWidth) { - // add 3 pixels of padding. No padding makes it go into ... mode cutting off the end - table.columnModel.getColumn(column).preferredWidth = component.preferredSize.width + 3 - table.columnModel.getColumn(column).maxWidth = component.preferredSize.width + 3 - } - wrapper.add(component, BorderLayout.NORTH) - // Make sure the background matches for selection - wrapper.background = component.background - // if a component is selected, it puts a border on it, move the border to the wrapper instead - if (isSelected) { - // this border has an outside and inside border, take only the outside border - wrapper.border = (component.border as? CompoundBorder)?.outsideBorder - } - component.border = null - return wrapper - } -} diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/SqsUtils.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/SqsUtils.kt new file mode 100644 index 0000000000..9663c204f9 --- /dev/null +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/SqsUtils.kt @@ -0,0 +1,7 @@ +// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package software.aws.toolkits.jetbrains.services.sqs + +const val MAX_NUMBER_OF_POLLED_MESSAGES = 10 +const val MAX_LENGTH_OF_POLLED_MESSAGES = 1024 diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/toolwindow/MessagesTable.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/toolwindow/MessagesTable.kt new file mode 100644 index 0000000000..a5b71c3b67 --- /dev/null +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/toolwindow/MessagesTable.kt @@ -0,0 +1,55 @@ +// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package software.aws.toolkits.jetbrains.services.sqs.toolwindow + +import com.intellij.ui.ScrollPaneFactory +import com.intellij.ui.TableSpeedSearch +import com.intellij.ui.table.TableView +import com.intellij.util.ui.ListTableModel +import software.amazon.awssdk.services.sqs.model.Message +import software.aws.toolkits.resources.message +import javax.swing.JComponent +import javax.swing.JTable + +class MessagesTable { + val component: JComponent + val table: TableView + val tableModel = ListTableModel( + arrayOf( + MessageIdColumn(), + MessageBodyColumn(), + MessageSenderIdColumn(), + MessageDateColumn() + ), mutableListOf() + ) + + init { + table = TableView(tableModel).apply { + autoscrolls = true + tableHeader.reorderingAllowed = false + tableHeader.resizingAllowed = false + autoResizeMode = JTable.AUTO_RESIZE_LAST_COLUMN + setPaintBusy(true) + emptyText.text = message("loading_resource.loading") + } + + TableSpeedSearch(table) + component = ScrollPaneFactory.createScrollPane(table) + } + + fun reset() { + while (tableModel.rowCount != 0) { + tableModel.removeRow(0) + } + } + + fun setBusy(busy: Boolean) { + table.setPaintBusy(busy) + if (busy) { + table.emptyText.text = message("loading_resource.loading") + } else { + table.emptyText.text = message("sqs.message.no_messages") + } + } +} diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/toolwindow/PollMessagePane.form b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/toolwindow/PollMessagePane.form index df67b5fe83..4b0a860819 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/toolwindow/PollMessagePane.form +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/toolwindow/PollMessagePane.form @@ -1,12 +1,59 @@
- + - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/toolwindow/PollMessagePane.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/toolwindow/PollMessagePane.kt index 7f91aec188..b3c1cfa006 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/toolwindow/PollMessagePane.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/toolwindow/PollMessagePane.kt @@ -2,8 +2,94 @@ // SPDX-License-Identifier: Apache-2.0 package software.aws.toolkits.jetbrains.services.sqs.toolwindow +import com.intellij.openapi.ui.SimpleToolWindowPanel +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext +import software.amazon.awssdk.services.sqs.SqsClient +import software.amazon.awssdk.services.sqs.model.Message +import software.amazon.awssdk.services.sqs.model.QueueAttributeName +import software.aws.toolkits.jetbrains.services.sqs.MAX_NUMBER_OF_POLLED_MESSAGES +import software.aws.toolkits.jetbrains.services.sqs.Queue +import software.aws.toolkits.jetbrains.utils.ApplicationThreadPoolScope +import software.aws.toolkits.resources.message +import javax.swing.JButton +import javax.swing.JLabel import javax.swing.JPanel -class PollMessagePane { +class PollMessagePane( + private val client: SqsClient, + private val queue: Queue +) : CoroutineScope by ApplicationThreadPoolScope("PollMessagesPane") { lateinit var component: JPanel + lateinit var messagesAvailableLabel: JLabel + lateinit var tablePanel: SimpleToolWindowPanel + lateinit var pollButton: JButton + val messagesTable = MessagesTable() + + private fun createUIComponents() { + tablePanel = SimpleToolWindowPanel(false, true) + } + + init { + tablePanel.setContent(PollWarning(this).content) + pollButton.isVisible = false + pollButton.addActionListener { + poll() + } + } + + fun startPolling() { + tablePanel.setContent(messagesTable.component) + pollButton.isVisible = true + launch { + requestMessages() + addTotal() + } + } + + suspend fun requestMessages() { + try { + withContext(Dispatchers.IO) { + val polledMessages: List = client.receiveMessage { + it.queueUrl(queue.queueUrl) + it.attributeNames(QueueAttributeName.ALL) + it.maxNumberOfMessages(MAX_NUMBER_OF_POLLED_MESSAGES) + }.messages() + + polledMessages.forEach { + messagesTable.tableModel.addRow(it) + } + messagesTable.setBusy(busy = false) + } + } catch (e: Exception) { + messagesTable.table.emptyText.text = message("sqs.failed_to_poll_messages") + } + } + + suspend fun addTotal() { + try { + withContext(Dispatchers.IO) { + val numMessages = client.getQueueAttributes { + it.queueUrl(queue.queueUrl) + it.attributeNames(QueueAttributeName.APPROXIMATE_NUMBER_OF_MESSAGES) + }.attributes().getValue(QueueAttributeName.APPROXIMATE_NUMBER_OF_MESSAGES) + + messagesAvailableLabel.text = message("sqs.messages.available.text") + numMessages + } + } catch (e: Exception) { + messagesAvailableLabel.text = message("sqs.failed_to_load_total") + } + } + + // TODO: Add message table actions + + private fun poll() = launch { + // TODO: Add debounce + messagesTable.setBusy(busy = true) + messagesTable.reset() + requestMessages() + addTotal() + } } diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/toolwindow/PollMessageUtils.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/toolwindow/PollMessageUtils.kt new file mode 100644 index 0000000000..fd126d52e2 --- /dev/null +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/toolwindow/PollMessageUtils.kt @@ -0,0 +1,50 @@ +// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package software.aws.toolkits.jetbrains.services.sqs.toolwindow + +import com.intellij.util.ui.ColumnInfo +import org.apache.commons.lang.StringUtils +import software.amazon.awssdk.services.sqs.model.Message +import software.amazon.awssdk.services.sqs.model.MessageSystemAttributeName +import software.aws.toolkits.jetbrains.services.sqs.MAX_LENGTH_OF_POLLED_MESSAGES +import software.aws.toolkits.jetbrains.utils.ui.ResizingDateColumnRenderer +import software.aws.toolkits.jetbrains.utils.ui.ResizingTextColumnRenderer +import software.aws.toolkits.jetbrains.utils.ui.WrappingCellRenderer +import software.aws.toolkits.resources.message +import java.lang.IllegalArgumentException +import javax.swing.table.TableCellRenderer + +class MessageIdColumn : ColumnInfo(message("sqs.message.message_id")) { + private val renderer = ResizingTextColumnRenderer() + override fun valueOf(item: Message?): String? = item?.messageId() + override fun isCellEditable(item: Message?): Boolean = false + override fun getRenderer(item: Message?): TableCellRenderer? = renderer +} + +class MessageBodyColumn : ColumnInfo(message("sqs.message.message_body")) { + private val renderer = WrappingCellRenderer(wrapOnSelection = true, toggleableWrap = false) + // Truncated the message body to show up to 1KB, as it can be up to 256KB in size. Cannot limit the retrieved message size through API. + override fun valueOf(item: Message?): String? = StringUtils.abbreviate(item?.body(), MAX_LENGTH_OF_POLLED_MESSAGES) + override fun isCellEditable(item: Message?): Boolean = false + override fun getRenderer(item: Message?): TableCellRenderer? = renderer +} + +class MessageSenderIdColumn : ColumnInfo(message("sqs.message.sender_id")) { + private val renderer = ResizingTextColumnRenderer() + override fun valueOf(item: Message?): String? = item?.attributes()?.getValue(MessageSystemAttributeName.SENDER_ID) + override fun isCellEditable(item: Message?): Boolean = false + override fun getRenderer(item: Message?): TableCellRenderer? = renderer +} + +class MessageDateColumn : ColumnInfo(message("sqs.message.timestamp")) { + private val renderer = ResizingDateColumnRenderer(showSeconds = true) + override fun valueOf(item: Message): String { + if (item !is Message) { + throw IllegalArgumentException(message("sqs.failed_to_poll_messages")) + } + return item.attributes().getValue(MessageSystemAttributeName.SENT_TIMESTAMP) + } + override fun isCellEditable(item: Message?): Boolean = false + override fun getRenderer(item: Message?): TableCellRenderer? = renderer +} diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/toolwindow/PollWarning.form b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/toolwindow/PollWarning.form new file mode 100644 index 0000000000..f609c077e2 --- /dev/null +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/toolwindow/PollWarning.form @@ -0,0 +1,32 @@ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/toolwindow/PollWarning.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/toolwindow/PollWarning.kt new file mode 100644 index 0000000000..14b1a4d362 --- /dev/null +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/toolwindow/PollWarning.kt @@ -0,0 +1,21 @@ +// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +package software.aws.toolkits.jetbrains.services.sqs.toolwindow + +import com.intellij.ui.components.JBLabel +import javax.swing.JButton +import javax.swing.JPanel +import software.aws.toolkits.resources.message + +class PollWarning(private val pane: PollMessagePane) { + lateinit var content: JPanel + lateinit var warningText: JBLabel + lateinit var pollButton: JButton + + init { + warningText.text = message("sqs.poll.warning.text") + pollButton.addActionListener { + pane.startPolling() + } + } +} diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/toolwindow/SqsWindow.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/toolwindow/SqsWindow.kt index cccaf3614b..208af75248 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/toolwindow/SqsWindow.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/toolwindow/SqsWindow.kt @@ -9,8 +9,10 @@ import icons.AwsIcons import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.withContext import kotlinx.coroutines.launch +import software.amazon.awssdk.services.sqs.SqsClient import software.aws.toolkits.core.utils.error import software.aws.toolkits.core.utils.getLogger +import software.aws.toolkits.jetbrains.core.awsClient import software.aws.toolkits.jetbrains.core.toolwindow.ToolkitToolWindowManager import software.aws.toolkits.jetbrains.core.toolwindow.ToolkitToolWindowType import software.aws.toolkits.jetbrains.services.sqs.Queue @@ -21,13 +23,14 @@ import software.aws.toolkits.resources.message class SqsWindow(private val project: Project) : CoroutineScope by ApplicationThreadPoolScope("SqsWindow") { private val toolWindow = ToolkitToolWindowManager.getInstance(project, SQS_TOOL_WINDOW) private val edtContext = getCoroutineUiContext() + private val client: SqsClient = project.awsClient() fun pollMessage(queue: Queue) { - showQueue(queue, SqsWindowUI(project, queue).apply { pollMessage() }) + showQueue(queue, SqsWindowUI(client, queue).apply { pollMessage() }) } fun sendMessage(queue: Queue) { - showQueue(queue, SqsWindowUI(project, queue).apply { sendMessage() }) + showQueue(queue, SqsWindowUI(client, queue).apply { sendMessage() }) } private fun showQueue(queue: Queue, component: SqsWindowUI) = launch { diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/toolwindow/SqsWindowUI.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/toolwindow/SqsWindowUI.kt index cc67119790..663635b080 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/toolwindow/SqsWindowUI.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/toolwindow/SqsWindowUI.kt @@ -3,16 +3,22 @@ package software.aws.toolkits.jetbrains.services.sqs.toolwindow -import com.intellij.openapi.project.Project import com.intellij.ui.components.JBTabbedPane +import com.intellij.util.ui.JBUI +import software.amazon.awssdk.services.sqs.SqsClient import software.aws.toolkits.jetbrains.services.sqs.Queue import software.aws.toolkits.resources.message // Will add more parameters once window is populated -class SqsWindowUI(private val project: Project, val queue: Queue) { +class SqsWindowUI( + private val client: SqsClient, + val queue: Queue +) { val mainPanel = JBTabbedPane().apply { - this.add(message("sqs.queue.polled.messages"), PollMessagePane().component) - this.add(message("sqs.send.message"), SendMessagePane().component) + tabComponentInsets = JBUI.emptyInsets() + border = JBUI.Borders.empty() + add(message("sqs.queue.polled.messages"), PollMessagePane(client, queue).component) + add(message("sqs.send.message"), SendMessagePane().component) } fun pollMessage() { diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/utils/ui/ResizingColumnRenderer.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/utils/ui/ResizingColumnRenderer.kt new file mode 100644 index 0000000000..9dc20db569 --- /dev/null +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/utils/ui/ResizingColumnRenderer.kt @@ -0,0 +1,47 @@ +// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package software.aws.toolkits.jetbrains.utils.ui + +import java.awt.BorderLayout +import java.awt.Component +import javax.swing.JLabel +import javax.swing.JPanel +import javax.swing.JTable +import javax.swing.border.CompoundBorder +import javax.swing.table.DefaultTableCellRenderer +import javax.swing.table.TableCellRenderer + +abstract class ResizingColumnRenderer : TableCellRenderer { + private val defaultRenderer = DefaultTableCellRenderer() + abstract fun getText(value: Any?): String? + + override fun getTableCellRendererComponent(table: JTable?, value: Any?, isSelected: Boolean, hasFocus: Boolean, row: Int, column: Int): Component { + // This wrapper will let us force the component to be at the top instead of in the middle for linewraps + val wrapper = JPanel(BorderLayout()) + val defaultComponent = defaultRenderer.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column) + if (table == null) { + return defaultComponent + } + val component = defaultComponent as? JLabel ?: return defaultComponent + + // This will set the component text accordingly + component.text = getText(value) + + if (component.preferredSize.width > table.columnModel.getColumn(column).preferredWidth) { + // add 3 pixels of padding. No padding makes it go into ... mode cutting off the end + table.columnModel.getColumn(column).preferredWidth = component.preferredSize.width + 3 + table.columnModel.getColumn(column).maxWidth = component.preferredSize.width + 3 + } + wrapper.add(component, BorderLayout.NORTH) + // Make sure the background matches for selection + wrapper.background = component.background + // if a component is selected, it puts a border on it, move the border to the wrapper instead + if (isSelected) { + // this border has an outside and inside border, take only the outside border + wrapper.border = (component.border as? CompoundBorder)?.outsideBorder + } + component.border = null + return wrapper + } +} 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 497d33f2a9..921ba41763 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 @@ -17,6 +17,8 @@ import com.intellij.ui.components.JBLabel import com.intellij.ui.components.JBTextArea import com.intellij.ui.paint.LinePainter2D import com.intellij.ui.speedSearch.SpeedSearchSupply +import com.intellij.util.text.DateFormatUtil +import com.intellij.util.text.SyncDateFormat import com.intellij.util.ui.GraphicsUtil import com.intellij.util.ui.JBUI import com.intellij.util.ui.UIUtil @@ -29,6 +31,7 @@ import java.awt.Graphics2D import java.awt.Shape import java.awt.event.MouseEvent import java.awt.geom.RoundRectangle2D +import java.text.SimpleDateFormat import javax.swing.AbstractButton import javax.swing.JComboBox import javax.swing.JComponent @@ -197,3 +200,22 @@ class WrappingCellRenderer(private val wrapOnSelection: Boolean, private val tog return component } } + +class ResizingDateColumnRenderer(showSeconds: Boolean) : ResizingColumnRenderer() { + private val formatter: SyncDateFormat = if (showSeconds) { + SyncDateFormat(SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS")) + } else { + DateFormatUtil.getDateTimeFormat() + } + + override fun getText(value: Any?): String? { + val text = (value as? String)?.toLongOrNull()?.let { + formatter.format(it) + } + return text + } +} + +class ResizingTextColumnRenderer() : ResizingColumnRenderer() { + override fun getText(value: Any?): String? = (value as? String)?.trim() +} diff --git a/jetbrains-core/tst/software/aws/toolkits/jetbrains/services/sqs/toolwindow/PollMessagePaneTest.kt b/jetbrains-core/tst/software/aws/toolkits/jetbrains/services/sqs/toolwindow/PollMessagePaneTest.kt new file mode 100644 index 0000000000..802f1a3b2e --- /dev/null +++ b/jetbrains-core/tst/software/aws/toolkits/jetbrains/services/sqs/toolwindow/PollMessagePaneTest.kt @@ -0,0 +1,119 @@ +// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package software.aws.toolkits.jetbrains.services.sqs.toolwindow + +import com.nhaarman.mockitokotlin2.whenever +import kotlinx.coroutines.runBlocking +import org.assertj.core.api.Assertions.assertThat +import org.junit.Before +import org.junit.Test +import org.mockito.Mockito +import software.amazon.awssdk.services.sqs.SqsClient +import software.amazon.awssdk.services.sqs.model.Message +import software.amazon.awssdk.services.sqs.model.GetQueueAttributesRequest +import software.amazon.awssdk.services.sqs.model.GetQueueAttributesResponse +import software.amazon.awssdk.services.sqs.model.MessageSystemAttributeName +import software.amazon.awssdk.services.sqs.model.QueueAttributeName +import software.amazon.awssdk.services.sqs.model.ReceiveMessageRequest +import software.amazon.awssdk.services.sqs.model.ReceiveMessageResponse +import software.aws.toolkits.core.region.AwsRegion +import software.aws.toolkits.jetbrains.core.region.MockRegionProvider +import software.aws.toolkits.jetbrains.services.sqs.Queue +import software.aws.toolkits.jetbrains.utils.BaseCoroutineTest +import software.aws.toolkits.jetbrains.utils.waitForModelToBeAtLeast +import software.aws.toolkits.jetbrains.utils.waitForTrue +import software.aws.toolkits.resources.message +import javax.swing.JLabel + +class PollMessagePaneTest : BaseCoroutineTest() { + private lateinit var client: SqsClient + private lateinit var region: AwsRegion + private lateinit var queue: Queue + private lateinit var pane: PollMessagePane + private lateinit var table: MessagesTable + private lateinit var label: JLabel + + private val message: Message = Message.builder() + .body("ABC") + .messageId("XYZ") + .attributes(mapOf(Pair(MessageSystemAttributeName.SENDER_ID, "1234567890:test1"), Pair(MessageSystemAttributeName.SENT_TIMESTAMP, "111111111"))) + .build() + + @Before + fun reset() { + client = mockClientManagerRule.create() + region = MockRegionProvider.getInstance().defaultRegion() + queue = Queue("https://sqs.us-east-1.amazonaws.com/123456789012/test1", region) + pane = PollMessagePane(client, queue) + table = pane.messagesTable + label = pane.messagesAvailableLabel + } + + @Test + fun `Message received`() { + whenever(client.receiveMessage(Mockito.any())).thenReturn( + ReceiveMessageResponse.builder().messages(message).build() + ) + val tableModel = table.tableModel + runBlocking { + pane.requestMessages() + tableModel.waitForModelToBeAtLeast(1) + } + + assertThat(tableModel.items.first().messageId()).isEqualTo("XYZ") + assertThat(tableModel.items.first().body()).isEqualTo("ABC") + assertThat(tableModel.items.first().attributes().getValue(MessageSystemAttributeName.SENDER_ID)).isEqualTo("1234567890:test1") + assertThat(tableModel.items.first().attributes().getValue(MessageSystemAttributeName.SENT_TIMESTAMP)).isEqualTo("111111111") + } + + @Test + fun `No messages received`() { + whenever(client.receiveMessage(Mockito.any())).thenReturn( + ReceiveMessageResponse.builder().build() + ) + runBlocking { + pane.requestMessages() + waitForTrue { table.table.emptyText.text == message("sqs.message.no_messages") } + } + + assertThat(table.tableModel.items.size).isZero() + } + + @Test + fun `Error receiving messages`() { + whenever(client.receiveMessage(Mockito.any())).then { + throw IllegalStateException("Network Error") + } + runBlocking { + pane.requestMessages() + waitForTrue { table.table.emptyText.text == message("sqs.failed_to_poll_messages") } + } + + assertThat(table.tableModel.items.size).isZero() + } + + @Test + fun `Available messages are displayed`() { + whenever(client.getQueueAttributes(Mockito.any())).thenReturn( + GetQueueAttributesResponse.builder().attributes(mutableMapOf(Pair(QueueAttributeName.APPROXIMATE_NUMBER_OF_MESSAGES, "10"))).build() + ) + runBlocking { + pane.addTotal() + } + + assertThat(label.text).isEqualTo(message("sqs.messages.available.text") + "10") + } + + @Test + fun `Error displaying messages available`() { + whenever(client.getQueueAttributes(Mockito.any())).then { + throw IllegalStateException("Network Error") + } + runBlocking { + pane.addTotal() + } + + assertThat(label.text).isEqualTo(message("sqs.failed_to_load_total")) + } +} diff --git a/jetbrains-core/tst/software/aws/toolkits/jetbrains/services/sqs/toolwindow/PollMessageUtilsTest.kt b/jetbrains-core/tst/software/aws/toolkits/jetbrains/services/sqs/toolwindow/PollMessageUtilsTest.kt new file mode 100644 index 0000000000..57ec8a037b --- /dev/null +++ b/jetbrains-core/tst/software/aws/toolkits/jetbrains/services/sqs/toolwindow/PollMessageUtilsTest.kt @@ -0,0 +1,59 @@ +// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package software.aws.toolkits.jetbrains.services.sqs.toolwindow + +import com.intellij.testFramework.ProjectRule +import org.assertj.core.api.Assertions.assertThat +import org.junit.Rule +import org.junit.Test +import software.amazon.awssdk.services.sqs.model.Message +import software.amazon.awssdk.services.sqs.model.MessageSystemAttributeName +import software.aws.toolkits.core.utils.RuleUtils +import software.aws.toolkits.jetbrains.services.sqs.MAX_LENGTH_OF_POLLED_MESSAGES + +class PollMessageUtilsTest { + private val message1 = buildMessage(RuleUtils.randomName(length = 10)) + private val message2 = buildMessage(RuleUtils.randomName(length = 1024)) + private val message3 = buildMessage(RuleUtils.randomName(length = 1025)) + + @JvmField + @Rule + val projectRule = ProjectRule() + + @Test + fun `Message mapped to columns as expected`() { + val table = MessagesTable().apply { + tableModel.addRow(message1) + } + + assertThat(table.tableModel.items.size).isOne() + assertThat(table.tableModel.items.first().body()).isEqualTo(message1.body()) + assertThat(table.tableModel.items.first().messageId()).isEqualTo(message1.messageId()) + assertThat(table.tableModel.items.first().attributes().getValue(MessageSystemAttributeName.SENDER_ID)).isEqualTo("1234567890:test1") + assertThat(table.tableModel.items.first().attributes().getValue(MessageSystemAttributeName.SENT_TIMESTAMP)).isEqualTo("111111111") + } + + @Test + fun `Long message body is truncated`() { + // Message 1 has length below 1024 + val column1 = MessageBodyColumn().valueOf(message1) + // Message 2 has length of 1024 + val column2 = MessageBodyColumn().valueOf(message2) + // Message 3 has length of 1025 + val column3 = MessageBodyColumn().valueOf(message3) + + assertThat(column1?.length).isEqualTo(10) + assertThat(column2?.length).isEqualTo(MAX_LENGTH_OF_POLLED_MESSAGES) + assertThat(column3?.length).isEqualTo(MAX_LENGTH_OF_POLLED_MESSAGES) + } + + private fun buildMessage(body: String): Message { + val message = Message.builder() + .body(body) + .messageId("ABC") + .attributes(mapOf(Pair(MessageSystemAttributeName.SENDER_ID, "1234567890:test1"), Pair(MessageSystemAttributeName.SENT_TIMESTAMP, "111111111"))) + .build() + return message + } +} diff --git a/resources/resources/software/aws/toolkits/resources/localized_messages.properties b/resources/resources/software/aws/toolkits/resources/localized_messages.properties index 723a966749..4e2b7b7c16 100644 --- a/resources/resources/software/aws/toolkits/resources/localized_messages.properties +++ b/resources/resources/software/aws/toolkits/resources/localized_messages.properties @@ -699,7 +699,16 @@ settings.states.invalid.short=Unable to connect settings.states.validating=Validating connection to AWS... settings.states.validating.short=Validating connection settings.title=AWS Connection Settings +sqs.failed_to_load_total=Failed to load number of messages available +sqs.failed_to_poll_messages=Failed to poll messages +sqs.message.message_body=Message Body +sqs.message.message_id=Message ID +sqs.message.no_messages=No messages polled +sqs.message.sender_id=Sender ID +sqs.message.timestamp=Sent Timestamp +sqs.messages.available.text=Messages Available: sqs.poll.message=Poll for Messages +sqs.poll.warning.text=To view messages in the queue, poll for messages.

Once polled, the sample of messages remain in the queue and are not returned to subsequent receive requests for the duration of the visibility timeout.

sqs.queue.polled.messages=Polled Messages sqs.send.message=Send a Message sqs.toolwindow=SQS From 791e21a63805e48029e09c2b98149eab7bd28e10 Mon Sep 17 00:00:00 2001 From: manodnyab <66754471+manodnyab@users.noreply.github.com> Date: Thu, 30 Jul 2020 13:14:45 -0700 Subject: [PATCH 0005/1546] CloudWatch Logs Insights: Query Editor Form (#1948) * Query Editor opens, StartQuery executed * Query Editor opens, StartQuery executed * Query Editor opens, StartQuery executed * Gets query-id * Query executed, query-id obtained * Query executed, query-id obtained * Query executed, query-id obtained * Query Editor Relative time updated * Log Group Type updated * Start and end date converted to single object * Start and end date converted to single object * Changed message components, test names * Tests updated --- jetbrains-core/resources/META-INF/plugin.xml | 3 + .../logs/insights/AddRemoveLogGroupTable.kt | 49 +++++ .../cloudwatch/logs/insights/QueryEditor.kt | 63 ++++++ .../logs/insights/QueryEditorDialog.kt | 153 +++++++++++++++ .../cloudwatch/logs/insights/Queryeditor.form | 179 ++++++++++++++++++ .../logs/insights/QueryingLogGroups.kt | 26 +++ .../logs/insights/SelectedLogGroup.kt | 31 +++ .../logs/insights/actions/QueryGroupAction.kt | 15 ++ .../logs/insights/QueryingLogGroupsTest.kt | 137 ++++++++++++++ .../resources/localized_messages.properties | 16 ++ 10 files changed, 672 insertions(+) create mode 100644 jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/insights/AddRemoveLogGroupTable.kt create mode 100644 jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/insights/QueryEditor.kt create mode 100644 jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/insights/QueryEditorDialog.kt create mode 100644 jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/insights/Queryeditor.form create mode 100644 jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/insights/QueryingLogGroups.kt create mode 100644 jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/insights/SelectedLogGroup.kt create mode 100644 jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/insights/actions/QueryGroupAction.kt create mode 100644 jetbrains-core/tst/software/aws/toolkits/jetbrains/services/cloudwatch/logs/insights/QueryingLogGroupsTest.kt diff --git a/jetbrains-core/resources/META-INF/plugin.xml b/jetbrains-core/resources/META-INF/plugin.xml index 88b97b7585..e8a951487f 100644 --- a/jetbrains-core/resources/META-INF/plugin.xml +++ b/jetbrains-core/resources/META-INF/plugin.xml @@ -306,6 +306,9 @@ + + diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/insights/AddRemoveLogGroupTable.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/insights/AddRemoveLogGroupTable.kt new file mode 100644 index 0000000000..09f9005aa9 --- /dev/null +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/insights/AddRemoveLogGroupTable.kt @@ -0,0 +1,49 @@ +// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package software.aws.toolkits.jetbrains.services.cloudwatch.logs.insights +import com.intellij.execution.util.ListTableWithButtons +import com.intellij.openapi.project.Project +import com.intellij.util.ui.ListTableModel +import software.aws.toolkits.resources.message +import javax.swing.table.TableCellEditor + +class AddRemoveLogGroupTable(project: Project) : ListTableWithButtons () { + init { + // Currently shows a sample table + // TODO Display log entries, Add and Remove log groups + } + override fun cloneElement(variable: SelectedLogGroups): SelectedLogGroups = variable.copy() + override fun createElement(): SelectedLogGroups = SelectedLogGroups() + fun getSelLogGroups(): List = elements.toList() + override fun createListModel(): ListTableModel<*> = ListTableModel( + StringColInfo( + message("cloudwatch.logs.selected_log_groups"), + { it.logGroups }, + { mapping, value -> mapping.logGroups = value } + ) + ) + + private inner class StringColInfo( + name: String, + private val retrieveFunc: (SelectedLogGroups) -> String?, + private val setFunc: (SelectedLogGroups, String?) -> Unit, + private val editor: () -> TableCellEditor? = { null } + ) : ListTableWithButtons.ElementsColumnInfoBase(name) { + override fun valueOf(item: SelectedLogGroups): String? = retrieveFunc.invoke(item) + + override fun setValue(item: SelectedLogGroups, value: String?) { + if (value == valueOf(item)) { + return + } + setFunc.invoke(item, value) + setModified() + } + + override fun isCellEditable(item: SelectedLogGroups?): Boolean = false + override fun getDescription(element: SelectedLogGroups?): String? = null + } + + override fun canDeleteElement(selection: SelectedLogGroups?): Boolean = true + override fun isEmpty(element: SelectedLogGroups): Boolean = element.logGroups.isNullOrEmpty() +} diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/insights/QueryEditor.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/insights/QueryEditor.kt new file mode 100644 index 0000000000..fc42153db4 --- /dev/null +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/insights/QueryEditor.kt @@ -0,0 +1,63 @@ +// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +package software.aws.toolkits.jetbrains.services.cloudwatch.logs.insights + +import com.intellij.openapi.project.Project +import com.intellij.openapi.ui.ComboBox +import com.intellij.openapi.ui.SimpleToolWindowPanel +import com.michaelbaranov.microba.calendar.DatePicker +import software.aws.toolkits.resources.message +import javax.swing.JComboBox +import javax.swing.JPanel +import javax.swing.JRadioButton +import javax.swing.JLabel +import javax.swing.JTextArea +import javax.swing.JButton +import javax.swing.JTextField + +class QueryEditor internal constructor(private val project: Project) { + lateinit var absoluteTimeRadioButton: JRadioButton + lateinit var relativeTimeRadioButton: JRadioButton + lateinit var searchTerm: JRadioButton + lateinit var querySearchTerm: JTextField + lateinit var queryLogGroupsRadioButton: JRadioButton + var saveQueryButton: JButton? = null + lateinit var retrieveSavedQueriesButton: JButton + private lateinit var tablePanel: SimpleToolWindowPanel + lateinit var queryBox: JTextArea + lateinit var logGroupLabel: JLabel + lateinit var endDate: DatePicker + lateinit var queryEditorBasePanel: JPanel + lateinit var relativeTimeUnit: JComboBox<*> + lateinit var relativeTimeNumber: JTextField + lateinit var startDate: DatePicker + private lateinit var showLogGroupTable: AddRemoveLogGroupTable + + private fun initArLogGroupTable() { + showLogGroupTable.tableView.listTableModel + showLogGroupTable.getSelLogGroups() + } + + private fun createUIComponents() { + // TODO: place custom component creation code here + tablePanel = SimpleToolWindowPanel(false, true) + showLogGroupTable = AddRemoveLogGroupTable(project) + initArLogGroupTable() + tablePanel.setContent(showLogGroupTable.component) + val timeUnits = arrayOf( + message("cloudwatch.logs.time_minutes"), + message("cloudwatch.logs.time_hours"), + message("cloudwatch.logs.time_days"), + message("cloudwatch.logs.time_weeks")) + relativeTimeUnit = ComboBox(timeUnits) + } + + init { + startDate.isEnabled = false + endDate.isEnabled = false + relativeTimeNumber.isEnabled = false + relativeTimeUnit.isEnabled = false + querySearchTerm.isEnabled = false + queryBox.isEnabled = false + } +} diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/insights/QueryEditorDialog.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/insights/QueryEditorDialog.kt new file mode 100644 index 0000000000..7cb37f77b7 --- /dev/null +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/insights/QueryEditorDialog.kt @@ -0,0 +1,153 @@ +// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package software.aws.toolkits.jetbrains.services.cloudwatch.logs.insights + +import com.intellij.openapi.project.Project +import com.intellij.openapi.ui.DialogWrapper +import com.intellij.openapi.ui.ValidationInfo +import software.amazon.awssdk.services.cloudwatchlogs.CloudWatchLogsClient +import software.aws.toolkits.jetbrains.core.awsClient +import java.awt.event.ActionEvent +import javax.swing.Action +import javax.swing.JComponent +import software.aws.toolkits.resources.message +import java.time.temporal.ChronoUnit +import java.util.Calendar +import java.util.Date + +val relativeTimeUnit = mapOf( + message("cloudwatch.logs.time_minutes") to ChronoUnit.MINUTES, + message("cloudwatch.logs.time_hours") to ChronoUnit.HOURS, + message("cloudwatch.logs.time_days") to ChronoUnit.DAYS, + message("cloudwatch.logs.time_weeks") to ChronoUnit.WEEKS) + +class QueryEditorDialog( + private val project: Project, + private val lGroupName: String, + private val client: CloudWatchLogsClient +) : DialogWrapper(project) { + constructor(project: Project, logGroupName: String) : + this(project = project, lGroupName = logGroupName, client = project.awsClient()) + + private val view = QueryEditor(project) + private val queryingLogGroupApiCall = QueryingLogGroups(project) + private val action: OkAction = QueryLogGroupOkAction() + private val logGroupNames = listOf(lGroupName) + + init { + super.init() + title = message("cloudwatch.logs.query_editor_title") + view.absoluteTimeRadioButton.addActionListener { + view.startDate.isEnabled = true + view.endDate.isEnabled = true + view.relativeTimeNumber.isEnabled = false + view.relativeTimeUnit.setEnabled(false) + } + view.relativeTimeRadioButton.addActionListener { + view.startDate.isEnabled = false + view.endDate.isEnabled = false + view.relativeTimeNumber.isEnabled = true + view.relativeTimeUnit.setEnabled(true) + } + view.queryLogGroupsRadioButton.addActionListener { + view.queryBox.isEnabled = true + view.querySearchTerm.isEnabled = false + } + view.searchTerm.addActionListener { + view.queryBox.isEnabled = false + view.querySearchTerm.isEnabled = true + } + } + override fun createCenterPanel(): JComponent? = view.queryEditorBasePanel + override fun doValidate(): ValidationInfo? = validateEditorEntries(view) + override fun getOKAction(): Action = action + override fun doCancelAction() { + super.doCancelAction() + } + + override fun doOKAction() { + // Do nothing, close logic is handled separately + } + + private fun getCurrentTime() = Calendar.getInstance().toInstant() + + private fun getRelativeTime(unitOfTime: ChronoUnit?, relTimeNumber: Long): StartEndDate = StartEndDate(getCurrentTime().minus(relTimeNumber, unitOfTime), + getCurrentTime()) + + private fun getAbsoluteTime(startDate: Date, endDate: Date): StartEndDate = StartEndDate(startDate.toInstant(), endDate.toInstant()) + + private fun getFilterQuery(searchTerm: String): String { + if (searchTerm.contains("/")) { + val regexTerm = searchTerm.replace("/", "\\/") + return "fields @message, @timestamp | filter @message like /$regexTerm/" + } + return "fields @message, @timestamp | filter @message like /$searchTerm/" + } + + private fun beginQuerying() { + if (!okAction.isEnabled) { + return + } + val funDetails = getFunctionDetails() + val queryStartEndDate: StartEndDate + queryStartEndDate = (if (funDetails.absoluteTimeSelected) { + getAbsoluteTime(funDetails.startDateAbsolute, funDetails.endDateAbsolute) + } else { + getRelativeTime(relativeTimeUnit[funDetails.relativeTimeUnit], funDetails.relativeTimeNumber.toLong()) + }) + val query = if (funDetails.enterQuery) { + funDetails.query } else { + getFilterQuery(funDetails.searchTerm) + } + close(OK_EXIT_CODE) + queryingLogGroupApiCall.executeStartQuery(queryStartEndDate, funDetails.logGroupName, query, client) + } + + private fun getFunctionDetails(): QueryDetails = QueryDetails( + logGroupName = logGroupNames, + absoluteTimeSelected = view.absoluteTimeRadioButton.isSelected, + startDateAbsolute = view.startDate.date, + endDateAbsolute = view.endDate.date, + relativeTimeSelected = view.relativeTimeRadioButton.isSelected, + relativeTimeUnit = view.relativeTimeUnit.selectedItem.toString(), + relativeTimeNumber = view.relativeTimeNumber.text, + searchTermSelected = view.searchTerm.isSelected, + searchTerm = view.querySearchTerm.text, + enterQuery = view.queryLogGroupsRadioButton.isSelected, + query = view.queryBox.text + ) + + private inner class QueryLogGroupOkAction : OkAction() { + init { + putValue(Action.NAME, message("cloudwatch.logs.query.form.ok_Button")) + } + override fun doAction(e: ActionEvent?) { + super.doAction(e) + if (doValidateAll().isNotEmpty()) return + beginQuerying() + } + } + + public fun validateEditorEntries(view: QueryEditor): ValidationInfo? { + if (!view.absoluteTimeRadioButton.isSelected && !view.relativeTimeRadioButton.isSelected) { + return ValidationInfo(message("cloudwatch.logs.validation.timerange"), view.absoluteTimeRadioButton) + } + if (view.relativeTimeRadioButton.isSelected && view.relativeTimeNumber.text.isEmpty()) { + return ValidationInfo(message("cloudwatch.logs.no_relative_time_number"), view.relativeTimeNumber) + } + if (view.absoluteTimeRadioButton.isSelected && view.startDate.date > view.endDate.date) { + return ValidationInfo(message("cloudwatch.logs.compare.start.end.date"), view.startDate) + } + if (!view.queryLogGroupsRadioButton.isSelected && !view.searchTerm.isSelected) { + return ValidationInfo(message("cloudwatch.logs.no_query_selected"), view.searchTerm) + } + if (view.queryLogGroupsRadioButton.isSelected && view.queryBox.text.isEmpty()) { + return ValidationInfo(message("cloudwatch.logs.no_query_entered"), view.queryBox) + } + if (view.searchTerm.isSelected && view.querySearchTerm.text.isEmpty()) { + return ValidationInfo(message("cloudwatch.logs.no_term_entered"), view.querySearchTerm) + } + return null + } +} diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/insights/Queryeditor.form b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/insights/Queryeditor.form new file mode 100644 index 0000000000..53cf2e9b4f --- /dev/null +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/insights/Queryeditor.form @@ -0,0 +1,179 @@ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/insights/QueryingLogGroups.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/insights/QueryingLogGroups.kt new file mode 100644 index 0000000000..4f67c45b00 --- /dev/null +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/insights/QueryingLogGroups.kt @@ -0,0 +1,26 @@ +// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package software.aws.toolkits.jetbrains.services.cloudwatch.logs.insights + +import com.intellij.openapi.project.Project +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.launch +import software.amazon.awssdk.services.cloudwatchlogs.CloudWatchLogsClient +import software.amazon.awssdk.services.cloudwatchlogs.model.StartQueryRequest +import software.aws.toolkits.jetbrains.utils.ApplicationThreadPoolScope + +class QueryingLogGroups(private val project: Project) : CoroutineScope by ApplicationThreadPoolScope("ExecutingQuery") { + fun executeStartQuery(queryStartEndDate: StartEndDate, logGroupNames: List, query: String, client: CloudWatchLogsClient) = launch { + // TODO: Multiple log groups queried (currently only a single log group can be selected and queried) + val request = StartQueryRequest.builder() + .endTime(queryStartEndDate.endDate.epochSecond) + .logGroupName(logGroupNames[0]) + .queryString(query) + .startTime(queryStartEndDate.startDate.epochSecond) + .build() + val response = client.startQuery(request) + val queryId = response.queryId() + // TODO: Get the results of the query with qid + } +} diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/insights/SelectedLogGroup.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/insights/SelectedLogGroup.kt new file mode 100644 index 0000000000..2dd12aa543 --- /dev/null +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/insights/SelectedLogGroup.kt @@ -0,0 +1,31 @@ +// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package software.aws.toolkits.jetbrains.services.cloudwatch.logs.insights + +import java.time.Instant +import java.util.Date + +data class SelectedLogGroups( + // TODO Will be changed to List of string in future implementation + var logGroups: String? = null +) + +data class QueryDetails( + val logGroupName: List, + val absoluteTimeSelected: Boolean, + val startDateAbsolute: Date, + val endDateAbsolute: Date, + val relativeTimeSelected: Boolean, + val relativeTimeUnit: String, + val relativeTimeNumber: String, + val searchTermSelected: Boolean, + val searchTerm: String, + val enterQuery: Boolean, + val query: String +) + +data class StartEndDate( + val startDate: Instant, + val endDate: Instant +) diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/insights/actions/QueryGroupAction.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/insights/actions/QueryGroupAction.kt new file mode 100644 index 0000000000..68fa51aa12 --- /dev/null +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/insights/actions/QueryGroupAction.kt @@ -0,0 +1,15 @@ +// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package software.aws.toolkits.jetbrains.services.cloudwatch.logs.insights.actions + +import com.intellij.openapi.actionSystem.AnActionEvent +import com.intellij.openapi.project.DumbAware +import software.aws.toolkits.jetbrains.core.explorer.actions.SingleResourceNodeAction +import software.aws.toolkits.jetbrains.services.cloudwatch.logs.CloudWatchLogsNode +import software.aws.toolkits.jetbrains.services.cloudwatch.logs.insights.QueryEditorDialog +import software.aws.toolkits.resources.message + +class QueryGroupAction : SingleResourceNodeAction(message("cloudwatch.logs.query")), DumbAware { + override fun actionPerformed(selected: CloudWatchLogsNode, e: AnActionEvent) = QueryEditorDialog(selected.nodeProject, selected.logGroupName).show() +} diff --git a/jetbrains-core/tst/software/aws/toolkits/jetbrains/services/cloudwatch/logs/insights/QueryingLogGroupsTest.kt b/jetbrains-core/tst/software/aws/toolkits/jetbrains/services/cloudwatch/logs/insights/QueryingLogGroupsTest.kt new file mode 100644 index 0000000000..ee7502f21c --- /dev/null +++ b/jetbrains-core/tst/software/aws/toolkits/jetbrains/services/cloudwatch/logs/insights/QueryingLogGroupsTest.kt @@ -0,0 +1,137 @@ +// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package software.aws.toolkits.jetbrains.services.cloudwatch.logs.insights + +import com.intellij.testFramework.RunsInEdt +import com.intellij.testFramework.runInEdtAndWait +import org.junit.Rule +import org.junit.Test +import software.aws.toolkits.jetbrains.utils.rules.JavaCodeInsightTestFixtureRule +import org.assertj.core.api.Assertions.assertThat +import software.amazon.awssdk.services.cloudwatchlogs.CloudWatchLogsClient +import software.aws.toolkits.jetbrains.core.MockClientManagerRule +import software.aws.toolkits.resources.message +import java.util.Calendar +import java.util.Date + +@RunsInEdt +class QueryingLogGroupsTest { + @JvmField + @Rule + val projectRule = JavaCodeInsightTestFixtureRule() + + @JvmField + @Rule + val mockClientManagerRule = MockClientManagerRule(projectRule) + private lateinit var view: QueryEditor + private lateinit var validator: QueryEditorDialog + private lateinit var client: CloudWatchLogsClient + + @Test + fun `Absolute or relative time selected`() { + runInEdtAndWait { + getValidator() + getViewDetails(absoluteTime = false, relativeTime = false) + assertThat(validator.validateEditorEntries(view)?.message).contains(message("cloudwatch.logs.validation.timerange")) + } + } + + @Test + fun `Start date must be before end date`() { + runInEdtAndWait { + getValidator() + val cal = Calendar.getInstance() + cal.add(Calendar.DATE, -1) + getViewDetails(absoluteTime = true, startDate = Calendar.getInstance().time, endDate = cal.time) + assertThat(validator.validateEditorEntries(view)?.message).contains(message("cloudwatch.logs.compare.start.end.date")) + } + } + + @Test + fun `Relative Time, no time entered`() { + runInEdtAndWait { + getValidator() + getViewDetails(relativeTime = true, relativeTimeNumber = "") + assertThat(validator.validateEditorEntries(view)?.message).contains(message("cloudwatch.logs.no_relative_time_number")) + } + } + + @Test + fun `Neither Search Term nor Querying through log groups selected`() { + runInEdtAndWait { + getValidator() + getViewDetails(relativeTime = true) + assertThat(validator.validateEditorEntries(view)?.message).contains(message("cloudwatch.logs.no_query_selected")) + } + } + + @Test + fun `No search term entered`() { + runInEdtAndWait { + getValidator() + getViewDetails(relativeTime = true, querySearch = true, searchTerm = "") + assertThat(validator.validateEditorEntries(view)?.message).contains(message("cloudwatch.logs.no_term_entered")) + } + } + + @Test + fun `No query entered`() { + runInEdtAndWait { + getValidator() + getViewDetails(relativeTime = true, queryLogs = true, query = "") + assertThat(validator.validateEditorEntries(view)?.message).contains(message("cloudwatch.logs.no_query_entered")) + } + } + + @Test + fun `Path with relative time and queries correctly entered gets executed`() { + runInEdtAndWait { + getValidator() + getViewDetails(relativeTime = true, queryLogs = true, query = "fields @timestamp") + assertThat(validator.validateEditorEntries(view)?.message).isNull() + } + } + + @Test + fun `Path with absolute time and a search term entered gets executed`() { + runInEdtAndWait { + getValidator() + val cal = Calendar.getInstance() + cal.add(Calendar.DATE, -1) + getViewDetails(absoluteTime = true, endDate = Calendar.getInstance().time, startDate = cal.time, querySearch = true, searchTerm = "Error") + assertThat(validator.validateEditorEntries(view)?.message).isNull() + } + } + + private fun getViewDetails( + absoluteTime: Boolean = false, + relativeTime: Boolean = false, + startDate: Date = Calendar.getInstance().time, + endDate: Date = Calendar.getInstance().time, + relativeTimeUnit: String = "Minutes", + relativeTimeNumber: String = "1", + querySearch: Boolean = false, + queryLogs: Boolean = false, + searchTerm: String = "Example", + query: String = "Example Query" + ) { + view.relativeTimeRadioButton.isSelected = relativeTime + view.endDate.date = endDate + view.startDate.date = startDate + view.absoluteTimeRadioButton.isSelected = absoluteTime + view.relativeTimeUnit.selectedItem = relativeTimeUnit + view.relativeTimeNumber.text = relativeTimeNumber + view.queryLogGroupsRadioButton.isSelected = queryLogs + view.searchTerm.isSelected = querySearch + view.querySearchTerm.text = searchTerm + view.queryBox.text = query + } + + private fun getValidator() { + val project = projectRule.project + view = QueryEditor(project) + client = mockClientManagerRule.create() + validator = QueryEditorDialog(project, "log1", client) + } +} diff --git a/resources/resources/software/aws/toolkits/resources/localized_messages.properties b/resources/resources/software/aws/toolkits/resources/localized_messages.properties index 77052ad742..e244c8f4fd 100644 --- a/resources/resources/software/aws/toolkits/resources/localized_messages.properties +++ b/resources/resources/software/aws/toolkits/resources/localized_messages.properties @@ -191,6 +191,7 @@ cloudformation.update_stack.timeout=Failed to update stack {0} in {1} seconds. V cloudformation.yaml.invalid_root_type=Template does not start with a mapping: {0} cloudformation.yaml.too_many_documents=There should only be 1 YAML document per file: {0} cloudformation.yaml.too_many_files=Found {0} YAML files but only expected 1 +cloudwatch.logs.compare.start.end.date=Start date must be before end date cloudwatch.logs.copy_action=Copy {0, choice, 1#message|2#messages} cloudwatch.logs.delete_log_group=Delete Log Group cloudwatch.logs.download=Download Log Stream @@ -207,23 +208,38 @@ cloudwatch.logs.log_group_title=Group: {0} cloudwatch.logs.log_stream_does_not_exist=Log stream {0} does not exist cloudwatch.logs.log_stream_title=Stream: {0} cloudwatch.logs.log_streams=Log Streams +cloudwatch.logs.no_end_date=End Date must be specified cloudwatch.logs.no_events=No Events in Log Stream cloudwatch.logs.no_events_query=No Events matching the query found in Log Stream {0} cloudwatch.logs.no_log_streams=No Log Stream found +cloudwatch.logs.no_query_entered=Query must be specified +cloudwatch.logs.no_query_selected=Either a query or search term must be entered +cloudwatch.logs.no_relative_time_number=Number must be specified +cloudwatch.logs.no_start_date=Start Date must be specified +cloudwatch.logs.no_term_entered=Search Term must be specified cloudwatch.logs.open=View Log Streams cloudwatch.logs.open_in_editor=Open in Editor cloudwatch.logs.open_in_editor_failed=Open in editor failed cloudwatch.logs.opening_in_editor=Opening Stream {0} in editor +cloudwatch.logs.query=Open Query Editor +cloudwatch.logs.query.form.ok_Button=Apply +cloudwatch.logs.query_editor_title=Query Log Groups cloudwatch.logs.save_action=Save to a File cloudwatch.logs.saving_to_disk=Saving Stream {0} to disk cloudwatch.logs.saving_to_disk_failed=Saving Stream {0} failed cloudwatch.logs.saving_to_disk_succeeded=Finished saving Log Stream {0} to {1} +cloudwatch.logs.selected_log_groups=Log Groups Selected cloudwatch.logs.show_logs_around=Show Logs Around cloudwatch.logs.stream_save_to_file=Save {0} to a file cloudwatch.logs.stream_too_big=Stream is Too Large cloudwatch.logs.stream_too_big_message=Log stream {0} is too large to open in the editor cloudwatch.logs.tail=Tail logs +cloudwatch.logs.time_days=Days +cloudwatch.logs.time_hours=Hours +cloudwatch.logs.time_minutes=Minutes +cloudwatch.logs.time_weeks=Weeks cloudwatch.logs.toolwindow=CloudWatch Logs +cloudwatch.logs.validation.timerange=Time Range not Selected cloudwatch.logs.view_log_stream=View Log Stream cloudwatch.logs.view_log_streams=View Log Streams cloudwatch.logs.wrap=Wrap logs From d4b700a6520eb360e1d0778ce8ac8ce94efc5724 Mon Sep 17 00:00:00 2001 From: Ran Yoo <30319724+ranyoo2367@users.noreply.github.com> Date: Wed, 5 Aug 2020 15:13:10 -0400 Subject: [PATCH 0006/1546] SQS: Add send a message tab (#1967) * Send message pane setup & test * Added context help * Style changes * Message fix * PR changes: fifo fields and caching * Localization fix * PR revisions 2 --- jetbrains-core/resources/META-INF/plugin.xml | 1 - .../toolkits/jetbrains/services/sqs/Queue.kt | 1 + .../jetbrains/services/sqs/SqsUtils.kt | 1 + .../services/sqs/toolwindow/FifoPanel.form | 90 ++++++++++ .../services/sqs/toolwindow/FifoPanel.kt | 100 +++++++++++ .../sqs/toolwindow/SendMessagePane.form | 86 ++++++++- .../sqs/toolwindow/SendMessagePane.kt | 102 ++++++++++- .../services/sqs/toolwindow/SqsWindow.kt | 4 +- .../services/sqs/toolwindow/SqsWindowUI.kt | 7 +- .../sqs/toolwindow/SendMessagePaneTest.kt | 170 ++++++++++++++++++ .../resources/localized_messages.properties | 17 +- 11 files changed, 568 insertions(+), 11 deletions(-) create mode 100644 jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/toolwindow/FifoPanel.form create mode 100644 jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/toolwindow/FifoPanel.kt create mode 100644 jetbrains-core/tst/software/aws/toolkits/jetbrains/services/sqs/toolwindow/SendMessagePaneTest.kt diff --git a/jetbrains-core/resources/META-INF/plugin.xml b/jetbrains-core/resources/META-INF/plugin.xml index 39bd2bc598..8a68d3420d 100644 --- a/jetbrains-core/resources/META-INF/plugin.xml +++ b/jetbrains-core/resources/META-INF/plugin.xml @@ -348,7 +348,6 @@ - diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/Queue.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/Queue.kt index dbaf621318..ca6bb9e39b 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/Queue.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/Queue.kt @@ -28,4 +28,5 @@ class Queue(val queueUrl: String, val region: AwsRegion) { } val arn = "arn:${region.partitionId}:sqs:${region.id}:$accountId:$queueName" + val isFifo: Boolean by lazy { queueName.endsWith(".fifo") } } diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/SqsUtils.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/SqsUtils.kt index 9663c204f9..0e124c61f7 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/SqsUtils.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/SqsUtils.kt @@ -5,3 +5,4 @@ package software.aws.toolkits.jetbrains.services.sqs const val MAX_NUMBER_OF_POLLED_MESSAGES = 10 const val MAX_LENGTH_OF_POLLED_MESSAGES = 1024 +const val MAX_LENGTH_OF_FIFO_ID = 128 diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/toolwindow/FifoPanel.form b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/toolwindow/FifoPanel.form new file mode 100644 index 0000000000..0ab56a9469 --- /dev/null +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/toolwindow/FifoPanel.form @@ -0,0 +1,90 @@ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/toolwindow/FifoPanel.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/toolwindow/FifoPanel.kt new file mode 100644 index 0000000000..7709fe443c --- /dev/null +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/toolwindow/FifoPanel.kt @@ -0,0 +1,100 @@ +// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +package software.aws.toolkits.jetbrains.services.sqs.toolwindow + +import com.intellij.icons.AllIcons +import com.intellij.ide.HelpTooltip +import com.intellij.ui.components.JBTextField +import software.aws.toolkits.jetbrains.services.sqs.MAX_LENGTH_OF_FIFO_ID +import software.aws.toolkits.resources.message +import javax.swing.JLabel +import javax.swing.JPanel +import javax.swing.event.DocumentEvent +import javax.swing.event.DocumentListener + +class FifoPanel { + lateinit var component: JPanel + lateinit var deduplicationId: JBTextField + lateinit var groupId: JBTextField + lateinit var deduplicationErrorLabel: JLabel + lateinit var groupErrorLabel: JLabel + lateinit var deduplicationContextHelp: JLabel + lateinit var groupContextHelp: JLabel + + init { + loadComponents() + setFields() + } + + private fun loadComponents() { + deduplicationErrorLabel.isVisible = false + groupErrorLabel.isVisible = false + + deduplicationContextHelp.icon = AllIcons.General.ContextHelp + HelpTooltip().apply { + setDescription(message("sqs.message.deduplication_id.tooltip")) + installOn(deduplicationContextHelp) + } + groupContextHelp.icon = AllIcons.General.ContextHelp + HelpTooltip().apply { + setDescription(message("sqs.message.group_id.tooltip")) + installOn(groupContextHelp) + } + } + + private fun setFields() { + deduplicationId.apply { + emptyText.text = message("sqs.required.empty.text") + document.addDocumentListener(createListener(this, deduplicationErrorLabel)) + } + + groupId.apply { + emptyText.text = message("sqs.required.empty.text") + document.addDocumentListener(createListener(this, groupErrorLabel)) + } + } + + fun validateFields(): Boolean { + var deduplicationIsValid = true + var groupIsValid = true + + if (deduplicationId.text.length > MAX_LENGTH_OF_FIFO_ID) { + deduplicationErrorLabel.text = message("sqs.message.validation.long.id") + deduplicationErrorLabel.isVisible = true + deduplicationIsValid = false + } else if (deduplicationId.text.isEmpty()) { + deduplicationErrorLabel.text = message("sqs.message.validation.empty.deduplication_id") + deduplicationErrorLabel.isVisible = true + deduplicationIsValid = false + } + + if (groupId.text.length > MAX_LENGTH_OF_FIFO_ID) { + groupErrorLabel.text = message("sqs.message.validation.long.id") + groupErrorLabel.isVisible = true + groupIsValid = false + } else if (groupId.text.isEmpty()) { + groupErrorLabel.text = message("sqs.message.validation.empty.group_id") + groupErrorLabel.isVisible = true + groupIsValid = false + } + + return (deduplicationIsValid && groupIsValid) + } + + private fun createListener(textField: JBTextField, errorLabel: JLabel): DocumentListener = object : DocumentListener { + override fun changedUpdate(e: DocumentEvent?) {} + override fun insertUpdate(e: DocumentEvent?) { + if (textField.text.length > MAX_LENGTH_OF_FIFO_ID) { + errorLabel.text = message("sqs.message.validation.long.id") + errorLabel.isVisible = true + } else { + errorLabel.isVisible = false + } + } + override fun removeUpdate(e: DocumentEvent?) { + if (textField.text.length <= MAX_LENGTH_OF_FIFO_ID) { + errorLabel.isVisible = false + } + } + } +} diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/toolwindow/SendMessagePane.form b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/toolwindow/SendMessagePane.form index 2c6afb6d35..39b1a0ba64 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/toolwindow/SendMessagePane.form +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/toolwindow/SendMessagePane.form @@ -1,12 +1,90 @@
- - + + - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/toolwindow/SendMessagePane.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/toolwindow/SendMessagePane.kt index 0145d706b1..5071886553 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/toolwindow/SendMessagePane.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/toolwindow/SendMessagePane.kt @@ -2,8 +2,108 @@ // SPDX-License-Identifier: Apache-2.0 package software.aws.toolkits.jetbrains.services.sqs.toolwindow +import com.intellij.ide.util.PropertiesComponent +import com.intellij.ui.components.JBTextArea +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext +import software.amazon.awssdk.services.sqs.SqsClient +import software.aws.toolkits.jetbrains.services.sqs.Queue +import software.aws.toolkits.jetbrains.utils.ApplicationThreadPoolScope +import software.aws.toolkits.resources.message +import java.awt.event.KeyEvent +import java.awt.event.KeyListener +import javax.swing.JButton +import javax.swing.JLabel import javax.swing.JPanel -class SendMessagePane { +class SendMessagePane( + private val client: SqsClient, + private val queue: Queue, + private val messageCache: PropertiesComponent +) : CoroutineScope by ApplicationThreadPoolScope("SendMessagePane") { lateinit var component: JPanel + lateinit var inputText: JBTextArea + lateinit var sendButton: JButton + lateinit var clearButton: JButton + lateinit var bodyErrorLabel: JLabel + lateinit var confirmationLabel: JLabel + lateinit var fifoFields: FifoPanel + + init { + loadComponents() + setButtons() + setFields() + } + + private fun loadComponents() { + if (!queue.isFifo) { + fifoFields.component.isVisible = false + } + bodyErrorLabel.isVisible = false + confirmationLabel.isVisible = false + } + + private fun setButtons() { + sendButton.addActionListener { + launch { sendMessage() } + } + clearButton.addActionListener { + inputText.text = "" + fifoFields.deduplicationId.text = "" + fifoFields.groupId.text = "" + messageCache.setValue(queue.queueUrl, "") + confirmationLabel.isVisible = false + } + } + + private fun setFields() { + inputText.apply { + emptyText.text = message("sqs.send.message.body.empty.text") + text = messageCache.getValue(queue.queueUrl) + addKeyListener(object : KeyListener { + override fun keyTyped(e: KeyEvent?) { + if (bodyErrorLabel.isVisible) { + bodyErrorLabel.isVisible = false + } + } + override fun keyPressed(e: KeyEvent?) {} + override fun keyReleased(e: KeyEvent?) {} + }) + } + } + + suspend fun sendMessage() { + if (validateFields()) { + try { + withContext(Dispatchers.IO) { + val messageId = client.sendMessage { + it.queueUrl(queue.queueUrl) + it.messageBody(inputText.text) + if (queue.isFifo) { + it.messageDeduplicationId(fifoFields.deduplicationId.text) + it.messageGroupId(fifoFields.groupId.text) + } + }.messageId() + confirmationLabel.text = message("sqs.send.message.success", messageId) + } + messageCache.setValue(queue.queueUrl, inputText.text) + } catch (e: Exception) { + confirmationLabel.text = message("sqs.failed_to_send_message") + } + confirmationLabel.isVisible = true + } + } + + private fun validateFields(): Boolean { + val inputIsValid = inputText.text.isNotEmpty() + bodyErrorLabel.isVisible = !inputIsValid + + return if (queue.isFifo) { + (fifoFields.validateFields() && inputIsValid) + } else { + inputIsValid + } + } } diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/toolwindow/SqsWindow.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/toolwindow/SqsWindow.kt index 208af75248..d2b6c74806 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/toolwindow/SqsWindow.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/toolwindow/SqsWindow.kt @@ -26,11 +26,11 @@ class SqsWindow(private val project: Project) : CoroutineScope by ApplicationThr private val client: SqsClient = project.awsClient() fun pollMessage(queue: Queue) { - showQueue(queue, SqsWindowUI(client, queue).apply { pollMessage() }) + showQueue(queue, SqsWindowUI(project, client, queue).apply { pollMessage() }) } fun sendMessage(queue: Queue) { - showQueue(queue, SqsWindowUI(client, queue).apply { sendMessage() }) + showQueue(queue, SqsWindowUI(project, client, queue).apply { sendMessage() }) } private fun showQueue(queue: Queue, component: SqsWindowUI) = launch { diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/toolwindow/SqsWindowUI.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/toolwindow/SqsWindowUI.kt index 663635b080..694386cb62 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/toolwindow/SqsWindowUI.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/toolwindow/SqsWindowUI.kt @@ -3,22 +3,25 @@ package software.aws.toolkits.jetbrains.services.sqs.toolwindow +import com.intellij.ide.util.PropertiesComponent +import com.intellij.openapi.project.Project import com.intellij.ui.components.JBTabbedPane import com.intellij.util.ui.JBUI import software.amazon.awssdk.services.sqs.SqsClient import software.aws.toolkits.jetbrains.services.sqs.Queue import software.aws.toolkits.resources.message -// Will add more parameters once window is populated class SqsWindowUI( + private val project: Project, private val client: SqsClient, val queue: Queue ) { + val messageCache: PropertiesComponent = PropertiesComponent.getInstance(project) val mainPanel = JBTabbedPane().apply { tabComponentInsets = JBUI.emptyInsets() border = JBUI.Borders.empty() add(message("sqs.queue.polled.messages"), PollMessagePane(client, queue).component) - add(message("sqs.send.message"), SendMessagePane().component) + add(message("sqs.send.message"), SendMessagePane(client, queue, messageCache).component) } fun pollMessage() { diff --git a/jetbrains-core/tst/software/aws/toolkits/jetbrains/services/sqs/toolwindow/SendMessagePaneTest.kt b/jetbrains-core/tst/software/aws/toolkits/jetbrains/services/sqs/toolwindow/SendMessagePaneTest.kt new file mode 100644 index 0000000000..160325f1b0 --- /dev/null +++ b/jetbrains-core/tst/software/aws/toolkits/jetbrains/services/sqs/toolwindow/SendMessagePaneTest.kt @@ -0,0 +1,170 @@ +// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package software.aws.toolkits.jetbrains.services.sqs.toolwindow + +import com.intellij.ide.util.PropertiesComponent +import com.nhaarman.mockitokotlin2.whenever +import kotlinx.coroutines.runBlocking +import org.assertj.core.api.Assertions.assertThat +import org.junit.Before +import org.junit.Test +import org.mockito.Mockito +import software.amazon.awssdk.services.sqs.SqsClient +import software.amazon.awssdk.services.sqs.model.SendMessageRequest +import software.amazon.awssdk.services.sqs.model.SendMessageResponse +import software.aws.toolkits.core.region.AwsRegion +import software.aws.toolkits.core.utils.RuleUtils +import software.aws.toolkits.jetbrains.core.region.MockRegionProvider +import software.aws.toolkits.jetbrains.services.sqs.MAX_LENGTH_OF_FIFO_ID +import software.aws.toolkits.jetbrains.services.sqs.Queue +import software.aws.toolkits.jetbrains.utils.BaseCoroutineTest +import software.aws.toolkits.jetbrains.utils.waitForTrue +import software.aws.toolkits.resources.message + +class SendMessagePaneTest : BaseCoroutineTest() { + private lateinit var client: SqsClient + private lateinit var region: AwsRegion + private lateinit var standardQueue: Queue + private lateinit var fifoQueue: Queue + private lateinit var standardPane: SendMessagePane + private lateinit var fifoPane: SendMessagePane + private lateinit var cache: PropertiesComponent + + @Before + fun reset() { + client = mockClientManagerRule.create() + region = MockRegionProvider.getInstance().defaultRegion() + standardQueue = Queue("https://sqs.us-east-1.amazonaws.com/123456789012/standard", region) + fifoQueue = Queue("https://sqs.us-east-1.amazonaws.com/123456789012/fifo.fifo", region) + cache = PropertiesComponent.getInstance(projectRule.project) + standardPane = SendMessagePane(client, standardQueue, cache) + fifoPane = SendMessagePane(client, fifoQueue, cache) + } + + @Test + fun `No input fails to send for standard`() { + standardPane.apply { + inputText.text = "" + runBlocking { sendMessage() } + } + + assertThat(standardPane.bodyErrorLabel.isVisible).isTrue() + assertThat(standardPane.fifoFields.component.isVisible).isFalse() + } + + @Test + fun `No input fails to send for fifo`() { + fifoPane.apply { + inputText.text = "" + fifoFields.deduplicationId.text = "" + fifoFields.groupId.text = "" + runBlocking { sendMessage() } + } + + assertThat(fifoPane.bodyErrorLabel.isVisible).isTrue() + assertThat(fifoPane.fifoFields.deduplicationErrorLabel.isVisible).isTrue() + assertThat(fifoPane.fifoFields.groupErrorLabel.isVisible).isTrue() + } + + @Test + fun `No deduplication ID fails to send for fifo`() { + fifoPane.apply { + inputText.text = MESSAGE + fifoFields.deduplicationId.text = "" + fifoFields.groupId.text = GROUP_ID + runBlocking { sendMessage() } + } + + assertThat(fifoPane.bodyErrorLabel.isVisible).isFalse() + assertThat(fifoPane.fifoFields.deduplicationErrorLabel.isVisible).isTrue() + assertThat(fifoPane.fifoFields.groupErrorLabel.isVisible).isFalse() + } + + @Test + fun `No group ID fails to send for fifo`() { + fifoPane.apply { + inputText.text = MESSAGE + fifoFields.deduplicationId.text = DEDUPLICATION_ID + fifoFields.groupId.text = "" + runBlocking { sendMessage() } + } + + assertThat(fifoPane.bodyErrorLabel.isVisible).isFalse() + assertThat(fifoPane.fifoFields.deduplicationErrorLabel.isVisible).isFalse() + assertThat(fifoPane.fifoFields.groupErrorLabel.isVisible).isTrue() + } + + @Test + fun `ID too long fails`() { + fifoPane.apply { + inputText.text = MESSAGE + fifoFields.deduplicationId.text = RuleUtils.randomName(length = MAX_LENGTH_OF_FIFO_ID + 1) + fifoFields.groupId.text = GROUP_ID + runBlocking { sendMessage() } + } + + assertThat(fifoPane.bodyErrorLabel.isVisible).isFalse() + assertThat(fifoPane.fifoFields.deduplicationErrorLabel.isVisible).isTrue() + assertThat(fifoPane.fifoFields.groupErrorLabel.isVisible).isFalse() + assertThat(fifoPane.fifoFields.deduplicationErrorLabel.text).isEqualTo(message("sqs.message.validation.long.id")) + } + + @Test + fun `Error sending message`() { + whenever(client.sendMessage(Mockito.any())).then { + throw IllegalStateException("Network Error") + } + + standardPane.apply { + inputText.text = MESSAGE + runBlocking { + sendMessage() + waitForTrue { standardPane.confirmationLabel.isVisible } + } + } + + assertThat(standardPane.confirmationLabel.text).isEqualTo(message("sqs.failed_to_send_message")) + } + + @Test + fun `Message sent for standard`() { + whenever(client.sendMessage(Mockito.any())).thenReturn( + SendMessageResponse.builder().messageId(MESSAGE_ID).build() + ) + standardPane.apply { + inputText.text = MESSAGE + runBlocking { + sendMessage() + waitForTrue { standardPane.confirmationLabel.isVisible } + } + } + + assertThat(standardPane.confirmationLabel.text).isEqualTo(message("sqs.send.message.success", MESSAGE_ID)) + } + + @Test + fun `Message sent for fifo`() { + whenever(client.sendMessage(Mockito.any())).thenReturn( + SendMessageResponse.builder().messageId(MESSAGE_ID).build() + ) + fifoPane.apply { + inputText.text = MESSAGE + fifoFields.deduplicationId.text = DEDUPLICATION_ID + fifoFields.groupId.text = GROUP_ID + runBlocking { + sendMessage() + waitForTrue { fifoPane.confirmationLabel.isVisible } + } + } + + assertThat(fifoPane.confirmationLabel.text).isEqualTo(message("sqs.send.message.success", MESSAGE_ID)) + } + + private companion object { + const val MESSAGE_ID = "123" + const val MESSAGE = "Message body" + const val DEDUPLICATION_ID = "Deduplication ID" + const val GROUP_ID = "Group Id" + } +} diff --git a/resources/resources/software/aws/toolkits/resources/localized_messages.properties b/resources/resources/software/aws/toolkits/resources/localized_messages.properties index 4e2b7b7c16..f81607fd53 100644 --- a/resources/resources/software/aws/toolkits/resources/localized_messages.properties +++ b/resources/resources/software/aws/toolkits/resources/localized_messages.properties @@ -701,15 +701,30 @@ settings.states.validating.short=Validating connection settings.title=AWS Connection Settings sqs.failed_to_load_total=Failed to load number of messages available sqs.failed_to_poll_messages=Failed to poll messages +sqs.failed_to_send_message=Failed to send message +sqs.message.deduplication_id.tooltip=

Used for deduplication of sent messages.

If a message with a particular message deduplication ID is sent successfully, any messages sent with the same message deduplication ID are accepted successfully but aren't delivered during the 5-minute deduplication interval.

+sqs.message.group_id.tooltip=

Specifies that a message belongs to a specific message group.

Messages that belong to the same message group are always processed in a strict order relative to the message group.

sqs.message.message_body=Message Body sqs.message.message_id=Message ID sqs.message.no_messages=No messages polled sqs.message.sender_id=Sender ID sqs.message.timestamp=Sent Timestamp -sqs.messages.available.text=Messages Available: +sqs.message.validation.empty.deduplication_id=Please enter deduplication ID. +sqs.message.validation.empty.group_id=Please enter group ID. +sqs.message.validation.empty.message.body=Please enter message body. +sqs.message.validation.long.id=ID exceeds the maximum length of 128 characters. +sqs.messages.available.text=Messages Available: sqs.poll.message=Poll for Messages sqs.poll.warning.text=To view messages in the queue, poll for messages.

Once polled, the sample of messages remain in the queue and are not returned to subsequent receive requests for the duration of the visibility timeout.

sqs.queue.polled.messages=Polled Messages +sqs.required.empty.text=(Required) sqs.send.message=Send a Message +sqs.send.message.body=Message Body: +sqs.send.message.body.empty.text=Enter message body +sqs.send.message.clear.button=Clear +sqs.send.message.deduplication_id=Message Deduplication ID: +sqs.send.message.group_id=Message Group ID: +sqs.send.message.send.button=Send +sqs.send.message.success=Sent message ID: {0} sqs.toolwindow=SQS sqs.url.parse_error=Error parsing queue URL From 55835740c0069d03104a6293993a97d83ec01906 Mon Sep 17 00:00:00 2001 From: manodnyab <66754471+manodnyab@users.noreply.github.com> Date: Fri, 7 Aug 2020 08:16:01 -0700 Subject: [PATCH 0007/1546] CloudWatch Logs Insights : Save Query (#1968) * SaveQuery * Save Query updated * Save Query, added successmessage * Added tests for API,Save Query Error message updated * SaveQueryTest * SaveQueryTest * Save Query * Save Query API test updated Co-authored-by: Hunter Werlla --- jetbrains-core/resources/META-INF/plugin.xml | 1 - .../logs/insights/EnterQueryName.form | 21 +++++ .../logs/insights/EnterQueryName.kt | 12 +++ .../cloudwatch/logs/insights/QueryEditor.kt | 4 +- .../logs/insights/QueryEditorDialog.kt | 7 ++ .../logs/insights/QueryEditorUtils.kt | 6 ++ .../logs/insights/SaveQueryDialog.kt | 79 ++++++++++++++++++ .../cloudwatch/logs/insights/SaveQueryTest.kt | 80 +++++++++++++++++++ .../resources/localized_messages.properties | 8 +- 9 files changed, 215 insertions(+), 3 deletions(-) create mode 100644 jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/insights/EnterQueryName.form create mode 100644 jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/insights/EnterQueryName.kt create mode 100644 jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/insights/QueryEditorUtils.kt create mode 100644 jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/insights/SaveQueryDialog.kt create mode 100644 jetbrains-core/tst/software/aws/toolkits/jetbrains/services/cloudwatch/logs/insights/SaveQueryTest.kt diff --git a/jetbrains-core/resources/META-INF/plugin.xml b/jetbrains-core/resources/META-INF/plugin.xml index 630b0da5b3..607d969ad0 100644 --- a/jetbrains-core/resources/META-INF/plugin.xml +++ b/jetbrains-core/resources/META-INF/plugin.xml @@ -304,7 +304,6 @@ - diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/insights/EnterQueryName.form b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/insights/EnterQueryName.form new file mode 100644 index 0000000000..8da2df3981 --- /dev/null +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/insights/EnterQueryName.form @@ -0,0 +1,21 @@ + +
+ + + + + + + + + + + + + + + + + + +
diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/insights/EnterQueryName.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/insights/EnterQueryName.kt new file mode 100644 index 0000000000..59aa211937 --- /dev/null +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/insights/EnterQueryName.kt @@ -0,0 +1,12 @@ +// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +package software.aws.toolkits.jetbrains.services.cloudwatch.logs.insights + +import com.intellij.openapi.project.Project +import javax.swing.JPanel +import javax.swing.JTextField + +class EnterQueryName(project: Project) { + lateinit var queryName: JTextField + lateinit var saveQueryPanel: JPanel +} diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/insights/QueryEditor.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/insights/QueryEditor.kt index fc42153db4..a4de75e35e 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/insights/QueryEditor.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/insights/QueryEditor.kt @@ -21,7 +21,7 @@ class QueryEditor internal constructor(private val project: Project) { lateinit var searchTerm: JRadioButton lateinit var querySearchTerm: JTextField lateinit var queryLogGroupsRadioButton: JRadioButton - var saveQueryButton: JButton? = null + lateinit var saveQueryButton: JButton lateinit var retrieveSavedQueriesButton: JButton private lateinit var tablePanel: SimpleToolWindowPanel lateinit var queryBox: JTextArea @@ -59,5 +59,7 @@ class QueryEditor internal constructor(private val project: Project) { relativeTimeUnit.isEnabled = false querySearchTerm.isEnabled = false queryBox.isEnabled = false + saveQueryButton.isEnabled = false + queryBox.text = default_query } } diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/insights/QueryEditorDialog.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/insights/QueryEditorDialog.kt index 7cb37f77b7..1747d3927b 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/insights/QueryEditorDialog.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/insights/QueryEditorDialog.kt @@ -53,10 +53,17 @@ class QueryEditorDialog( view.queryLogGroupsRadioButton.addActionListener { view.queryBox.isEnabled = true view.querySearchTerm.isEnabled = false + view.saveQueryButton.isEnabled = true } view.searchTerm.addActionListener { view.queryBox.isEnabled = false view.querySearchTerm.isEnabled = true + view.saveQueryButton.isEnabled = false + } + + view.saveQueryButton.addActionListener { + val query = if (view.queryBox.text.isNotEmpty()) view.queryBox.text else default_query + SaveQueryDialog(project, query, logGroupNames).show() } } override fun createCenterPanel(): JComponent? = view.queryEditorBasePanel diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/insights/QueryEditorUtils.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/insights/QueryEditorUtils.kt new file mode 100644 index 0000000000..5b93c6a8bf --- /dev/null +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/insights/QueryEditorUtils.kt @@ -0,0 +1,6 @@ +// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package software.aws.toolkits.jetbrains.services.cloudwatch.logs.insights + +const val default_query = "fields @timestamp, @message| sort @timestamp desc| limit 20" diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/insights/SaveQueryDialog.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/insights/SaveQueryDialog.kt new file mode 100644 index 0000000000..075cbd293d --- /dev/null +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/insights/SaveQueryDialog.kt @@ -0,0 +1,79 @@ +// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package software.aws.toolkits.jetbrains.services.cloudwatch.logs.insights + +import com.intellij.openapi.project.Project +import com.intellij.openapi.ui.DialogWrapper +import com.intellij.openapi.ui.ValidationInfo +import kotlinx.coroutines.CoroutineScope +import software.amazon.awssdk.services.cloudwatchlogs.CloudWatchLogsClient +import software.amazon.awssdk.services.cloudwatchlogs.model.PutQueryDefinitionRequest +import software.aws.toolkits.jetbrains.core.awsClient +import software.aws.toolkits.resources.message +import java.awt.event.ActionEvent +import javax.swing.Action +import javax.swing.JComponent +import kotlinx.coroutines.launch +import software.amazon.awssdk.services.cloudwatchlogs.model.DescribeQueryDefinitionsRequest +import software.aws.toolkits.jetbrains.utils.ApplicationThreadPoolScope +import software.aws.toolkits.jetbrains.utils.notifyError +import software.aws.toolkits.jetbrains.utils.notifyInfo + +class SaveQueryDialog( + private val project: Project, + private val query: String, + private val logGroups: List, + private val client: CloudWatchLogsClient = project.awsClient() +) : DialogWrapper(project), CoroutineScope by ApplicationThreadPoolScope("SavingQuery") { + + val view = EnterQueryName(project) + private val action: OkAction = object : OkAction() { + init { + putValue(Action.NAME, message("cloudwatch.logs.save_query")) + } + override fun doAction(e: ActionEvent?) { + super.doAction(e) + if (doValidateAll().isNotEmpty()) return + saveQuery() + + close(OK_EXIT_CODE) + } + } + init { + super.init() + title = message("cloudwatch.logs.save_query_dialog_name") + } + + override fun createCenterPanel(): JComponent? = view.saveQueryPanel + override fun doValidate(): ValidationInfo? = validateQueryName(view) + override fun getOKAction(): Action = action + + fun checkQueryName(queryName: String): Boolean { + val request = DescribeQueryDefinitionsRequest.builder().queryDefinitionNamePrefix(queryName).build() + val response = client.describeQueryDefinitions(request) + return response.queryDefinitions().isEmpty() + } + + fun saveQuery() = launch { + try { + val queryName = view.queryName.text + if (checkQueryName(queryName)) { + val request = PutQueryDefinitionRequest.builder().logGroupNames(logGroups).name(queryName).queryString(query).build() + val response = client.putQueryDefinition(request) + notifyInfo(message("cloudwatch.logs.saved_query_status"), message("cloudwatch.logs.query_saved_successfully"), project) + } else { + notifyError(message("cloudwatch.logs.saved_query_status"), message("cloudwatch.logs.query_not_saved")) + } + } catch (e: Exception) { + notifyError(message("cloudwatch.logs.saved_query_status"), e.toString()) + } + } + + fun validateQueryName(view: EnterQueryName): ValidationInfo? { + if (view.queryName.text.isEmpty()) { + return ValidationInfo(message("cloudwatch.logs.query_name"), view.queryName) + } + return null + } +} diff --git a/jetbrains-core/tst/software/aws/toolkits/jetbrains/services/cloudwatch/logs/insights/SaveQueryTest.kt b/jetbrains-core/tst/software/aws/toolkits/jetbrains/services/cloudwatch/logs/insights/SaveQueryTest.kt new file mode 100644 index 0000000000..db27ece6a5 --- /dev/null +++ b/jetbrains-core/tst/software/aws/toolkits/jetbrains/services/cloudwatch/logs/insights/SaveQueryTest.kt @@ -0,0 +1,80 @@ +// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package software.aws.toolkits.jetbrains.services.cloudwatch.logs.insights + +import com.intellij.testFramework.RunsInEdt +import com.intellij.testFramework.runInEdtAndWait +import com.nhaarman.mockitokotlin2.argumentCaptor +import com.nhaarman.mockitokotlin2.doReturn +import com.nhaarman.mockitokotlin2.stub +import org.junit.Rule +import org.junit.Test +import software.aws.toolkits.jetbrains.utils.rules.JavaCodeInsightTestFixtureRule +import software.aws.toolkits.resources.message +import org.assertj.core.api.Assertions.assertThat +import software.amazon.awssdk.services.cloudwatchlogs.CloudWatchLogsClient +import software.amazon.awssdk.services.cloudwatchlogs.model.QueryDefinition +import software.amazon.awssdk.services.cloudwatchlogs.model.PutQueryDefinitionRequest +import software.amazon.awssdk.services.cloudwatchlogs.model.PutQueryDefinitionResponse +import software.amazon.awssdk.services.cloudwatchlogs.model.DescribeQueryDefinitionsRequest +import software.amazon.awssdk.services.cloudwatchlogs.model.DescribeQueryDefinitionsResponse +import software.aws.toolkits.jetbrains.core.MockClientManagerRule + +@RunsInEdt +class SaveQueryTest { + @JvmField + @Rule + val projectRule = JavaCodeInsightTestFixtureRule() + @JvmField + @Rule + val mockClientManagerRule = MockClientManagerRule(projectRule) + private lateinit var client: CloudWatchLogsClient + private lateinit var view: EnterQueryName + private lateinit var validator: SaveQueryDialog + + @Test + fun `Query name not entered, error message displayed`() { + runInEdtAndWait { + val project = projectRule.project + view = EnterQueryName(project) + client = mockClientManagerRule.create() + validator = SaveQueryDialog(project, "fields @timestamp", listOf("log1"), client) + view.queryName.text = "" + assertThat(validator.validateQueryName(view)?.message).contains(message("cloudwatch.logs.query_name")) + } + } + + @Test + fun `Path with correctly entered Query name returns no validation error`() { + runInEdtAndWait { + val project = projectRule.project + view = EnterQueryName(project) + client = mockClientManagerRule.create() + validator = SaveQueryDialog(project, "fields @timestamp", listOf("log1"), client) + view.queryName.text = "TrialQuery" + assertThat(validator.validateQueryName(view)?.message).isNull() + } + } + + @Test + fun `Save query API Call test`() { + val putQueryDefinitionCaptor = argumentCaptor() + val describeQueryDefinitionCaptor = argumentCaptor() + client = mockClientManagerRule.create() + val testQueryDefinition = QueryDefinition.builder().name("SampleQuery").queryDefinitionId("1234").build() + client.stub { + on { putQueryDefinition(putQueryDefinitionCaptor.capture()) } doReturn PutQueryDefinitionResponse.builder().queryDefinitionId("1234").build() + } + client.stub { + on { describeQueryDefinitions(describeQueryDefinitionCaptor.capture()) } doReturn + DescribeQueryDefinitionsResponse.builder().queryDefinitions(testQueryDefinition).build() + } + lateinit var dialog: SaveQueryDialog + runInEdtAndWait { + dialog = SaveQueryDialog(project = projectRule.project, query = "fields @timestamp", logGroups = listOf("log1"), client = client) + dialog.saveQuery() + assertThat(dialog.checkQueryName("SampleQuery")).isFalse() + } + } +} diff --git a/resources/resources/software/aws/toolkits/resources/localized_messages.properties b/resources/resources/software/aws/toolkits/resources/localized_messages.properties index 02efc2fd36..cffe8640e8 100644 --- a/resources/resources/software/aws/toolkits/resources/localized_messages.properties +++ b/resources/resources/software/aws/toolkits/resources/localized_messages.properties @@ -222,9 +222,15 @@ cloudwatch.logs.open_in_editor=Open in Editor cloudwatch.logs.open_in_editor_failed=Open in editor failed cloudwatch.logs.opening_in_editor=Opening Stream {0} in editor cloudwatch.logs.query=Open Query Editor -cloudwatch.logs.query.form.ok_Button=Apply +cloudwatch.logs.query.form.ok_Button=Execute cloudwatch.logs.query_editor_title=Query Log Groups +cloudwatch.logs.query_name=Query name is missing +cloudwatch.logs.query_not_saved=Query could not be saved. Try saving with a different name. +cloudwatch.logs.query_saved_successfully=Query has been saved to your account! cloudwatch.logs.save_action=Save to a File +cloudwatch.logs.save_query=Save Query +cloudwatch.logs.save_query_dialog_name=Enter Query Name +cloudwatch.logs.saved_query_status=Saved Query Status cloudwatch.logs.saving_to_disk=Saving Stream {0} to disk cloudwatch.logs.saving_to_disk_failed=Saving Stream {0} failed cloudwatch.logs.saving_to_disk_succeeded=Finished saving Log Stream {0} to {1} From ad6c5f129d1da277a64e176a4935dba41f84ca09 Mon Sep 17 00:00:00 2001 From: ranyoo2367 Date: Fri, 7 Aug 2020 13:19:11 -0400 Subject: [PATCH 0008/1546] Fixed accidental deletion --- jetbrains-core/build.gradle.kts | 1 + 1 file changed, 1 insertion(+) diff --git a/jetbrains-core/build.gradle.kts b/jetbrains-core/build.gradle.kts index 8ff09df45d..f9fce8613b 100644 --- a/jetbrains-core/build.gradle.kts +++ b/jetbrains-core/build.gradle.kts @@ -95,6 +95,7 @@ dependencies { api("software.amazon.awssdk:rds:$awsSdkVersion") api("software.amazon.awssdk:redshift:$awsSdkVersion") api("software.amazon.awssdk:secretsmanager:$awsSdkVersion") + api("software.amazon.awssdk:sqs:$awsSdkVersion") testImplementation(project(path = ":core", configuration = "testArtifacts")) testImplementation("com.github.tomakehurst:wiremock-jre8:2.26.0") From 7d2a5ac4669ed04bde14bcb2c080a95e66c060ee Mon Sep 17 00:00:00 2001 From: Ran Yoo <30319724+ranyoo2367@users.noreply.github.com> Date: Fri, 7 Aug 2020 16:33:49 -0400 Subject: [PATCH 0009/1546] SQS: Add create queue action (#1972) * Create action set up * Added tooltip help * Added tests * FIFO fix * PR changes, add .fifo for clients * PR changes: .fifo logical fix Co-authored-by: Hunter Werlla --- jetbrains-core/resources/META-INF/plugin.xml | 5 + .../services/sqs/CreateQueueDialog.kt | 113 ++++++++++++++ .../services/sqs/CreateQueuePanel.form | 82 ++++++++++ .../services/sqs/CreateQueuePanel.kt | 43 ++++++ .../jetbrains/services/sqs/SqsUtils.kt | 3 + .../services/sqs/actions/CreateQueueAction.kt | 24 +++ .../services/sqs/CreateQueueDialogTest.kt | 146 ++++++++++++++++++ .../resources/localized_messages.properties | 15 ++ 8 files changed, 431 insertions(+) create mode 100644 jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/CreateQueueDialog.kt create mode 100644 jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/CreateQueuePanel.form create mode 100644 jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/CreateQueuePanel.kt create mode 100644 jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/actions/CreateQueueAction.kt create mode 100644 jetbrains-core/tst/software/aws/toolkits/jetbrains/services/sqs/CreateQueueDialogTest.kt diff --git a/jetbrains-core/resources/META-INF/plugin.xml b/jetbrains-core/resources/META-INF/plugin.xml index 8a68d3420d..092156ab87 100644 --- a/jetbrains-core/resources/META-INF/plugin.xml +++ b/jetbrains-core/resources/META-INF/plugin.xml @@ -345,6 +345,11 @@ class="software.aws.toolkits.jetbrains.services.schemas.code.DownloadCodeForSchemaAction"/>
+ + + + diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/CreateQueueDialog.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/CreateQueueDialog.kt new file mode 100644 index 0000000000..20edf8b7da --- /dev/null +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/CreateQueueDialog.kt @@ -0,0 +1,113 @@ +// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package software.aws.toolkits.jetbrains.services.sqs + +import com.intellij.openapi.application.ModalityState +import com.intellij.openapi.application.runInEdt +import com.intellij.openapi.project.Project +import com.intellij.openapi.ui.DialogWrapper +import com.intellij.openapi.ui.ValidationInfo +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.launch +import software.amazon.awssdk.services.sqs.SqsClient +import software.amazon.awssdk.services.sqs.model.QueueAttributeName +import software.aws.toolkits.core.utils.getLogger +import software.aws.toolkits.core.utils.warn +import software.aws.toolkits.jetbrains.core.explorer.refreshAwsTree +import software.aws.toolkits.jetbrains.services.sqs.resources.SqsResources +import software.aws.toolkits.jetbrains.utils.ApplicationThreadPoolScope +import software.aws.toolkits.resources.message +import javax.swing.JComponent + +class CreateQueueDialog( + private val project: Project, + private val client: SqsClient +) : DialogWrapper(project), CoroutineScope by ApplicationThreadPoolScope("CreateQueueDialog") { + val view = CreateQueuePanel() + + init { + title = message("sqs.create.queue.title") + setOKButtonText(message("sqs.create.queue.create")) + setOKButtonTooltip(message("sqs.create.queue.tooltip")) + + init() + } + + override fun createCenterPanel(): JComponent? = view.component + + override fun getPreferredFocusedComponent(): JComponent? = view.queueName + + override fun doValidate(): ValidationInfo? = validateFields() + + // TODO: Override cancel action when telemetry added + + override fun doOKAction() { + if (isOKActionEnabled) { + setOKButtonText(message("sqs.create.queue.in_progress")) + isOKActionEnabled = false + + launch { + try { + createQueue() + runInEdt(ModalityState.any()) { + close(OK_EXIT_CODE) + } + project.refreshAwsTree(SqsResources.LIST_QUEUE_URLS) + } catch (e: Exception) { + // API only throws QueueNameExistsException if the request includes attributes whose values differ from those of the existing queue. + LOG.warn(e) { message("sqs.create.queue.failed", queueName()) } + setErrorText(e.message) + setOKButtonText(message("sqs.create.queue.create")) + isOKActionEnabled = true + } + } + } + } + + private fun validateFields(): ValidationInfo? { + if (view.queueName.text.isEmpty()) { + return ValidationInfo(message("sqs.create.validation.empty.queue.name"), view.queueName) + } + if (queueName().length > MAX_LENGTH_OF_QUEUE_NAME) { + return ValidationInfo(message("sqs.create.validation.long.queue.name", MAX_LENGTH_OF_QUEUE_NAME), view.queueName) + } + + if (view.fifoType.isSelected) { + if (!validateCharacters(queueName().substringBefore(FIFO_SUFFIX))) { + return ValidationInfo(message("sqs.create.validation.queue.name.invalid"), view.queueName) + } + } else { + if (!validateCharacters(queueName())) { + return ValidationInfo(message("sqs.create.validation.queue.name.invalid"), view.queueName) + } + } + + return null + } + + private fun validateCharacters(queueName: String): Boolean = queueName.matches("^[a-zA-Z0-9-_]*$".toRegex()) + + private fun queueName(): String { + val name = view.queueName.text.trim() + return if (view.fifoType.isSelected && !name.endsWith(FIFO_SUFFIX)) { + name + FIFO_SUFFIX + } else { + name + } + } + + fun createQueue() { + client.createQueue { + it.queueName(queueName()) + if (view.fifoType.isSelected) { + it.attributes(mutableMapOf(Pair(QueueAttributeName.FIFO_QUEUE, true.toString()))) + } + } + } + + private companion object { + val LOG = getLogger() + const val FIFO_SUFFIX = ".fifo" + } +} diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/CreateQueuePanel.form b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/CreateQueuePanel.form new file mode 100644 index 0000000000..952a63036c --- /dev/null +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/CreateQueuePanel.form @@ -0,0 +1,82 @@ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/CreateQueuePanel.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/CreateQueuePanel.kt new file mode 100644 index 0000000000..9545735b9b --- /dev/null +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/CreateQueuePanel.kt @@ -0,0 +1,43 @@ +// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +package software.aws.toolkits.jetbrains.services.sqs + +import com.intellij.icons.AllIcons +import com.intellij.ide.HelpTooltip +import software.aws.toolkits.resources.message +import javax.swing.JLabel +import javax.swing.JPanel +import javax.swing.JRadioButton +import javax.swing.JTextField + +class CreateQueuePanel { + lateinit var component: JPanel + lateinit var queueName: JTextField + lateinit var standardType: JRadioButton + lateinit var fifoType: JRadioButton + lateinit var fifoSuffixLabel: JLabel + lateinit var queueNameContextHelp: JLabel + + init { + setRadioButton() + setTooltip() + } + + private fun setRadioButton() { + fifoSuffixLabel.isVisible = false + fifoType.addActionListener { + fifoSuffixLabel.isVisible = true + } + standardType.addActionListener { + fifoSuffixLabel.isVisible = false + } + } + + private fun setTooltip() { + queueNameContextHelp.icon = AllIcons.General.ContextHelp + HelpTooltip().apply { + setDescription(message("sqs.queue.name.tooltip")) + installOn(queueNameContextHelp) + } + } +} diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/SqsUtils.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/SqsUtils.kt index 0e124c61f7..549eb4411c 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/SqsUtils.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/SqsUtils.kt @@ -6,3 +6,6 @@ package software.aws.toolkits.jetbrains.services.sqs const val MAX_NUMBER_OF_POLLED_MESSAGES = 10 const val MAX_LENGTH_OF_POLLED_MESSAGES = 1024 const val MAX_LENGTH_OF_FIFO_ID = 128 +const val MAX_LENGTH_OF_QUEUE_NAME = 80 +// Maximum length of queue name is 80, but the maximum will be 75 for FIFO queues due to '.fifo' suffix +const val MAX_LENGTH_OF_FIFO_QUEUE_NAME = 75 diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/actions/CreateQueueAction.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/actions/CreateQueueAction.kt new file mode 100644 index 0000000000..4ee0778db9 --- /dev/null +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/actions/CreateQueueAction.kt @@ -0,0 +1,24 @@ +// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package software.aws.toolkits.jetbrains.services.sqs.actions +import com.intellij.openapi.actionSystem.AnActionEvent +import com.intellij.openapi.actionSystem.PlatformDataKeys +import com.intellij.openapi.project.DumbAwareAction +import icons.AwsIcons +import software.amazon.awssdk.services.sqs.SqsClient +import software.aws.toolkits.jetbrains.core.awsClient +import software.aws.toolkits.jetbrains.services.sqs.CreateQueueDialog +import software.aws.toolkits.resources.message + +class CreateQueueAction : DumbAwareAction( + message("sqs.create.queue.title"), + null, + AwsIcons.Resources.Sqs.SQS_QUEUE +) { + override fun actionPerformed(e: AnActionEvent) { + val project = e.getRequiredData(PlatformDataKeys.PROJECT) + val client: SqsClient = project.awsClient() + CreateQueueDialog(project, client).show() + } +} diff --git a/jetbrains-core/tst/software/aws/toolkits/jetbrains/services/sqs/CreateQueueDialogTest.kt b/jetbrains-core/tst/software/aws/toolkits/jetbrains/services/sqs/CreateQueueDialogTest.kt new file mode 100644 index 0000000000..9c9cd021e0 --- /dev/null +++ b/jetbrains-core/tst/software/aws/toolkits/jetbrains/services/sqs/CreateQueueDialogTest.kt @@ -0,0 +1,146 @@ +// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package software.aws.toolkits.jetbrains.services.sqs + +import com.intellij.testFramework.ProjectRule +import com.intellij.testFramework.runInEdtAndWait +import com.nhaarman.mockitokotlin2.argumentCaptor +import com.nhaarman.mockitokotlin2.doReturn +import com.nhaarman.mockitokotlin2.doThrow +import com.nhaarman.mockitokotlin2.stub +import org.assertj.core.api.Assertions.assertThat +import org.assertj.core.api.Assertions.assertThatThrownBy +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import software.amazon.awssdk.services.sqs.SqsClient +import software.amazon.awssdk.services.sqs.model.CreateQueueRequest +import software.amazon.awssdk.services.sqs.model.CreateQueueResponse +import software.amazon.awssdk.services.sqs.model.QueueNameExistsException +import software.aws.toolkits.core.utils.RuleUtils +import software.aws.toolkits.jetbrains.core.MockClientManagerRule + +class CreateQueueDialogTest { + lateinit var client: SqsClient + + @Rule + @JvmField + val projectRule = ProjectRule() + + @JvmField + @Rule + val mockClientManagerRule = MockClientManagerRule(projectRule) + + @Before + fun setup() { + client = mockClientManagerRule.create() + } + + @Test + fun `Empty queue name fails`() { + runInEdtAndWait { + val dialog = CreateQueueDialog(projectRule.project, client).apply { + view.queueName.text = "" + } + + val validationInfo = dialog.validate() + assertThat(validationInfo).isNotNull() + } + } + + @Test + fun `Invalid queue name fails`() { + runInEdtAndWait { + val dialog = CreateQueueDialog(projectRule.project, client).apply { + view.queueName.text = INVALID_NAME + } + + val validationInfo = dialog.validate() + assertThat(validationInfo).isNotNull() + } + } + + @Test + fun `Too long standard queue name fails`() { + runInEdtAndWait { + val dialog = CreateQueueDialog(projectRule.project, client).apply { + view.queueName.text = RuleUtils.randomName(length = MAX_LENGTH_OF_QUEUE_NAME + 1) + } + + val validationInfo = dialog.validate() + assertThat(validationInfo).isNotNull() + } + } + + @Test + fun `Too long fifo queue name fails`() { + runInEdtAndWait { + val dialog = CreateQueueDialog(projectRule.project, client).apply { + view.queueName.text = RuleUtils.randomName(length = MAX_LENGTH_OF_FIFO_QUEUE_NAME + 1) + } + + val validationInfo = dialog.validate() + assertThat(validationInfo).isNotNull() + } + } + + @Test + fun `Standard queue created`() { + val createQueueCaptor = argumentCaptor() + client.stub { + on { createQueue(createQueueCaptor.capture()) } doReturn CreateQueueResponse.builder().build() + } + + runInEdtAndWait { + val dialog = CreateQueueDialog(projectRule.project, client).apply { + view.queueName.text = VALID_NAME + } + dialog.createQueue() + } + + assertThat(createQueueCaptor.firstValue.queueName()).isEqualTo(VALID_NAME) + } + + @Test + fun `Fifo queue created`() { + val createQueueCaptor = argumentCaptor() + client.stub { + on { createQueue(createQueueCaptor.capture()) } doReturn CreateQueueResponse.builder().build() + } + + runInEdtAndWait { + val dialog = CreateQueueDialog(projectRule.project, client).apply { + view.queueName.text = VALID_NAME + view.fifoType.isSelected = true + } + dialog.createQueue() + } + + assertThat(createQueueCaptor.firstValue.queueName()).isEqualTo(VALID_NAME_FIFO) + } + + @Test + fun `Error creating queue`() { + val createQueueCaptor = argumentCaptor() + client.stub { + on { createQueue(createQueueCaptor.capture()) } doThrow QueueNameExistsException.builder().message(ERROR_MESSAGE).build() + } + + runInEdtAndWait { + val dialog = CreateQueueDialog(projectRule.project, client).apply { + view.queueName.text = VALID_NAME + } + assertThatThrownBy { dialog.createQueue() }.hasMessage(ERROR_MESSAGE) + } + + assertThat(createQueueCaptor.firstValue.queueName()).isEqualTo(VALID_NAME) + } + + private companion object { + const val INVALID_NAME = "Hello_World!" + const val VALID_NAME = "Hello-World" + const val VALID_NAME_FIFO = "Hello-World.fifo" + const val ERROR_MESSAGE = "Queue already exists." + } +} diff --git a/resources/resources/software/aws/toolkits/resources/localized_messages.properties b/resources/resources/software/aws/toolkits/resources/localized_messages.properties index 99a8b3448d..2fa1cbb1e9 100644 --- a/resources/resources/software/aws/toolkits/resources/localized_messages.properties +++ b/resources/resources/software/aws/toolkits/resources/localized_messages.properties @@ -699,9 +699,22 @@ settings.states.invalid.short=Unable to connect settings.states.validating=Validating connection to AWS... settings.states.validating.short=Validating connection settings.title=AWS Connection Settings +sqs.create.fifo.label=FIFO +sqs.create.queue.create=Create +sqs.create.queue.failed=Failed to create queue {0} +sqs.create.queue.in_progress=Creating... +sqs.create.queue.name.label=Queue Name: +sqs.create.queue.title=Create Queue +sqs.create.queue.tooltip=It may take up to 60 seconds for the queue to be visible in the AWS Explorer. +sqs.create.queue.type.label=Queue Type: +sqs.create.standard.label=Standard +sqs.create.validation.empty.queue.name=Queue name must be specified. +sqs.create.validation.long.queue.name=Queue name must not exceed {0} characters in length. +sqs.create.validation.queue.name.invalid=Queue names can only contain alphanumeric characters, hyphens (-), and underscores (_). sqs.failed_to_load_total=Failed to load number of messages available sqs.failed_to_poll_messages=Failed to poll messages sqs.failed_to_send_message=Failed to send message +sqs.fifo.queue.tooltip=Supports first-in-first-out delivery and message ordering is preserved. sqs.message.deduplication_id.tooltip=

Used for deduplication of sent messages.

If a message with a particular message deduplication ID is sent successfully, any messages sent with the same message deduplication ID are accepted successfully but aren't delivered during the 5-minute deduplication interval.

sqs.message.group_id.tooltip=

Specifies that a message belongs to a specific message group.

Messages that belong to the same message group are always processed in a strict order relative to the message group.

sqs.message.message_body=Message Body @@ -716,6 +729,7 @@ sqs.message.validation.long.id=ID exceeds the maximum length of 128 characters. sqs.messages.available.text=Messages Available: sqs.poll.message=Poll for Messages sqs.poll.warning.text=To view messages in the queue, poll for messages.

Once polled, the sample of messages remain in the queue and are not returned to subsequent receive requests for the duration of the visibility timeout.

+sqs.queue.name.tooltip=

A queue name is case-sensitive and can have up to 80 characters of alphanumeric characters, hyphens (-), and underscores ( _ ).

If FIFO is selected, '.fifo' will be appended to the specified name if it does not already exist.

sqs.queue.polled.messages=Polled Messages sqs.required.empty.text=(Required) sqs.send.message=Send a Message @@ -726,5 +740,6 @@ sqs.send.message.deduplication_id=Message Deduplication ID: sqs.send.message.group_id=Message Group ID: sqs.send.message.send.button=Send sqs.send.message.success=Sent message ID: {0} +sqs.standard.queue.tooltip=Supports at-least-once delivery and message ordering is not preserved. sqs.toolwindow=SQS sqs.url.parse_error=Error parsing queue URL From 05d21207e89eb51e520efc287d719602d9d3f57e Mon Sep 17 00:00:00 2001 From: Ran Yoo <30319724+ranyoo2367@users.noreply.github.com> Date: Mon, 10 Aug 2020 13:16:11 -0400 Subject: [PATCH 0010/1546] SQS: Add delete queue action (#1974) * Create action set up * Added tooltip help * Added tests * FIFO fix * Set up delete action * Added tests for delete action * Fix style * Gradle telemetry version update * PR nit change --- gradle.properties | 2 +- jetbrains-core/resources/META-INF/plugin.xml | 2 + .../services/sqs/actions/CreateQueueAction.kt | 1 + .../services/sqs/actions/DeleteQueueAction.kt | 23 ++++++ .../services/sqs/toolwindow/SqsWindow.kt | 6 ++ .../ResourceOperationAgainstCodePipeline.kt | 3 +- .../services/sqs/DeleteQueueActionTest.kt | 72 +++++++++++++++++++ .../resources/localized_messages.properties | 2 + 8 files changed, 109 insertions(+), 2 deletions(-) create mode 100644 jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/actions/DeleteQueueAction.kt create mode 100644 jetbrains-core/tst/software/aws/toolkits/jetbrains/services/sqs/DeleteQueueActionTest.kt diff --git a/gradle.properties b/gradle.properties index 7017be1642..2b6a22fc2c 100644 --- a/gradle.properties +++ b/gradle.properties @@ -16,7 +16,7 @@ coroutinesVersion=1.3.3 ideaPluginVersion=0.4.20 ktlintVersion=0.36.0 jacksonVersion=2.9.8 -telemetryVersion=0.0.34 +telemetryVersion=0.0.38 assertjVersion=3.15.0 junitVersion=4.12 diff --git a/jetbrains-core/resources/META-INF/plugin.xml b/jetbrains-core/resources/META-INF/plugin.xml index 092156ab87..9586095b89 100644 --- a/jetbrains-core/resources/META-INF/plugin.xml +++ b/jetbrains-core/resources/META-INF/plugin.xml @@ -356,6 +356,8 @@ +
diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/actions/CreateQueueAction.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/actions/CreateQueueAction.kt index 4ee0778db9..d288835eb1 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/actions/CreateQueueAction.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/actions/CreateQueueAction.kt @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 package software.aws.toolkits.jetbrains.services.sqs.actions + import com.intellij.openapi.actionSystem.AnActionEvent import com.intellij.openapi.actionSystem.PlatformDataKeys import com.intellij.openapi.project.DumbAwareAction diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/actions/DeleteQueueAction.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/actions/DeleteQueueAction.kt new file mode 100644 index 0000000000..07f32f460f --- /dev/null +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/actions/DeleteQueueAction.kt @@ -0,0 +1,23 @@ +// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package software.aws.toolkits.jetbrains.services.sqs.actions + +import software.amazon.awssdk.services.sqs.SqsClient +import software.aws.toolkits.jetbrains.core.awsClient +import software.aws.toolkits.jetbrains.core.explorer.actions.DeleteResourceAction +import software.aws.toolkits.jetbrains.core.explorer.refreshAwsTree +import software.aws.toolkits.jetbrains.services.sqs.SqsQueueNode +import software.aws.toolkits.jetbrains.services.sqs.resources.SqsResources +import software.aws.toolkits.jetbrains.services.sqs.toolwindow.SqsWindow +import software.aws.toolkits.jetbrains.utils.TaggingResourceType +import software.aws.toolkits.resources.message + +class DeleteQueueAction : DeleteResourceAction(message("sqs.delete.queue.action"), TaggingResourceType.SQS_QUEUE) { + override fun performDelete(selected: SqsQueueNode) { + val client = selected.nodeProject.awsClient() + SqsWindow.getInstance(selected.nodeProject).closeQueue(selected.queueUrl) + client.deleteQueue { it.queueUrl(selected.queueUrl) } + selected.nodeProject.refreshAwsTree(SqsResources.LIST_QUEUE_URLS) + } +} diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/toolwindow/SqsWindow.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/toolwindow/SqsWindow.kt index d2b6c74806..1aa4981047 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/toolwindow/SqsWindow.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/toolwindow/SqsWindow.kt @@ -51,6 +51,12 @@ class SqsWindow(private val project: Project) : CoroutineScope by ApplicationThr } } + fun closeQueue(queueUrl: String) = launch { + withContext(edtContext) { + toolWindow.find(queueUrl)?.dispose() + } + } + companion object { internal val SQS_TOOL_WINDOW = ToolkitToolWindowType( "AWS.Sqs", diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/utils/ResourceOperationAgainstCodePipeline.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/utils/ResourceOperationAgainstCodePipeline.kt index 6a398cab30..780dbc0198 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/utils/ResourceOperationAgainstCodePipeline.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/utils/ResourceOperationAgainstCodePipeline.kt @@ -24,7 +24,8 @@ enum class TaggingResourceType(val value: String, val tagFilter: String) { LAMBDA_FUNCTION(message("codepipeline.lambda.resource_type"), "lambda:function"), CLOUDFORMATION_STACK(message("codepipeline.stack.resource_type"), "cloudformation:stack"), S3_BUCKET(message("codepipeline.bucket.resource_type"), "s3"), - CLOUDWATCHLOGS_GROUP(message("codepipeline.cloudwatch_group.resource_type"), "logs:log-group"); + CLOUDWATCHLOGS_GROUP(message("codepipeline.cloudwatch_group.resource_type"), "logs:log-group"), + SQS_QUEUE(message("codepipeline.sqs.resource_type"), "sqs:queue"); override fun toString() = value } diff --git a/jetbrains-core/tst/software/aws/toolkits/jetbrains/services/sqs/DeleteQueueActionTest.kt b/jetbrains-core/tst/software/aws/toolkits/jetbrains/services/sqs/DeleteQueueActionTest.kt new file mode 100644 index 0000000000..ec822ff179 --- /dev/null +++ b/jetbrains-core/tst/software/aws/toolkits/jetbrains/services/sqs/DeleteQueueActionTest.kt @@ -0,0 +1,72 @@ +// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package software.aws.toolkits.jetbrains.services.sqs + +import com.intellij.testFramework.ProjectRule +import com.nhaarman.mockitokotlin2.any +import com.nhaarman.mockitokotlin2.verify +import com.nhaarman.mockitokotlin2.whenever +import org.assertj.core.api.Assertions.assertThat +import org.assertj.core.api.Assertions.assertThatThrownBy +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.mockito.Mockito +import software.amazon.awssdk.services.sqs.SqsClient +import software.amazon.awssdk.services.sqs.model.DeleteQueueRequest +import software.aws.toolkits.core.utils.test.retryableAssert +import software.aws.toolkits.jetbrains.core.MockClientManagerRule +import software.aws.toolkits.jetbrains.core.toolwindow.ToolkitToolWindowManager +import software.aws.toolkits.jetbrains.services.sqs.actions.DeleteQueueAction +import software.aws.toolkits.jetbrains.services.sqs.toolwindow.SqsWindow +import java.util.function.Consumer +import javax.swing.JPanel + +class DeleteQueueActionTest { + lateinit var client: SqsClient + + @Rule + @JvmField + val projectRule = ProjectRule() + + @JvmField + @Rule + val mockClientManagerRule = MockClientManagerRule(projectRule) + + @Before + fun setup() { + client = mockClientManagerRule.create() + } + + @Test + fun `Delete queue succeeds`() { + val mockNode = SqsQueueNode(projectRule.project, QUEUE_URL) + DeleteQueueAction().performDelete(mockNode) + verify(client).deleteQueue(any>()) + } + + @Test + fun `Delete queue closes tool window`() { + val mockNode = SqsQueueNode(projectRule.project, QUEUE_URL) + val toolWindowManager = ToolkitToolWindowManager.getInstance(projectRule.project, SqsWindow.SQS_TOOL_WINDOW).apply { + addTab("test1", JPanel(), activate = true, id = QUEUE_URL) + } + DeleteQueueAction().performDelete(mockNode) + retryableAssert { + assertThat(toolWindowManager.find(QUEUE_URL)).isNull() + } + } + + @Test + fun `Delete queue returns error`() { + whenever(client.deleteQueue(Mockito.any())).thenThrow(RuntimeException(ERROR_MESSAGE)) + val mockNode = SqsQueueNode(projectRule.project, QUEUE_URL) + assertThatThrownBy { DeleteQueueAction().performDelete(mockNode) }.hasMessage(ERROR_MESSAGE) + } + + private companion object { + const val QUEUE_URL = "https://sqs.us-east-1.amazonaws.com/123456789012/test1" + const val ERROR_MESSAGE = "Network Error" + } +} diff --git a/resources/resources/software/aws/toolkits/resources/localized_messages.properties b/resources/resources/software/aws/toolkits/resources/localized_messages.properties index 2fa1cbb1e9..3321c225cf 100644 --- a/resources/resources/software/aws/toolkits/resources/localized_messages.properties +++ b/resources/resources/software/aws/toolkits/resources/localized_messages.properties @@ -237,6 +237,7 @@ codepipeline.resource.update.warning.message=The {0} ''{1}'' is part of an codepipeline.resource.update.warning.no_text=No codepipeline.resource.update.warning.title=Warning! codepipeline.resource.update.warning.yes_text=Yes +codepipeline.sqs.resource_type=AWS SQS queue codepipeline.stack.resource_type=AWS CloudFormation stack configure.toolkit=Configure AWS connection configure.toolkit.upsert_credentials.action=Edit AWS Credential file(s) @@ -711,6 +712,7 @@ sqs.create.standard.label=Standard sqs.create.validation.empty.queue.name=Queue name must be specified. sqs.create.validation.long.queue.name=Queue name must not exceed {0} characters in length. sqs.create.validation.queue.name.invalid=Queue names can only contain alphanumeric characters, hyphens (-), and underscores (_). +sqs.delete.queue.action=Delete Queue sqs.failed_to_load_total=Failed to load number of messages available sqs.failed_to_poll_messages=Failed to poll messages sqs.failed_to_send_message=Failed to send message From a9f71db53004cacdbe0aad41f6e26bfc172154ea Mon Sep 17 00:00:00 2001 From: Hunter Werlla Date: Wed, 12 Aug 2020 11:55:58 -0700 Subject: [PATCH 0011/1546] Add an option to get RDS/Redshift host and port from secretsmanager (#1971) Add an option to pull host and port from the secret. In this case, whatever is in those boxes is ignored (there is not a way to hide them) --- .../datagrip/auth/SecretsManagerAuth.kt | 22 ++++- .../datagrip/auth/SecretsManagerAuthWidget.kt | 21 ++++- .../datagrip/auth/SecretsManagerAuthTest.kt | 93 +++++++++++++++---- .../auth/SecretsManagerAuthWidgetTest.kt | 11 ++- .../resources/localized_messages.properties | 3 + 5 files changed, 123 insertions(+), 27 deletions(-) diff --git a/jetbrains-ultimate/src-201+/software/aws/toolkits/jetbrains/datagrip/auth/SecretsManagerAuth.kt b/jetbrains-ultimate/src-201+/software/aws/toolkits/jetbrains/datagrip/auth/SecretsManagerAuth.kt index 33043b8485..c521882cdd 100644 --- a/jetbrains-ultimate/src-201+/software/aws/toolkits/jetbrains/datagrip/auth/SecretsManagerAuth.kt +++ b/jetbrains-ultimate/src-201+/software/aws/toolkits/jetbrains/datagrip/auth/SecretsManagerAuth.kt @@ -61,8 +61,22 @@ class SecretsManagerAuth : DatabaseAuthProvider, CoroutineScope by ApplicationTh val project = connection.runConfiguration.project try { val connectionSettings = getConfiguration(connection) - val credentials = getCredentials(connection.runConfiguration.project, connectionSettings) - DatabaseCredentialsAuthProvider.applyCredentials(connection, credentials, true) + val dbSecret = getDbSecret(connection.runConfiguration.project, connectionSettings) + if (connection.connectionPoint.additionalJdbcProperties[GET_URL_FROM_SECRET]?.toBoolean() == true) { + dbSecret.host ?: throw IllegalArgumentException(message("datagrip.secretsmanager.validation.no_host", connectionSettings.secretId)) + dbSecret.port ?: throw IllegalArgumentException(message("datagrip.secretsmanager.validation.no_port", connectionSettings.secretId)) + // we have to rewrite the url which is pretty messy. The util is not better than using split (requires magic strings + // to access the properties), so use split instead + val db = connection.url.split("/").last() + val jdbcUrlBeginning = connection.url.split("://").first() + connection.url = "$jdbcUrlBeginning://${dbSecret.host}:${dbSecret.port}/$db" + } + + DatabaseCredentialsAuthProvider.applyCredentials( + connection, + Credentials(dbSecret.username, dbSecret.password), + true + ) } catch (e: Throwable) { result = Result.Failed throw e @@ -87,13 +101,13 @@ class SecretsManagerAuth : DatabaseAuthProvider, CoroutineScope by ApplicationTh ) } - private fun getCredentials(project: Project, configuration: SecretsManagerConfiguration): Credentials { + private fun getDbSecret(project: Project, configuration: SecretsManagerConfiguration): SecretsManagerDbSecret { val client = project.awsClient(configuration.connectionSettings.credentials, configuration.connectionSettings.region) val secret = client.getSecretValue { it.secretId(configuration.secretId) } val dbSecret = objectMapper.readValue(secret.secretString()) dbSecret.username ?: throw IllegalArgumentException(message("datagrip.secretsmanager.validation.no_username", secret.name())) dbSecret.password ?: throw IllegalArgumentException(message("datagrip.secretsmanager.validation.no_password", secret.name())) - return Credentials(dbSecret.username, dbSecret.password) + return dbSecret } companion object { diff --git a/jetbrains-ultimate/src-201+/software/aws/toolkits/jetbrains/datagrip/auth/SecretsManagerAuthWidget.kt b/jetbrains-ultimate/src-201+/software/aws/toolkits/jetbrains/datagrip/auth/SecretsManagerAuthWidget.kt index 3b821b127a..355300ac9c 100644 --- a/jetbrains-ultimate/src-201+/software/aws/toolkits/jetbrains/datagrip/auth/SecretsManagerAuthWidget.kt +++ b/jetbrains-ultimate/src-201+/software/aws/toolkits/jetbrains/datagrip/auth/SecretsManagerAuthWidget.kt @@ -6,6 +6,7 @@ package software.aws.toolkits.jetbrains.datagrip.auth import com.intellij.database.dataSource.DataSourceUiUtil import com.intellij.database.dataSource.LocalDataSource import com.intellij.database.dataSource.url.ui.UrlPropertiesPanel +import com.intellij.ui.components.JBCheckBox import com.intellij.ui.components.JBLabel import com.intellij.ui.components.JBTextField import com.intellij.util.text.nullize @@ -17,18 +18,21 @@ import software.aws.toolkits.resources.message import javax.swing.JPanel const val SECRET_ID_PROPERTY = "AWS.SecretId" +const val GET_URL_FROM_SECRET = "AWS.getUrlFromSecret" class SecretsManagerAuthWidget : AwsAuthWidget(userField = false) { private val secretIdSelector = JBTextField() + private val urlFromSecret = JBCheckBox(message("datagrip.secret_host")) - override val rowCount = 4 + override val rowCount = 5 override fun getRegionFromUrl(url: String?): String? = RdsResources.extractRegionFromUrl(url) ?: RedshiftUtils.extractRegionFromUrl(url) override fun createPanel(): JPanel { val panel = super.createPanel() - val label = JBLabel(message("datagrip.secret_id")) - panel.add(label, UrlPropertiesPanel.createLabelConstraints(3, 0, label.preferredSize.getWidth())) + val secretLabel = JBLabel(message("datagrip.secret_id")) + panel.add(secretLabel, UrlPropertiesPanel.createLabelConstraints(3, 0, secretLabel.preferredSize.getWidth())) panel.add(secretIdSelector, UrlPropertiesPanel.createSimpleConstraints(3, 1, 3)) + panel.add(urlFromSecret, UrlPropertiesPanel.createSimpleConstraints(4, 1, 3)) return panel } @@ -40,6 +44,12 @@ class SecretsManagerAuthWidget : AwsAuthWidget(userField = false) { SECRET_ID_PROPERTY, secretIdSelector.text.nullize() ) + + DataSourceUiUtil.putOrRemove( + dataSource.additionalJdbcProperties, + GET_URL_FROM_SECRET, + urlFromSecret.isSelected.toString() + ) } override fun reset(dataSource: LocalDataSource, resetCredentials: Boolean) { @@ -47,8 +57,13 @@ class SecretsManagerAuthWidget : AwsAuthWidget(userField = false) { dataSource.additionalJdbcProperties[SECRET_ID_PROPERTY]?.nullize()?.let { secretIdSelector.text = it } + dataSource.additionalJdbcProperties[GET_URL_FROM_SECRET]?.nullize()?.let { + urlFromSecret.isSelected = it.toBoolean() + } } @TestOnly internal fun getSecretId() = secretIdSelector.text + @TestOnly + internal fun getUrlFromSecretSet() = urlFromSecret.isSelected } diff --git a/jetbrains-ultimate/tst-201+/software/aws/toolkits/jetbrains/datagrip/auth/SecretsManagerAuthTest.kt b/jetbrains-ultimate/tst-201+/software/aws/toolkits/jetbrains/datagrip/auth/SecretsManagerAuthTest.kt index 30974b92e4..95108084d3 100644 --- a/jetbrains-ultimate/tst-201+/software/aws/toolkits/jetbrains/datagrip/auth/SecretsManagerAuthTest.kt +++ b/jetbrains-ultimate/tst-201+/software/aws/toolkits/jetbrains/datagrip/auth/SecretsManagerAuthTest.kt @@ -15,9 +15,11 @@ import com.nhaarman.mockitokotlin2.doThrow import com.nhaarman.mockitokotlin2.mock import com.nhaarman.mockitokotlin2.stub import org.assertj.core.api.Assertions.assertThat +import org.assertj.core.api.Assertions.assertThatThrownBy import org.junit.Before import org.junit.Rule import org.junit.Test +import org.mockito.ArgumentMatchers.anyString import software.amazon.awssdk.auth.credentials.AwsBasicCredentials import software.amazon.awssdk.services.secretsmanager.SecretsManagerClient import software.amazon.awssdk.services.secretsmanager.model.GetSecretValueRequest @@ -30,6 +32,7 @@ import software.aws.toolkits.jetbrains.core.credentials.MockCredentialsManager import software.aws.toolkits.jetbrains.core.region.MockRegionProvider import software.aws.toolkits.jetbrains.datagrip.CREDENTIAL_ID_PROPERTY import software.aws.toolkits.jetbrains.datagrip.REGION_ID_PROPERTY +import kotlin.test.assertNotNull class SecretsManagerAuthTest { @Rule @@ -49,7 +52,9 @@ class SecretsManagerAuthTest { private val credentialId = RuleUtils.randomName() private val defaultRegion = RuleUtils.randomName() private val dbHost = "${RuleUtils.randomName()}.555555.us-west-2.rds.amazonaws.com" - private val port = 5432 + private val port = RuleUtils.randomNumber() + private val secretDbHost = "${RuleUtils.randomName()}.555555.us-west-2.rds.amazonaws.com" + private val secretPort = RuleUtils.randomNumber() private val mockCreds = AwsBasicCredentials.create("Access", "ItsASecret") @@ -63,45 +68,71 @@ class SecretsManagerAuthTest { fun `Intercept credentials succeeds`() { createSecretsManagerClient() val connection = sAuth.intercept(buildConnection(), false)?.toCompletableFuture()?.get() - assertThat(connection).isNotNull - assertThat(connection!!.connectionProperties).containsKey("user") + assertNotNull(connection) + assertThat(connection.connectionProperties).containsKey("user") assertThat(connection.connectionProperties["user"]).isEqualTo(username) assertThat(connection.connectionProperties).containsKey("password") assertThat(connection.connectionProperties["password"]).isEqualTo(password) } - @Test(expected = IllegalArgumentException::class) + @Test + fun `Intercept credentials succeeds with host and port from secret`() { + createSecretsManagerClient() + val connection = sAuth.intercept(buildConnection(usesUrlFromSecret = true), false)?.toCompletableFuture()?.get() + assertNotNull(connection) + assertThat(connection.connectionProperties).containsKey("user") + assertThat(connection.connectionProperties["user"]).isEqualTo(username) + assertThat(connection.connectionProperties).containsKey("password") + assertThat(connection.connectionProperties["password"]).isEqualTo(password) + assertThat(connection.url).contains("//$secretDbHost:$secretPort/") + } + + @Test fun `No secret fails`() { - sAuth.intercept(buildConnection(hasSecret = false), false)?.unwrap() + assertThatThrownBy { sAuth.intercept(buildConnection(hasSecret = false), false)?.unwrap() }.isInstanceOf(IllegalArgumentException::class.java) } - @Test(expected = IllegalArgumentException::class) + @Test fun `Bad AWS connection fails`() { - sAuth.intercept(buildConnection(hasCredentials = false), false)?.unwrap() + assertThatThrownBy { sAuth.intercept(buildConnection(hasCredentials = false), false)?.unwrap() }.isInstanceOf(IllegalArgumentException::class.java) } - @Test(expected = IllegalArgumentException::class) + @Test fun `No username in credentials fails`() { createSecretsManagerClient(hasUsername = false) - sAuth.intercept(buildConnection(), false)?.unwrap() + assertThatThrownBy { sAuth.intercept(buildConnection(), false)?.unwrap() }.isInstanceOf(IllegalArgumentException::class.java) } - @Test(expected = IllegalArgumentException::class) + @Test fun `No password in credentials fails`() { createSecretsManagerClient(hasPassword = false) - sAuth.intercept(buildConnection(), false)?.unwrap() + assertThatThrownBy { sAuth.intercept(buildConnection(), false)?.unwrap() }.isInstanceOf(IllegalArgumentException::class.java) + } + + @Test + fun `When getting url from secret no host in secret fails`() { + createSecretsManagerClient(hasHost = false) + assertThatThrownBy { sAuth.intercept(buildConnection(usesUrlFromSecret = true), false)?.unwrap() }.isInstanceOf(IllegalArgumentException::class.java) + } + + @Test + fun `When getting url from secret no port in secret fails`() { + createSecretsManagerClient(hasPort = false) + assertThatThrownBy { sAuth.intercept(buildConnection(usesUrlFromSecret = true), false)?.unwrap() }.isInstanceOf(IllegalArgumentException::class.java) } - @Test(expected = RuntimeException::class) + @Test fun `Secrets Manager client throws fails`() { createSecretsManagerClient(succeeds = false) - sAuth.intercept(buildConnection(), false)?.unwrap() + assertThatThrownBy { sAuth.intercept(buildConnection(), false)?.unwrap() }.isInstanceOf(RuntimeException::class.java) } private fun createSecretsManagerClient( succeeds: Boolean = true, hasUsername: Boolean = true, - hasPassword: Boolean = true + hasPassword: Boolean = true, + hasHost: Boolean = true, + hasPort: Boolean = true ): SecretsManagerClient { val client = clientManager.create() val secretMap = mutableMapOf() @@ -111,6 +142,12 @@ class SecretsManagerAuthTest { if (hasPassword) { secretMap["password"] = password } + if (hasHost) { + secretMap["host"] = secretDbHost + } + if (hasPort) { + secretMap["port"] = secretPort.toString() + } client.stub { if (succeeds) { @@ -130,13 +167,16 @@ class SecretsManagerAuthTest { hasCredentials: Boolean = true, hasHost: Boolean = true, hasPort: Boolean = true, - hasSecret: Boolean = true + hasSecret: Boolean = true, + usesUrlFromSecret: Boolean = false ): DatabaseConnectionInterceptor.ProtoConnection { val mockConnection = mock { - on { url } doReturn if (hasUrl) { - "jdbc:postgresql://${if (hasHost) dbHost else ""}${if (hasPort) ":$port" else ""}/dev" - } else { - null + on { url } doAnswer { + if (hasUrl) { + "jdbc:postgresql://${if (hasHost) dbHost else ""}${if (hasPort) ":$port" else ""}/dev" + } else { + null + } } on { databaseDriver } doReturn null on { driverClass } doReturn "org.postgresql.Driver" @@ -153,6 +193,9 @@ class SecretsManagerAuthTest { if (hasSecret) { m[SECRET_ID_PROPERTY] = secret } + if (usesUrlFromSecret) { + m[GET_URL_FROM_SECRET] = true.toString() + } m } on { dataSource } doReturn mockConnection @@ -164,6 +207,11 @@ class SecretsManagerAuthTest { } return mock { val m = mutableMapOf() + var u = if (hasUrl) { + "jdbc:postgresql://${if (hasHost) dbHost else ""}${if (hasPort) ":$port" else ""}/dev" + } else { + null + } on { connectionPoint } doReturn dbConnectionPoint on { runConfiguration } doAnswer { mock { @@ -171,6 +219,13 @@ class SecretsManagerAuthTest { } } on { connectionProperties } doReturn m + on { getUrl() } doAnswer { + u + } + on { setUrl(anyString()) } doAnswer { + u = it.arguments[0] as String + Unit + } } } } diff --git a/jetbrains-ultimate/tst-201+/software/aws/toolkits/jetbrains/datagrip/auth/SecretsManagerAuthWidgetTest.kt b/jetbrains-ultimate/tst-201+/software/aws/toolkits/jetbrains/datagrip/auth/SecretsManagerAuthWidgetTest.kt index 30d6468ff7..463eedfbf1 100644 --- a/jetbrains-ultimate/tst-201+/software/aws/toolkits/jetbrains/datagrip/auth/SecretsManagerAuthWidgetTest.kt +++ b/jetbrains-ultimate/tst-201+/software/aws/toolkits/jetbrains/datagrip/auth/SecretsManagerAuthWidgetTest.kt @@ -51,6 +51,14 @@ class SecretsManagerAuthWidgetTest { assertThat(widget.getSecretId()).isEqualTo(defaultSecretId) } + @Test + fun `Get url from secret property set from widget`() { + widget.reset(buildDataSource(getUrlFromSecret = true), false) + assertThat(widget.getUrlFromSecretSet()).isEqualTo(true) + widget.reset(buildDataSource(getUrlFromSecret = false), false) + assertThat(widget.getUrlFromSecretSet()).isEqualTo(false) + } + @Test fun `Sets region from Redshift URL`() { widget.reset(mock(), false) @@ -77,7 +85,7 @@ class SecretsManagerAuthWidgetTest { assertThat(widget.getSelectedRegion()?.id).isEqualTo(defaultRegion) } - private fun buildDataSource(hasSecret: Boolean = true): LocalDataSource = mock { + private fun buildDataSource(hasSecret: Boolean = true, getUrlFromSecret: Boolean = false): LocalDataSource = mock { on { additionalJdbcProperties } doAnswer { mutableMapOf().also { it[CREDENTIAL_ID_PROPERTY] = credentialId @@ -85,6 +93,7 @@ class SecretsManagerAuthWidgetTest { if (hasSecret) { it[SECRET_ID_PROPERTY] = defaultSecretId } + it[GET_URL_FROM_SECRET] = getUrlFromSecret.toString() } } } diff --git a/resources/resources/software/aws/toolkits/resources/localized_messages.properties b/resources/resources/software/aws/toolkits/resources/localized_messages.properties index d2673bcf04..a01d6d5ecd 100644 --- a/resources/resources/software/aws/toolkits/resources/localized_messages.properties +++ b/resources/resources/software/aws/toolkits/resources/localized_messages.properties @@ -278,6 +278,7 @@ credentials.sso.login.message=Login to AWS at {0}, code: {1} credentials.sso.login.open_browser=Open Browser credentials.sso.login.title=AWS SSO Login Required datagrip.auth.secrets_manager=SecretsManager Auth +datagrip.secret_host=Use the url and port from the secret datagrip.secret_id=Secret Name/ARN: datagrip.secretsmanager.action=Connect with Secrets Manager... datagrip.secretsmanager.action.confirm_continue=

{0}


Continue setting up the database?

@@ -288,7 +289,9 @@ datagrip.secretsmanager.validation.different_address=Secret {0} specifies a diff datagrip.secretsmanager.validation.different_engine=Secret {0} specifies a different database engine ''{1}'' datagrip.secretsmanager.validation.exception=Exception thrown while validating secret datagrip.secretsmanager.validation.failed_to_get=Failed to retrieve secret {0} +datagrip.secretsmanager.validation.no_host=Secret {0} does not have a field named ''host'' datagrip.secretsmanager.validation.no_password=Secret {0} does not have a field named ''password'' +datagrip.secretsmanager.validation.no_port=Secret {0} does not have a field named ''port'' datagrip.secretsmanager.validation.no_secret=No SecretsManager secret specified datagrip.secretsmanager.validation.no_username=Secret {0} does not have a field named ''username'' datagrip.secretsmanager.validation.unkown_engine=Unknown database engine {0} From cd61934da47b34d24b185467afb1ef607f84d542 Mon Sep 17 00:00:00 2001 From: Austin Brooks Date: Thu, 13 Aug 2020 10:10:34 -0700 Subject: [PATCH 0012/1546] Make the Lambda sub-system dynamic plugin compatible (#1960) --- .../resources/META-INF/ext-java.xml | 10 +- .../resources/META-INF/ext-python.xml | 10 +- jetbrains-core/resources/META-INF/plugin.xml | 30 ++-- .../jetbrains/core/IdBasedExtensionPoint.kt | 28 ++++ .../jetbrains/services/lambda/Lambda.kt | 4 +- .../services/lambda/LambdaBuilderUtils.kt | 4 +- .../jetbrains/services/lambda/RuntimeGroup.kt | 150 ++++++------------ .../services/lambda/SamProjectWizard.kt | 4 +- .../DeployServerlessApplicationAction.kt | 2 +- .../lambda/actions/UpdateFunctionAction.kt | 2 +- .../completion/HandlerCompletionProvider.kt | 5 +- .../execution/LambdaRunConfigurationType.kt | 6 +- .../local/LocalLambdaRunConfiguration.kt | 4 +- .../LocalLambdaRunConfigurationProducer.kt | 8 +- .../local/LocalLambdaRunSettingsEditor.kt | 2 +- .../lambda/execution/local/SamDebugger.kt | 2 +- .../lambda/execution/local/SamInvokeRunner.kt | 4 +- .../services/lambda/java/JavaRuntimeGroup.kt | 6 +- .../lambda/java/JavaSamProjectWizard.kt | 10 +- .../lambda/python/PythonRuntimeGroup.kt | 26 ++- .../lambda/python/PythonSamProjectWizard.kt | 19 +-- .../lambda/upload/EditFunctionDialog.kt | 4 +- .../lambda/upload/LambdaLineMarker.kt | 4 +- .../jetbrains/services/schemas/Schemas.kt | 10 +- .../code/DownloadCodeForSchemaDialog.kt | 30 ++-- .../ui/wizard/IntelliJSdkSelectionPanel.kt | 14 +- .../ui/wizard/SamInitSelectionPanel.java | 2 +- .../SchemaResourceSelectorSelectionPanel.kt | 5 +- .../ui/wizard/SchemaSelectionPanel.kt | 2 +- .../jetbrains/ui/wizard/SdkSelectionPanel.kt | 2 +- .../java/JavaLambdaHandlerResolverTest.kt | 3 +- .../python/PythonLambdaHandlerResolverTest.kt | 2 +- .../ui/wizard/SchemaSelectionPanelTest.kt | 3 - .../resources/META-INF/ext-rider.xml | 12 +- .../lambda/dotnet/DotNetRuntimeGroup.kt | 26 +-- .../ui/wizard/DotNetSamProjectGenerator.kt | 3 +- .../resources/META-INF/ext-nodejs.xml | 11 +- .../lambda/nodejs/NodeJsRuntimeGroup.kt | 7 +- .../nodejs/NodeJsLambdaHandlerResolverTest.kt | 5 +- 39 files changed, 219 insertions(+), 262 deletions(-) create mode 100644 jetbrains-core/src/software/aws/toolkits/jetbrains/core/IdBasedExtensionPoint.kt diff --git a/jetbrains-core/resources/META-INF/ext-java.xml b/jetbrains-core/resources/META-INF/ext-java.xml index 02716190c4..1e7d2c55eb 100644 --- a/jetbrains-core/resources/META-INF/ext-java.xml +++ b/jetbrains-core/resources/META-INF/ext-java.xml @@ -7,11 +7,11 @@ - - - - - + + + + + diff --git a/jetbrains-core/resources/META-INF/ext-python.xml b/jetbrains-core/resources/META-INF/ext-python.xml index 6bd2f1407d..a046bd772c 100644 --- a/jetbrains-core/resources/META-INF/ext-python.xml +++ b/jetbrains-core/resources/META-INF/ext-python.xml @@ -7,11 +7,11 @@ - - - - - + + + + + diff --git a/jetbrains-core/resources/META-INF/plugin.xml b/jetbrains-core/resources/META-INF/plugin.xml index 5994bba31f..e6826e55bc 100644 --- a/jetbrains-core/resources/META-INF/plugin.xml +++ b/jetbrains-core/resources/META-INF/plugin.xml @@ -101,34 +101,26 @@ - - - - + - - - + + - - - + + - - - + + - - - + + - - - + + diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/core/IdBasedExtensionPoint.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/core/IdBasedExtensionPoint.kt new file mode 100644 index 0000000000..b0467202c8 --- /dev/null +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/core/IdBasedExtensionPoint.kt @@ -0,0 +1,28 @@ +// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +package software.aws.toolkits.jetbrains.core + +import com.intellij.openapi.extensions.CustomLoadingExtensionPointBean +import com.intellij.util.KeyedLazyInstance +import com.intellij.util.xmlb.annotations.Attribute + +/** + * Extension point that is used to tie multiple extension points together by a common ID. + * + * For example, if you have a "parent" extension point that defines a new ID, i.e Lambda runtime. + * "Children" extension points that support it can then use the same ID to correlate each other, + * i.e Lambda building, Lambda handler can be looked up using the same ID as defined by the Runtime parent EP. + * + * Additional attributes can be defined on the EP by extending this class. + */ +open class IdBasedExtensionPoint : CustomLoadingExtensionPointBean(), KeyedLazyInstance { + @Attribute("id") + lateinit var id: String + + @Attribute("implementationClass") + lateinit var implementationClass: String + + override fun getImplementationClassName(): String = implementationClass + + override fun getKey(): String = id +} diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/Lambda.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/Lambda.kt index ed75596996..50f9693213 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/Lambda.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/Lambda.kt @@ -34,7 +34,7 @@ object Lambda { private val LOG = getLogger() fun findPsiElementsForHandler(project: Project, runtime: Runtime, handler: String): Array { - val resolver = runtime.runtimeGroup?.let { LambdaHandlerResolver.getInstance(it) } ?: return emptyArray() + val resolver = runtime.runtimeGroup?.let { LambdaHandlerResolver.getInstanceOrNull(it) } ?: return emptyArray() // Don't search through ".aws-sam" folders val samBuildFileScopes = GlobalSearchScope.filesScope(project, findSamBuildContents(project)) @@ -50,7 +50,7 @@ object Lambda { fun isHandlerValid(project: Project, runtime: Runtime, handler: String): Boolean = ReadAction.compute { runtime.runtimeGroup?.let { - LambdaHandlerResolver.getInstance(it) + LambdaHandlerResolver.getInstanceOrNull(it) }?.isHandlerValid(project, handler) == true } diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/LambdaBuilderUtils.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/LambdaBuilderUtils.kt index 63bf51b42a..1612718282 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/LambdaBuilderUtils.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/LambdaBuilderUtils.kt @@ -33,7 +33,7 @@ object LambdaBuilderUtils { module: Module, runtimeGroup: RuntimeGroup, request: BuildLambdaRequest, - lambdaBuilder: LambdaBuilder = LambdaBuilder.getInstanceOrThrow(runtimeGroup) + lambdaBuilder: LambdaBuilder = LambdaBuilder.getInstance(runtimeGroup) ): CompletionStage { val buildViewManager = ServiceManager.getService(module.project, BuildViewManager::class.java) @@ -51,7 +51,7 @@ object LambdaBuilderUtils { module: Module, runtimeGroup: RuntimeGroup, request: PackageLambdaFromHandler, - lambdaBuilder: LambdaBuilder = LambdaBuilder.getInstanceOrThrow(runtimeGroup) + lambdaBuilder: LambdaBuilder = LambdaBuilder.getInstance(runtimeGroup) ): CompletionStage { val buildViewManager = ServiceManager.getService(module.project, BuildViewManager::class.java) diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/RuntimeGroup.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/RuntimeGroup.kt index de580db9c0..f90c1f78f0 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/RuntimeGroup.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/RuntimeGroup.kt @@ -7,7 +7,6 @@ package software.aws.toolkits.jetbrains.services.lambda import com.intellij.lang.Language import com.intellij.openapi.actionSystem.AnActionEvent import com.intellij.openapi.actionSystem.LangDataKeys -import com.intellij.openapi.extensions.AbstractExtensionPointBean import com.intellij.openapi.extensions.ExtensionPointName import com.intellij.openapi.module.Module import com.intellij.openapi.module.ModuleType @@ -17,108 +16,65 @@ import com.intellij.openapi.projectRoots.SdkType import com.intellij.openapi.roots.ModuleRootManager import com.intellij.openapi.roots.ProjectRootManager import com.intellij.openapi.util.KeyedExtensionCollector -import com.intellij.openapi.util.LazyInstance -import com.intellij.util.KeyedLazyInstance -import com.intellij.util.xmlb.annotations.Attribute import software.amazon.awssdk.services.lambda.model.Runtime +import software.aws.toolkits.jetbrains.core.IdBasedExtensionPoint + +/** + * IDs for built-in runtime groups that ship with toolkit + */ +object BuiltInRuntimeGroups { + const val Python = "PYTHON" + const val Dotnet = "DOTNET" + const val Java = "JAVA" + const val NodeJs = "NODEJS" +} /** * Grouping of Lambda [Runtime] by parent language. * - * A Lambda [Runtime] belongs to a single [RuntimeGroup], a [RuntimeGroup] may have several - * Lambda [Runtime]s, [Language]s or [Sdk]s. + * A Lambda [Runtime] belongs to a single [RuntimeGroup], a [RuntimeGroup] may have several Lambda [Runtime]s, [Language]s or [Sdk]s. */ -enum class RuntimeGroup { - JAVA, - PYTHON, - NODEJS, - DOTNET; - - private val info by lazy { - RuntimeGroupInformation.getInstances(this) - } +interface RuntimeGroup { + val id: String + val runtimes: Set + val languageIds: Set - val runtimes: Set by lazy { info.flatMap { it.runtimes }.toSet() } - val languageIds: Set by lazy { info.flatMap { it.languageIds }.toSet() } + fun determineRuntime(project: Project): Runtime? = null + fun determineRuntime(module: Module): Runtime? = null + fun getModuleType(): ModuleType<*>? = null + fun getIdeSdkType(): SdkType? = null + fun supportsSamBuild(): Boolean = true - fun determineRuntime(project: Project): Runtime? = info.asSequence().mapNotNull { it.determineRuntime(project) }.firstOrNull() - fun determineRuntime(module: Module): Runtime? = info.asSequence().mapNotNull { it.determineRuntime(module) }.firstOrNull() - fun getModuleType(): ModuleType<*>? = info.asSequence().mapNotNull { it.getModuleType() }.firstOrNull() - fun getIdeSdkType(): SdkType? = info.asSequence().mapNotNull { it.getIdeSdkType() }.firstOrNull() - fun supportsSamBuild(): Boolean = info.asSequence().all { it.supportsSamBuild() } + companion object { + private val EP_NAM = ExtensionPointName.create("aws.toolkit.lambda.runtimeGroup") - internal companion object { - /** - * Lazily apply the predicate to each [RuntimeGroup] and return the first match (or null) - */ @JvmStatic - fun find(predicate: (RuntimeGroup) -> Boolean): RuntimeGroup? = RuntimeGroup.values().asSequence().filter(predicate).firstOrNull() + fun find(predicate: (RuntimeGroup) -> Boolean): RuntimeGroup? = registeredRuntimeGroups().firstOrNull(predicate) + + fun getById(id: String): RuntimeGroup = find { it.id == id } ?: throw IllegalStateException("No RuntimeGroup with $id is registered") fun determineRuntime(project: Project?): Runtime? = project?.let { _ -> - values().asSequence().mapNotNull { it.determineRuntime(project) }.firstOrNull() + registeredRuntimeGroups().asSequence().mapNotNull { it.determineRuntime(project) }.firstOrNull() } fun determineRuntime(module: Module?): Runtime? = module?.let { _ -> - values().asSequence().mapNotNull { it.determineRuntime(module) }.firstOrNull() + registeredRuntimeGroups().asSequence().mapNotNull { it.determineRuntime(module) }.firstOrNull() } fun determineRuntimeGroup(project: Project?): RuntimeGroup? = project?.let { _ -> - values().asSequence().find { it.determineRuntime(project) != null } + registeredRuntimeGroups().find { it.determineRuntime(project) != null } } - } -} - -/** - * Represents information about a specific [Runtime] or [RuntimeGroup]. A single [RuntimeGroup] can have more than one RuntimeGroupInformation - * registered. - */ -interface RuntimeGroupInformation { - val runtimes: Set - val languageIds: Set - /** - * Attempt to determine the runtime from the [project] level scope. - */ - fun determineRuntime(project: Project): Runtime? - - /** - * Attempt to determine the runtime from the [module] level scope. - * Do not fall back to [Project] level scope; logic controlling fallback to [Project] scope should be done at the call-site. - */ - fun determineRuntime(module: Module): Runtime? - - /** - * The IDE module type that should be associated with this runtime group - */ - fun getModuleType(): ModuleType<*>? - - /** - * The IDE SDK type that this runtime group supports - */ - fun getIdeSdkType(): SdkType? - - /** - * Whether this runtime group supports SAM build so that SAM template with runtimes of this type could be deployed to AWS. - */ - fun supportsSamBuild(): Boolean - - companion object : RuntimeGroupExtensionPointObject(ExtensionPointName("aws.toolkit.lambda.runtimeGroup")) { - fun getInstances(runtimeGroup: RuntimeGroup): List = collector.forKey(runtimeGroup) + fun registeredRuntimeGroups(): List = EP_NAM.extensionList } } -abstract class SdkBasedRuntimeGroupInformation : RuntimeGroupInformation { +abstract class SdkBasedRuntimeGroup : RuntimeGroup { protected abstract fun runtimeForSdk(sdk: Sdk): Runtime? override fun determineRuntime(project: Project): Runtime? = ProjectRootManager.getInstance(project).projectSdk?.let { runtimeForSdk(it) } override fun determineRuntime(module: Module): Runtime? = ModuleRootManager.getInstance(module).sdk?.let { runtimeForSdk(it) } - - override fun getModuleType(): ModuleType<*>? = null - - override fun getIdeSdkType(): SdkType? = null - - override fun supportsSamBuild(): Boolean = false } val Runtime?.validOrNull: Runtime? get() = this?.takeUnless { it == Runtime.UNKNOWN_TO_SDK_VERSION } @@ -139,38 +95,24 @@ fun AnActionEvent.runtime(): Runtime? { } /** - * A bean that represents an extension point based on a [RuntimeGroup] + * To be implemented on a companion object of the extension point object to expose factory methods. + * See [software.aws.toolkits.jetbrains.services.lambda.LambdaBuilder] */ -class RuntimeGroupExtensionPoint : AbstractExtensionPointBean(), KeyedLazyInstance { - - @Attribute("implementation") - lateinit var implementation: String +abstract class RuntimeGroupExtensionPointObject(private val extensionPointName: ExtensionPointName>) { + private val collector = KeyedExtensionCollector(extensionPointName.name) - /** - * The [RuntimeGroup] that this extension point refers to - */ - @Attribute("runtimeGroup") - lateinit var runtimeGroup: RuntimeGroup + fun getInstanceOrNull(runtimeGroup: RuntimeGroup): T? = collector.findSingle(runtimeGroup.id) + fun getInstance(runtimeGroup: RuntimeGroup): T = getInstanceOrNull(runtimeGroup) + ?: throw IllegalStateException("Attempted to retrieve feature for unsupported runtime group $runtimeGroup") - private val instance = object : LazyInstance() { - override fun getInstanceClass(): Class = findClass(implementation) + fun supportedRuntimeGroups(): Set { + val alRuntimeGroups = RuntimeGroup.registeredRuntimeGroups() + val supportedIds = extensionPointName.extensions.map { it.id } + return alRuntimeGroups.filter { supportedIds.contains(it.id) }.toSet() } - override fun getKey(): String = runtimeGroup.name - - override fun getInstance(): T = instance.value -} - -/** - * To be implemented on a companion object of the extension point object to expose factory methods. - * See [software.aws.toolkits.jetbrains.services.lambda.LambdaBuilder] - */ -abstract class RuntimeGroupExtensionPointObject(private val extensionPointName: ExtensionPointName>) { - protected val collector = KeyedExtensionCollector(extensionPointName.name) - fun getInstance(runtimeGroup: RuntimeGroup): T? = collector.findSingle(runtimeGroup) - fun getInstanceOrThrow(runtimeGroup: RuntimeGroup): T = - getInstance(runtimeGroup) ?: throw IllegalStateException("Attempted to retrieve feature for unsupported runtime group $runtimeGroup") - - val supportedRuntimeGroups: Set by lazy { extensionPointName.extensions.map { it.runtimeGroup }.toSet() } - val supportedLanguages: Set by lazy { supportedRuntimeGroups.flatMap { it.languageIds }.mapNotNull { Language.findLanguageByID(it) }.toSet() } + fun supportedLanguages(): Set { + val supportedRuntimeGroups = supportedRuntimeGroups() + return supportedRuntimeGroups.asSequence().flatMap { it.languageIds.asSequence() }.mapNotNull { Language.findLanguageByID(it) }.toSet() + } } diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/SamProjectWizard.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/SamProjectWizard.kt index a53de60744..4c7f7888b3 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/SamProjectWizard.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/SamProjectWizard.kt @@ -187,8 +187,8 @@ abstract class SamProjectTemplate { @JvmField val SAM_TEMPLATES = - SamProjectWizard.supportedRuntimeGroups.flatMap { - SamProjectWizard.getInstanceOrThrow(it).listTemplates() + SamProjectWizard.supportedRuntimeGroups().flatMap { + SamProjectWizard.getInstance(it).listTemplates() } } } 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 index 1e2f666655..68dc71a3ff 100644 --- 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 @@ -153,7 +153,7 @@ class DeployServerlessApplicationAction : AnAction( super.update(e) // If there are no supported runtime groups, it will never succeed so don't show it - e.presentation.isVisible = if (LambdaHandlerResolver.supportedRuntimeGroups.isEmpty()) { + e.presentation.isVisible = if (LambdaHandlerResolver.supportedRuntimeGroups().isEmpty()) { false } else { getSamTemplateFile(e) != null diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/actions/UpdateFunctionAction.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/actions/UpdateFunctionAction.kt index 4fa721eda8..b69857f74a 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/actions/UpdateFunctionAction.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/actions/UpdateFunctionAction.kt @@ -53,7 +53,7 @@ class UpdateFunctionConfigurationAction : UpdateFunctionAction(EditFunctionMode. class UpdateFunctionCodeAction : UpdateFunctionAction(EditFunctionMode.UPDATE_CODE, message("lambda.function.updateCode.action")) { override fun update(selected: LambdaFunctionNode, e: AnActionEvent) { - if (selected.value.runtime.runtimeGroup?.let { LambdaBuilder.getInstance(it) } != null) { + if (selected.value.runtime.runtimeGroup?.let { LambdaBuilder.getInstanceOrNull(it) } != null) { return } e.presentation.isVisible = false diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/completion/HandlerCompletionProvider.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/completion/HandlerCompletionProvider.kt index 87712775a8..efb83fd14b 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/completion/HandlerCompletionProvider.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/completion/HandlerCompletionProvider.kt @@ -13,7 +13,6 @@ import software.aws.toolkits.core.utils.getLogger import software.aws.toolkits.core.utils.info import software.aws.toolkits.jetbrains.services.lambda.RuntimeGroup import software.aws.toolkits.jetbrains.services.lambda.runtimeGroup -import java.lang.IllegalStateException class HandlerCompletionProvider(private val project: Project, runtime: Runtime?) : TextCompletionProvider { @@ -22,8 +21,8 @@ class HandlerCompletionProvider(private val project: Project, runtime: Runtime?) private val handlerCompletion: HandlerCompletion? by lazy { val runtimeGroup = runtime?.runtimeGroup ?: RuntimeGroup.determineRuntime(project)?.runtimeGroup ?: return@lazy null - return@lazy HandlerCompletion.getInstance(runtimeGroup) ?: let { - logger.info { "Lambda handler completion provider is not registered for runtime: ${runtimeGroup.name}. Completion is not supported." } + return@lazy HandlerCompletion.getInstanceOrNull(runtimeGroup) ?: let { + logger.info { "Lambda handler completion provider is not registered for runtime: ${runtimeGroup.id}. Completion is not supported." } null } } diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/execution/LambdaRunConfigurationType.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/execution/LambdaRunConfigurationType.kt index d2e960aedc..d063b9591e 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/execution/LambdaRunConfigurationType.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/execution/LambdaRunConfigurationType.kt @@ -20,10 +20,10 @@ class LambdaRunConfigurationType : AwsIcons.Resources.LAMBDA_FUNCTION ) { init { - // Although it should work, isApplicable doesn't seem to work for locallambdarunconfigurationfactory - // and it still shows up when it is not applicalbe. So we have to decide in the configuration to add it or not. + // Although it should work, isApplicable doesn't seem to work for LocalLambdaRunConfigurationFactory + // and it still shows up when it is not applicable. So we have to decide in the configuration to add it or not. // TODO see if this is resolvable - if (LambdaHandlerResolver.supportedRuntimeGroups.isNotEmpty()) { + if (LambdaHandlerResolver.supportedRuntimeGroups().isNotEmpty()) { addFactory(LocalLambdaRunConfigurationFactory(this)) } addFactory(RemoteLambdaRunConfigurationFactory(this)) diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/execution/local/LocalLambdaRunConfiguration.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/execution/local/LocalLambdaRunConfiguration.kt index 43a9d498aa..1bf8c3b862 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/execution/local/LocalLambdaRunConfiguration.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/execution/local/LocalLambdaRunConfiguration.kt @@ -143,7 +143,7 @@ class LocalLambdaRunConfiguration(project: Project, factory: ConfigurationFactor override fun getRefactoringElementListener(element: PsiElement?): RefactoringElementListener? { element?.run { val handlerResolver = element.language.runtimeGroup?.let { runtimeGroup -> - LambdaHandlerResolver.getInstance(runtimeGroup) + LambdaHandlerResolver.getInstanceOrNull(runtimeGroup) } ?: return null val handlerPsi = handlerPsiElement() ?: return null @@ -262,7 +262,7 @@ class LocalLambdaRunConfiguration(project: Project, factory: ConfigurationFactor val handler = serializableOptions.functionOptions.handler ?: return null return runtime() ?.runtimeGroup - ?.let { LambdaHandlerResolver.getInstance(it) } + ?.let { LambdaHandlerResolver.getInstanceOrNull(it) } ?.handlerDisplayName(handler) ?: handler } diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/execution/local/LocalLambdaRunConfigurationProducer.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/execution/local/LocalLambdaRunConfigurationProducer.kt index 2deb337efd..d0793efb13 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/execution/local/LocalLambdaRunConfigurationProducer.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/execution/local/LocalLambdaRunConfigurationProducer.kt @@ -52,10 +52,10 @@ class LocalLambdaRunConfigurationProducer : LazyRunConfigurationProducer().clearRequests() - val supported = LambdaBuilder.supportedRuntimeGroups.flatMap { it.runtimes }.sorted() + val supported = LambdaBuilder.supportedRuntimeGroups().flatMap { it.runtimes }.sorted() val selected = RuntimeGroup.determineRuntime(project)?.let { if (it in supported) it else null } view = LocalLambdaRunSettingsEditorPanel(project) diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/execution/local/SamDebugger.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/execution/local/SamDebugger.kt index b2ee2f3731..8945727c40 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/execution/local/SamDebugger.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/execution/local/SamDebugger.kt @@ -24,7 +24,7 @@ internal class SamDebugger(runtimeGroup: RuntimeGroup) : SamRunner() { private val logger = getLogger() } - private val debugExtension = SamDebugSupport.getInstanceOrThrow(runtimeGroup) + private val debugExtension = SamDebugSupport.getInstance(runtimeGroup) private val debugPorts = debugExtension.getDebugPorts() diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/execution/local/SamInvokeRunner.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/execution/local/SamInvokeRunner.kt index 6091d110e1..2c389c265a 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/execution/local/SamInvokeRunner.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/execution/local/SamInvokeRunner.kt @@ -66,8 +66,8 @@ class SamInvokeRunner : AsyncProgramRunner() { val runtimeGroup = runtimeValue?.runtimeGroup ?: return false - return SamDebugSupport.supportedRuntimeGroups.contains(runtimeGroup) && - SamDebugSupport.getInstance(runtimeGroup)?.isSupported(runtimeValue) ?: false + return SamDebugSupport.supportedRuntimeGroups().contains(runtimeGroup) && + SamDebugSupport.getInstanceOrNull(runtimeGroup)?.isSupported(runtimeValue) ?: false } return false diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/java/JavaRuntimeGroup.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/java/JavaRuntimeGroup.kt index a28cf616e0..bc378d2502 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/java/JavaRuntimeGroup.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/java/JavaRuntimeGroup.kt @@ -12,9 +12,11 @@ import com.intellij.openapi.projectRoots.JavaSdkVersion import com.intellij.openapi.projectRoots.Sdk import com.intellij.openapi.projectRoots.SdkType import software.amazon.awssdk.services.lambda.model.Runtime -import software.aws.toolkits.jetbrains.services.lambda.SdkBasedRuntimeGroupInformation +import software.aws.toolkits.jetbrains.services.lambda.BuiltInRuntimeGroups +import software.aws.toolkits.jetbrains.services.lambda.SdkBasedRuntimeGroup -class JavaRuntimeGroup : SdkBasedRuntimeGroupInformation() { +class JavaRuntimeGroup : SdkBasedRuntimeGroup() { + override val id: String = BuiltInRuntimeGroups.Java override val runtimes = setOf(Runtime.JAVA8, Runtime.JAVA11) override val languageIds = setOf(JavaLanguage.INSTANCE.id) diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/java/JavaSamProjectWizard.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/java/JavaSamProjectWizard.kt index 7fee1caa96..29467710f2 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/java/JavaSamProjectWizard.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/java/JavaSamProjectWizard.kt @@ -20,7 +20,7 @@ import org.jetbrains.plugins.gradle.util.GradleConstants import software.amazon.awssdk.services.lambda.model.Runtime import software.aws.toolkits.core.utils.getLogger import software.aws.toolkits.core.utils.logWhenNull -import software.aws.toolkits.jetbrains.services.lambda.RuntimeGroup +import software.aws.toolkits.jetbrains.services.lambda.BuiltInRuntimeGroups import software.aws.toolkits.jetbrains.services.lambda.SamNewProjectSettings import software.aws.toolkits.jetbrains.services.lambda.SamProjectTemplate import software.aws.toolkits.jetbrains.services.lambda.SamProjectWizard @@ -37,13 +37,11 @@ import software.aws.toolkits.resources.message import java.nio.file.Paths class JavaSamProjectWizard : SamProjectWizard { - override fun createSchemaSelectionPanel( - generator: SamProjectGenerator - ): SchemaSelectionPanel = - SchemaResourceSelectorSelectionPanel(generator.builder, RuntimeGroup.JAVA, generator.defaultSourceCreatingProject) + override fun createSchemaSelectionPanel(generator: SamProjectGenerator): SchemaSelectionPanel = + SchemaResourceSelectorSelectionPanel(generator.builder, generator.defaultSourceCreatingProject) override fun createSdkSelectionPanel(generator: SamProjectGenerator): SdkSelectionPanel = - IntelliJSdkSelectionPanel(generator.builder, RuntimeGroup.JAVA) + IntelliJSdkSelectionPanel(generator.builder, BuiltInRuntimeGroups.Java) override fun listTemplates(): Collection = listOf( SamHelloWorldMaven(), diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/python/PythonRuntimeGroup.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/python/PythonRuntimeGroup.kt index 486ee0b247..35965cc349 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/python/PythonRuntimeGroup.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/python/PythonRuntimeGroup.kt @@ -11,34 +11,30 @@ import com.jetbrains.python.PythonModuleTypeBase import com.jetbrains.python.psi.LanguageLevel import com.jetbrains.python.sdk.PythonSdkType import software.amazon.awssdk.services.lambda.model.Runtime -import software.aws.toolkits.jetbrains.services.lambda.SdkBasedRuntimeGroupInformation - -class PythonRuntimeGroup : SdkBasedRuntimeGroupInformation() { +import software.aws.toolkits.jetbrains.services.lambda.BuiltInRuntimeGroups +import software.aws.toolkits.jetbrains.services.lambda.SdkBasedRuntimeGroup +class PythonRuntimeGroup : SdkBasedRuntimeGroup() { + override val id: String = BuiltInRuntimeGroups.Python override val runtimes: Set = setOf( Runtime.PYTHON2_7, Runtime.PYTHON3_6, Runtime.PYTHON3_7, Runtime.PYTHON3_8 ) - override val languageIds: Set = setOf(PythonLanguage.INSTANCE.id) - override fun runtimeForSdk(sdk: Sdk): Runtime? = determineRuntimeForSdk(sdk) + override fun runtimeForSdk(sdk: Sdk): Runtime? = when { + sdk.sdkType is PythonSdkType && PythonSdkType.getLanguageLevelForSdk(sdk).isAtLeast(LanguageLevel.PYTHON38) -> Runtime.PYTHON3_8 + sdk.sdkType is PythonSdkType && PythonSdkType.getLanguageLevelForSdk(sdk).isAtLeast(LanguageLevel.PYTHON37) -> Runtime.PYTHON3_7 + sdk.sdkType is PythonSdkType && PythonSdkType.getLanguageLevelForSdk(sdk).isPy3K -> Runtime.PYTHON3_6 + sdk.sdkType is PythonSdkType && PythonSdkType.getLanguageLevelForSdk(sdk).isPython2 -> Runtime.PYTHON2_7 + else -> null + } override fun getModuleType(): ModuleType<*> = PythonModuleTypeBase.getInstance() override fun getIdeSdkType(): SdkType = PythonSdkType.getInstance() override fun supportsSamBuild(): Boolean = true - - companion object { - fun determineRuntimeForSdk(sdk: Sdk) = when { - sdk.sdkType is PythonSdkType && PythonSdkType.getLanguageLevelForSdk(sdk).isAtLeast(LanguageLevel.PYTHON38) -> Runtime.PYTHON3_8 - sdk.sdkType is PythonSdkType && PythonSdkType.getLanguageLevelForSdk(sdk).isAtLeast(LanguageLevel.PYTHON37) -> Runtime.PYTHON3_7 - sdk.sdkType is PythonSdkType && PythonSdkType.getLanguageLevelForSdk(sdk).isPy3K -> Runtime.PYTHON3_6 - sdk.sdkType is PythonSdkType && PythonSdkType.getLanguageLevelForSdk(sdk).isPython2 -> Runtime.PYTHON2_7 - else -> null - } - } } diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/python/PythonSamProjectWizard.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/python/PythonSamProjectWizard.kt index 55ae5507e3..25adad9185 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/python/PythonSamProjectWizard.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/python/PythonSamProjectWizard.kt @@ -9,7 +9,7 @@ import com.intellij.openapi.roots.ModifiableRootModel import com.intellij.openapi.vfs.VirtualFile import com.intellij.util.PlatformUtils import software.amazon.awssdk.services.lambda.model.Runtime -import software.aws.toolkits.jetbrains.services.lambda.RuntimeGroup +import software.aws.toolkits.jetbrains.services.lambda.BuiltInRuntimeGroups import software.aws.toolkits.jetbrains.services.lambda.SamNewProjectSettings import software.aws.toolkits.jetbrains.services.lambda.SamProjectTemplate import software.aws.toolkits.jetbrains.services.lambda.SamProjectWizard @@ -29,16 +29,13 @@ import software.aws.toolkits.resources.message import java.nio.file.Paths class PythonSamProjectWizard : SamProjectWizard { - override fun createSchemaSelectionPanel( - generator: SamProjectGenerator - ): SchemaSelectionPanel = - SchemaResourceSelectorSelectionPanel(generator.builder, RuntimeGroup.PYTHON, generator.defaultSourceCreatingProject) - - override fun createSdkSelectionPanel(generator: SamProjectGenerator): SdkSelectionPanel = - when { - PlatformUtils.isPyCharm() -> PyCharmSdkSelectionPanel(generator.step) - else -> IntelliJSdkSelectionPanel(generator.builder, RuntimeGroup.PYTHON) - } + override fun createSchemaSelectionPanel(generator: SamProjectGenerator): SchemaSelectionPanel = + SchemaResourceSelectorSelectionPanel(generator.builder, generator.defaultSourceCreatingProject) + + override fun createSdkSelectionPanel(generator: SamProjectGenerator): SdkSelectionPanel = when { + PlatformUtils.isPyCharm() -> PyCharmSdkSelectionPanel(generator.step) + else -> IntelliJSdkSelectionPanel(generator.builder, BuiltInRuntimeGroups.Python) + } override fun listTemplates(): Collection = listOf( SamHelloWorldPython(), diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/upload/EditFunctionDialog.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/upload/EditFunctionDialog.kt index efbffefa47..07b05e6826 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/upload/EditFunctionDialog.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/upload/EditFunctionDialog.kt @@ -217,7 +217,7 @@ class EditFunctionDialog( val s3Bucket = view.sourceBucket.selectedItem as String - val lambdaBuilder = psiFile.language.runtimeGroup?.let { LambdaBuilder.getInstance(it) } ?: return + val lambdaBuilder = psiFile.language.runtimeGroup?.let { LambdaBuilder.getInstanceOrNull(it) } ?: return val lambdaCreator = LambdaCreatorFactory.create(AwsClientManager.getInstance(project), lambdaBuilder) FileDocumentManager.getInstance().saveAllDocuments() @@ -338,7 +338,7 @@ class UploadToLambdaValidator { val runtime = view.runtime.selected() ?: return ValidationInfo(message("lambda.upload_validation.runtime"), view.runtime) - runtime.runtimeGroup?.let { LambdaBuilder.getInstance(it) } ?: return ValidationInfo( + runtime.runtimeGroup?.let { LambdaBuilder.getInstanceOrNull(it) } ?: return ValidationInfo( message("lambda.upload_validation.unsupported_runtime", runtime), view.runtime ) diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/upload/LambdaLineMarker.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/upload/LambdaLineMarker.kt index a3f624d45e..507fcb6545 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/upload/LambdaLineMarker.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/upload/LambdaLineMarker.kt @@ -44,7 +44,7 @@ class LambdaLineMarker : LineMarkerProviderDescriptor() { } val runtimeGroup = element.language.runtimeGroup ?: return null - val handlerResolver = LambdaHandlerResolver.getInstance(runtimeGroup) ?: return null + val handlerResolver = LambdaHandlerResolver.getInstanceOrNull(runtimeGroup) ?: return null val handler = handlerResolver.determineHandler(element) ?: return null return if (handlerResolver.shouldShowLineMarker(handler) || shouldShowLineMarker(element.containingFile, handler, runtimeGroup)) { @@ -52,7 +52,7 @@ class LambdaLineMarker : LineMarkerProviderDescriptor() { val smartPsiElementPointer = SmartPointerManager.createPointer(element) - if (element.language in LambdaBuilder.supportedLanguages) { + if (element.language in LambdaBuilder.supportedLanguages()) { val executorActions = ExecutorAction.getActions(1) executorActions.forEach { actionGroup.add(LineMarkerActionWrapper(element, it)) diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/schemas/Schemas.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/schemas/Schemas.kt index f2ef12f82d..63e44a67b1 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/schemas/Schemas.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/schemas/Schemas.kt @@ -5,18 +5,18 @@ package software.aws.toolkits.jetbrains.services.schemas import com.fasterxml.jackson.annotation.JsonProperty import software.amazon.awssdk.services.schemas.model.SchemaSummary -import software.aws.toolkits.jetbrains.services.lambda.RuntimeGroup +import software.aws.toolkits.jetbrains.services.lambda.BuiltInRuntimeGroups import software.aws.toolkits.resources.message enum class SchemaCodeLangs( val apiValue: String, val text: String, val extension: String, - val runtimeGroup: RuntimeGroup + val runtimeGroupId: String ) { - JAVA8("Java8", message("schemas.schema.SchemaCodeLangs.JAVA8"), "java", RuntimeGroup.JAVA), - PYTHON3_6("Python36", message("schemas.schema.SchemaCodeLangs.PYTHON3_6"), "py", RuntimeGroup.PYTHON), - TYPESCRIPT("TypeScript3", message("schemas.schema.SchemaCodeLangs.TYPESCRIPT"), "ts", RuntimeGroup.NODEJS); + JAVA8("Java8", message("schemas.schema.SchemaCodeLangs.JAVA8"), "java", BuiltInRuntimeGroups.Java), + PYTHON3_6("Python36", message("schemas.schema.SchemaCodeLangs.PYTHON3_6"), "py", BuiltInRuntimeGroups.Python), + TYPESCRIPT("TypeScript3", message("schemas.schema.SchemaCodeLangs.TYPESCRIPT"), "ts", BuiltInRuntimeGroups.NodeJs); override fun toString() = text } diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/schemas/code/DownloadCodeForSchemaDialog.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/schemas/code/DownloadCodeForSchemaDialog.kt index 3ff4a54e14..e8a9b7e693 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/schemas/code/DownloadCodeForSchemaDialog.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/schemas/code/DownloadCodeForSchemaDialog.kt @@ -46,8 +46,8 @@ class DownloadCodeForSchemaDialog( private val project: Project, private val schemaName: String = "", private val registryName: String = "", - private val version: String? = null, - private val language: SchemaCodeLangs? = null, + version: String? = null, + language: SchemaCodeLangs? = null, private val onClose: (() -> Unit)? = null ) : DialogWrapper(project) { @@ -62,7 +62,7 @@ class DownloadCodeForSchemaDialog( val latestVersion: String val view = DownloadCodeForSchemaPanel(project, this) - val validator = DownloadCodeForSchemaValidator() + private val validator = DownloadCodeForSchemaValidator() private val action: OkAction = DownloadCodeForSchemaOkAction() @@ -90,14 +90,14 @@ class DownloadCodeForSchemaDialog( private fun getContentRootOfCurrentFile(): String? { // Get the currently open files (plural in case they are split) val selectedFiles = FileEditorManager.getInstance(project).getSelectedFiles() - if (!selectedFiles.isEmpty()) { + if (selectedFiles.isNotEmpty()) { // return the content root of the first selected file return ProjectFileIndex.getInstance(project).getContentRootForFile(selectedFiles.first())?.path } // Otherwise, find the first content root of the project, and return that val contentRoots = ProjectRootManager.getInstance(project).contentRoots - if (!contentRoots.isEmpty()) { - return contentRoots.first()?.path + if (contentRoots.isNotEmpty()) { + return contentRoots.first().path } return null @@ -112,7 +112,7 @@ class DownloadCodeForSchemaDialog( private fun getLanguageForCurrentRuntime(): SchemaCodeLangs? { val currentRuntimeGroup = RuntimeGroup.determineRuntimeGroup(project) ?: return null - return SchemaCodeLangs.values().firstOrNull { it.runtimeGroup.equals(currentRuntimeGroup) } + return SchemaCodeLangs.values().firstOrNull { it.runtimeGroupId == currentRuntimeGroup.id } } override fun createCenterPanel(): JComponent? = view.content @@ -170,9 +170,9 @@ class DownloadCodeForSchemaDialog( private fun refreshDownloadCodeDirectory(schemaCodeDownloadDetails: SchemaCodeDownloadRequestDetails) { val file = File(schemaCodeDownloadDetails.destinationDirectory) - // Don't replace this with LocalFileSystem.getInstance().refreshIoFiles(listOf(file)) - it doesn't work. - val vFile = LocalFileSystem.getInstance().findFileByIoFile(file) - VfsUtil.markDirtyAndRefresh(false, true, true, vFile) + LocalFileSystem.getInstance().findFileByIoFile(file)?.let { + VfsUtil.markDirtyAndRefresh(false, true, true, it) + } } private fun showDownloadCompletionNotification( @@ -188,8 +188,7 @@ class DownloadCodeForSchemaDialog( error: Throwable?, project: Project ) { - val rootError = ExceptionUtils.getRootCause(error) - when (rootError) { + when (val rootError = ExceptionUtils.getRootCause(error)) { is SchemaCodeDownloadFileCollisionException -> notifyError(title = NOTIFICATION_TITLE, content = rootError.message ?: "", project = project) is Exception -> rootError.notifyError(title = NOTIFICATION_TITLE, project = project) } @@ -263,12 +262,13 @@ class DownloadCodeForSchemaValidator { return ValidationInfo(message("schemas.schema.download_code_bindings.validation.language_required"), view.language) } - val locationText = view.location.getText() - if (locationText.isNullOrEmpty()) { + val locationText = view.location.text + if (locationText.isEmpty()) { return ValidationInfo(message("schemas.schema.download_code_bindings.validation.fileLocation_required"), view.location) } + val file = File(locationText) - if (!file.exists() || !file.isDirectory()) { + if (!file.exists() || !file.isDirectory) { return ValidationInfo(message("schemas.schema.download_code_bindings.validation.fileLocation_invalid"), view.location) } diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/ui/wizard/IntelliJSdkSelectionPanel.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/ui/wizard/IntelliJSdkSelectionPanel.kt index a05cd471ca..ed6c496964 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/ui/wizard/IntelliJSdkSelectionPanel.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/ui/wizard/IntelliJSdkSelectionPanel.kt @@ -8,6 +8,7 @@ import com.intellij.ide.util.projectWizard.WizardContext import com.intellij.openapi.projectRoots.Sdk import com.intellij.openapi.projectRoots.SdkTypeId import com.intellij.openapi.ui.ValidationInfo +import software.aws.toolkits.jetbrains.services.lambda.BuiltInRuntimeGroups import software.aws.toolkits.jetbrains.services.lambda.RuntimeGroup import software.aws.toolkits.jetbrains.services.lambda.SdkBasedSdkSettings import software.aws.toolkits.jetbrains.services.lambda.SdkSettings @@ -15,7 +16,7 @@ import software.aws.toolkits.resources.message import javax.swing.JComponent import javax.swing.JLabel -class IntelliJSdkSelectionPanel(val builder: SamProjectBuilder, val runtimeGroup: RuntimeGroup) : SdkSelectionPanelBase() { +class IntelliJSdkSelectionPanel(private val builder: SamProjectBuilder, private val runtimeGroupId: String) : SdkSelectionPanelBase() { private var currentSdk: Sdk? = null private val dummyContext = object : WizardContext(null, {}) { override fun setProjectJdk(sdk: Sdk?) { @@ -40,10 +41,11 @@ class IntelliJSdkSelectionPanel(val builder: SamProjectBuilder, val runtimeGroup override fun getSdkSettings(): SdkSettings { currentSdkPanel.updateDataModel() - return when (runtimeGroup) { - RuntimeGroup.JAVA, RuntimeGroup.PYTHON -> SdkBasedSdkSettings(sdk = currentSdk) - RuntimeGroup.DOTNET -> object : SdkSettings {} - else -> throw RuntimeException("Unrecognized runtime group: $runtimeGroup") + // TODO: This should probably be EP based + return when (runtimeGroupId) { + BuiltInRuntimeGroups.Java, BuiltInRuntimeGroups.Python -> SdkBasedSdkSettings(sdk = currentSdk) + BuiltInRuntimeGroups.Dotnet -> object : SdkSettings {} + else -> throw RuntimeException("Unrecognized runtime group ID: $runtimeGroupId") } } @@ -52,7 +54,7 @@ class IntelliJSdkSelectionPanel(val builder: SamProjectBuilder, val runtimeGroup SdkSettingsStep( dummyContext, builder, - { t: SdkTypeId? -> t == runtimeGroup.getIdeSdkType() }, + { t: SdkTypeId? -> t == RuntimeGroup.getById(runtimeGroupId).getIdeSdkType() }, null ) } diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/ui/wizard/SamInitSelectionPanel.java b/jetbrains-core/src/software/aws/toolkits/jetbrains/ui/wizard/SamInitSelectionPanel.java index c6f01be797..5358b4b4b6 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/ui/wizard/SamInitSelectionPanel.java +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/ui/wizard/SamInitSelectionPanel.java @@ -83,7 +83,7 @@ public class SamInitSelectionPanel implements ValidatablePanel { // TODO: Move this to Kotlin... // Source all templates, find all the runtimes they support, then filter those by what the IDE supports - Set supportedRuntimeGroups = LambdaBuilder.Companion.getSupportedRuntimeGroups(); + Set supportedRuntimeGroups = LambdaBuilder.Companion.supportedRuntimeGroups(); SamProjectTemplate.SAM_TEMPLATES.stream() .flatMap(template -> template.supportedRuntimes().stream()) .sorted() diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/ui/wizard/SchemaResourceSelectorSelectionPanel.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/ui/wizard/SchemaResourceSelectorSelectionPanel.kt index 9d530327b9..e71181245a 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/ui/wizard/SchemaResourceSelectorSelectionPanel.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/ui/wizard/SchemaResourceSelectorSelectionPanel.kt @@ -7,7 +7,6 @@ import com.intellij.openapi.project.Project import com.intellij.openapi.ui.ValidationInfo import com.intellij.ui.ComboboxSpeedSearch import software.aws.toolkits.jetbrains.core.credentials.AwsConnectionManager -import software.aws.toolkits.jetbrains.services.lambda.RuntimeGroup import software.aws.toolkits.jetbrains.services.schemas.resources.SchemasResources import software.aws.toolkits.jetbrains.ui.AwsConnection import software.aws.toolkits.jetbrains.ui.ResourceSelector @@ -19,14 +18,12 @@ import javax.swing.JPanel class SchemaResourceSelectorSelectionPanel( val builder: SamProjectBuilder, - val runtimeGroup: RuntimeGroup, val project: Project, // Subsequent parameters injectable for unit tests to enable mocking because ResourceSelector has inconsistent unit test behaviour val resourceSelectorBuilder: ResourceSelector.ResourceBuilder = ResourceSelector.builder(project), useSpeedSearch: Boolean = true, rootPanelBuilder: () -> JPanel = { JPanel(BorderLayout()) } -) : - SchemaSelectionPanelBase(project) { +) : SchemaSelectionPanelBase(project) { override val schemaSelectionLabel: JLabel? = JLabel(message("sam.init.schema.label")) diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/ui/wizard/SchemaSelectionPanel.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/ui/wizard/SchemaSelectionPanel.kt index ac0346309f..7e435f5b2c 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/ui/wizard/SchemaSelectionPanel.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/ui/wizard/SchemaSelectionPanel.kt @@ -45,7 +45,7 @@ interface SchemaSelectionPanel { ): SchemaSelectionPanel = runtime.runtimeGroup?.let { runtimeGroup -> if (selectedTemplate.supportsDynamicSchemas()) - SamProjectWizard.getInstanceOrThrow(runtimeGroup).createSchemaSelectionPanel(generator) + SamProjectWizard.getInstance(runtimeGroup).createSchemaSelectionPanel(generator) else NoOpSchemaSelectionPanel() } ?: NoOpSchemaSelectionPanel() diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/ui/wizard/SdkSelectionPanel.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/ui/wizard/SdkSelectionPanel.kt index c450d88cab..81d1ad0f92 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/ui/wizard/SdkSelectionPanel.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/ui/wizard/SdkSelectionPanel.kt @@ -29,7 +29,7 @@ interface SdkSelectionPanel { @JvmStatic fun create(runtime: Runtime, generator: SamProjectGenerator): SdkSelectionPanel = runtime.runtimeGroup?.let { - SamProjectWizard.getInstanceOrThrow(it).createSdkSelectionPanel(generator) + SamProjectWizard.getInstance(it).createSdkSelectionPanel(generator) } ?: NoOpSdkSelectionPanel() } } diff --git a/jetbrains-core/tst/software/aws/toolkits/jetbrains/services/lambda/java/JavaLambdaHandlerResolverTest.kt b/jetbrains-core/tst/software/aws/toolkits/jetbrains/services/lambda/java/JavaLambdaHandlerResolverTest.kt index cad0d8f3a0..aa19183a32 100644 --- a/jetbrains-core/tst/software/aws/toolkits/jetbrains/services/lambda/java/JavaLambdaHandlerResolverTest.kt +++ b/jetbrains-core/tst/software/aws/toolkits/jetbrains/services/lambda/java/JavaLambdaHandlerResolverTest.kt @@ -13,6 +13,7 @@ import org.junit.Before import org.junit.Rule import org.junit.Test import software.amazon.awssdk.services.lambda.model.Runtime +import software.aws.toolkits.jetbrains.services.lambda.BuiltInRuntimeGroups import software.aws.toolkits.jetbrains.services.lambda.Lambda import software.aws.toolkits.jetbrains.services.lambda.LambdaHandlerResolver import software.aws.toolkits.jetbrains.services.lambda.RuntimeGroup @@ -418,7 +419,7 @@ class JavaLambdaHandlerResolverTest { @Test fun handlerDisplayNames() { - val sut = LambdaHandlerResolver.getInstanceOrThrow(RuntimeGroup.JAVA) + val sut = LambdaHandlerResolver.getInstance(RuntimeGroup.getById(BuiltInRuntimeGroups.Java)) assertThat(sut.handlerDisplayName("com.example.LambdaHandler::handleRequest")).isEqualTo("LambdaHandler.handleRequest") assertThat(sut.handlerDisplayName("com.example.LambdaHandler")).isEqualTo("LambdaHandler") diff --git a/jetbrains-core/tst/software/aws/toolkits/jetbrains/services/lambda/python/PythonLambdaHandlerResolverTest.kt b/jetbrains-core/tst/software/aws/toolkits/jetbrains/services/lambda/python/PythonLambdaHandlerResolverTest.kt index a15d506c15..3d2a47c67c 100644 --- a/jetbrains-core/tst/software/aws/toolkits/jetbrains/services/lambda/python/PythonLambdaHandlerResolverTest.kt +++ b/jetbrains-core/tst/software/aws/toolkits/jetbrains/services/lambda/python/PythonLambdaHandlerResolverTest.kt @@ -257,7 +257,7 @@ class PythonLambdaHandlerResolverTest { } } - private fun getHandlerResolver() = Runtime.PYTHON3_6.runtimeGroup?.let { LambdaHandlerResolver.getInstance(it) }!! + private fun getHandlerResolver() = Runtime.PYTHON3_6.runtimeGroup?.let { LambdaHandlerResolver.getInstanceOrNull(it) }!! private fun findHandler(handler: String): Array = Lambda.findPsiElementsForHandler(projectRule.project, Runtime.PYTHON3_6, handler) } diff --git a/jetbrains-core/tst/software/aws/toolkits/jetbrains/ui/wizard/SchemaSelectionPanelTest.kt b/jetbrains-core/tst/software/aws/toolkits/jetbrains/ui/wizard/SchemaSelectionPanelTest.kt index 75b9ab6494..08ef1c99db 100644 --- a/jetbrains-core/tst/software/aws/toolkits/jetbrains/ui/wizard/SchemaSelectionPanelTest.kt +++ b/jetbrains-core/tst/software/aws/toolkits/jetbrains/ui/wizard/SchemaSelectionPanelTest.kt @@ -16,7 +16,6 @@ import org.junit.Test import software.amazon.awssdk.services.schemas.model.DescribeSchemaResponse import software.aws.toolkits.jetbrains.core.MockResourceCache import software.aws.toolkits.jetbrains.core.Resource -import software.aws.toolkits.jetbrains.services.lambda.RuntimeGroup import software.aws.toolkits.jetbrains.services.schemas.SchemaTemplateParameters import software.aws.toolkits.jetbrains.services.schemas.resources.SchemasResources import software.aws.toolkits.jetbrains.services.schemas.resources.SchemasResources.LIST_REGISTRIES_AND_SCHEMAS @@ -63,7 +62,6 @@ class SchemaSelectionPanelTest { File(javaClass.getResource("/customerUploadedEventSchemaMultipleTypes.json.txt").toURI()).readText(Charsets.UTF_8) private val mockSamProjectBuilder = mock() - private val RUNTIME_GROUP = RuntimeGroup.JAVA private val mockResourceSelector = mock>() private val mockPanel = mock() @@ -80,7 +78,6 @@ class SchemaSelectionPanelTest { schemaSelectionPanel = SchemaResourceSelectorSelectionPanel( mockSamProjectBuilder, - RUNTIME_GROUP, projectRule.project, resourceSelectorBuilder = mockResourceSelectorBuilder, useSpeedSearch = false, diff --git a/jetbrains-rider/resources/META-INF/ext-rider.xml b/jetbrains-rider/resources/META-INF/ext-rider.xml index 6ce7f40f20..a33ec538f4 100644 --- a/jetbrains-rider/resources/META-INF/ext-rider.xml +++ b/jetbrains-rider/resources/META-INF/ext-rider.xml @@ -24,12 +24,12 @@ - - - - - - + + + + + + diff --git a/jetbrains-rider/src/software/aws/toolkits/jetbrains/services/lambda/dotnet/DotNetRuntimeGroup.kt b/jetbrains-rider/src/software/aws/toolkits/jetbrains/services/lambda/dotnet/DotNetRuntimeGroup.kt index e1781aa979..0a68fc3359 100644 --- a/jetbrains-rider/src/software/aws/toolkits/jetbrains/services/lambda/dotnet/DotNetRuntimeGroup.kt +++ b/jetbrains-rider/src/software/aws/toolkits/jetbrains/services/lambda/dotnet/DotNetRuntimeGroup.kt @@ -8,24 +8,24 @@ import com.intellij.openapi.projectRoots.Sdk import com.jetbrains.rider.ideaInterop.fileTypes.csharp.CSharpLanguage import com.jetbrains.rider.ideaInterop.fileTypes.vb.VbLanguage import software.amazon.awssdk.services.lambda.model.Runtime -import software.aws.toolkits.jetbrains.services.lambda.SdkBasedRuntimeGroupInformation +import software.aws.toolkits.jetbrains.services.lambda.BuiltInRuntimeGroups +import software.aws.toolkits.jetbrains.services.lambda.SdkBasedRuntimeGroup import software.aws.toolkits.jetbrains.utils.DotNetRuntimeUtils -class DotNetRuntimeGroup : SdkBasedRuntimeGroupInformation() { +class DotNetRuntimeGroup : SdkBasedRuntimeGroup() { + override val id: String = BuiltInRuntimeGroups.Dotnet - override val runtimes: Set - get() = setOf( - Runtime.DOTNETCORE2_1, - Runtime.DOTNETCORE3_1 - ) + override val runtimes: Set = setOf( + Runtime.DOTNETCORE2_1, + Runtime.DOTNETCORE3_1 + ) - override val languageIds: Set - get() = setOf(CSharpLanguage.id, VbLanguage.id) + override val languageIds: Set = setOf( + CSharpLanguage.id, + VbLanguage.id + ) override fun runtimeForSdk(sdk: Sdk): Runtime? = null - override fun supportsSamBuild(): Boolean = true - - override fun determineRuntime(project: Project): Runtime? = - DotNetRuntimeUtils.getCurrentDotNetCoreRuntime() + override fun determineRuntime(project: Project): Runtime? = DotNetRuntimeUtils.getCurrentDotNetCoreRuntime() } diff --git a/jetbrains-rider/src/software/aws/toolkits/jetbrains/ui/wizard/DotNetSamProjectGenerator.kt b/jetbrains-rider/src/software/aws/toolkits/jetbrains/ui/wizard/DotNetSamProjectGenerator.kt index 75f54d0747..9f0e5dd98b 100644 --- a/jetbrains-rider/src/software/aws/toolkits/jetbrains/ui/wizard/DotNetSamProjectGenerator.kt +++ b/jetbrains-rider/src/software/aws/toolkits/jetbrains/ui/wizard/DotNetSamProjectGenerator.kt @@ -26,6 +26,7 @@ import com.jetbrains.rider.ui.themes.RiderTheme import software.aws.toolkits.jetbrains.core.executables.ExecutableInstance import software.aws.toolkits.jetbrains.core.executables.ExecutableManager import software.aws.toolkits.jetbrains.core.executables.getExecutableIfPresent +import software.aws.toolkits.jetbrains.services.lambda.BuiltInRuntimeGroups import software.aws.toolkits.jetbrains.services.lambda.RuntimeGroup import software.aws.toolkits.jetbrains.services.lambda.sam.SamExecutable import software.aws.toolkits.jetbrains.utils.DotNetRuntimeUtils @@ -56,7 +57,7 @@ class DotNetSamProjectGenerator( private val generator = SamProjectGenerator() private val samPanel = SamInitSelectionPanel(generator) { // Only show templates for DotNet in Rider - RuntimeGroup.DOTNET.runtimes.contains(it) + RuntimeGroup.getById(BuiltInRuntimeGroups.Dotnet).runtimes.contains(it) } private val projectStructurePanel: JTabbedPane diff --git a/jetbrains-ultimate/resources/META-INF/ext-nodejs.xml b/jetbrains-ultimate/resources/META-INF/ext-nodejs.xml index 557fdae7cf..d4560180b3 100644 --- a/jetbrains-ultimate/resources/META-INF/ext-nodejs.xml +++ b/jetbrains-ultimate/resources/META-INF/ext-nodejs.xml @@ -7,12 +7,13 @@ - - - - - + + + + + + diff --git a/jetbrains-ultimate/src/software/aws/toolkits/jetbrains/services/lambda/nodejs/NodeJsRuntimeGroup.kt b/jetbrains-ultimate/src/software/aws/toolkits/jetbrains/services/lambda/nodejs/NodeJsRuntimeGroup.kt index 0fe424d2b4..12e8eff954 100644 --- a/jetbrains-ultimate/src/software/aws/toolkits/jetbrains/services/lambda/nodejs/NodeJsRuntimeGroup.kt +++ b/jetbrains-ultimate/src/software/aws/toolkits/jetbrains/services/lambda/nodejs/NodeJsRuntimeGroup.kt @@ -12,9 +12,12 @@ import com.intellij.openapi.module.WebModuleTypeBase import com.intellij.openapi.project.Project import com.intellij.openapi.projectRoots.Sdk import software.amazon.awssdk.services.lambda.model.Runtime -import software.aws.toolkits.jetbrains.services.lambda.SdkBasedRuntimeGroupInformation +import software.aws.toolkits.jetbrains.services.lambda.BuiltInRuntimeGroups +import software.aws.toolkits.jetbrains.services.lambda.SdkBasedRuntimeGroup + +class NodeJsRuntimeGroup : SdkBasedRuntimeGroup() { + override val id: String = BuiltInRuntimeGroups.NodeJs -class NodeJsRuntimeGroup : SdkBasedRuntimeGroupInformation() { override val runtimes: Set = setOf( Runtime.NODEJS10_X, Runtime.NODEJS12_X diff --git a/jetbrains-ultimate/tst/software/aws/toolkits/jetbrains/services/lambda/nodejs/NodeJsLambdaHandlerResolverTest.kt b/jetbrains-ultimate/tst/software/aws/toolkits/jetbrains/services/lambda/nodejs/NodeJsLambdaHandlerResolverTest.kt index 5a80002af2..59a668873f 100644 --- a/jetbrains-ultimate/tst/software/aws/toolkits/jetbrains/services/lambda/nodejs/NodeJsLambdaHandlerResolverTest.kt +++ b/jetbrains-ultimate/tst/software/aws/toolkits/jetbrains/services/lambda/nodejs/NodeJsLambdaHandlerResolverTest.kt @@ -10,6 +10,7 @@ import com.intellij.testFramework.runInEdtAndWait import org.assertj.core.api.Assertions.assertThat import org.junit.Rule import org.junit.Test +import software.aws.toolkits.jetbrains.services.lambda.BuiltInRuntimeGroups import software.aws.toolkits.jetbrains.services.lambda.LambdaHandlerResolver import software.aws.toolkits.jetbrains.services.lambda.RuntimeGroup import software.aws.toolkits.jetbrains.utils.rules.NodeJsCodeInsightTestFixtureRule @@ -275,7 +276,7 @@ class NodeJsLambdaHandlerResolverTest { } private fun assertDetermineHandler(handlerElement: PsiElement, expectedHandlerFullName: String?) { - val resolver = LambdaHandlerResolver.getInstanceOrThrow(RuntimeGroup.NODEJS) + val resolver = LambdaHandlerResolver.getInstance(RuntimeGroup.getById(BuiltInRuntimeGroups.NodeJs)) runInEdtAndWait { if (expectedHandlerFullName != null) { @@ -287,7 +288,7 @@ class NodeJsLambdaHandlerResolverTest { } private fun assertFindPsiElements(handler: String, shouldBeFound: Boolean) { - val resolver = LambdaHandlerResolver.getInstanceOrThrow(RuntimeGroup.NODEJS) + val resolver = LambdaHandlerResolver.getInstance(RuntimeGroup.getById(BuiltInRuntimeGroups.NodeJs)) runInEdtAndWait { val project = projectRule.fixture.project val lambdas = resolver.findPsiElements(project, handler, GlobalSearchScope.allScope(project)) From da1d4e7566c2b49496acc7903000029fe4128f26 Mon Sep 17 00:00:00 2001 From: Austin Brooks Date: Thu, 13 Aug 2020 15:47:46 -0700 Subject: [PATCH 0013/1546] Add support for java8.al2 (#1985) --- ...-e27cf426-5be2-4251-8d3a-dcf934d8511b.json | 4 ++ gradle.properties | 4 +- jetbrains-core/build.gradle.kts | 4 -- ...alLambdaRunConfigurationIntegrationTest.kt | 3 +- .../jetbrains/services/lambda/RuntimeGroup.kt | 38 ++++++++++++++----- .../local/LocalLambdaRunConfiguration.kt | 31 ++++++++++----- .../services/lambda/java/JavaRuntimeGroup.kt | 11 +++++- .../lambda/java/JavaSamProjectWizard.kt | 2 +- .../lambda/python/PythonRuntimeGroup.kt | 14 ++++--- .../services/lambda/sam/SamExecutable.kt | 16 ++++---- .../ui/wizard/SamInitSelectionPanel.java | 15 ++++++++ ...LocalLambdaRunConfigurationProducerTest.kt | 2 +- .../lambda/java/JavaRuntimeGroupTest.kt | 5 +-- .../services/lambda/sam/SamCommonTestUtils.kt | 4 +- .../lambda/sam/SamVersionCacheTest.kt | 2 +- .../lambda/dotnet/DotNetRuntimeGroup.kt | 11 +++--- .../lambda/nodejs/NodeJsRuntimeGroup.kt | 12 +++--- .../resources/localized_messages.properties | 2 +- 18 files changed, 119 insertions(+), 61 deletions(-) create mode 100644 .changes/next-release/feature-e27cf426-5be2-4251-8d3a-dcf934d8511b.json diff --git a/.changes/next-release/feature-e27cf426-5be2-4251-8d3a-dcf934d8511b.json b/.changes/next-release/feature-e27cf426-5be2-4251-8d3a-dcf934d8511b.json new file mode 100644 index 0000000000..42f32218ad --- /dev/null +++ b/.changes/next-release/feature-e27cf426-5be2-4251-8d3a-dcf934d8511b.json @@ -0,0 +1,4 @@ +{ + "type" : "feature", + "description" : "Add support for Lambda runtime java8.al2" +} \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index 7017be1642..3caa5dc8fe 100644 --- a/gradle.properties +++ b/gradle.properties @@ -11,12 +11,12 @@ publishChannel= # Common dependencies ideProfileName=2019.3 kotlinVersion=1.3.70 -awsSdkVersion=2.13.58 +awsSdkVersion=2.13.74 coroutinesVersion=1.3.3 ideaPluginVersion=0.4.20 ktlintVersion=0.36.0 jacksonVersion=2.9.8 -telemetryVersion=0.0.34 +telemetryVersion=0.0.39 assertjVersion=3.15.0 junitVersion=4.12 diff --git a/jetbrains-core/build.gradle.kts b/jetbrains-core/build.gradle.kts index aa9998b3a9..f7ee063ed4 100644 --- a/jetbrains-core/build.gradle.kts +++ b/jetbrains-core/build.gradle.kts @@ -2,14 +2,10 @@ // SPDX-License-Identifier: Apache-2.0 import groovy.lang.Closure -import org.gradle.jvm.tasks.Jar -import org.jetbrains.intellij.IntelliJPluginExtension import org.jetbrains.intellij.tasks.PatchPluginXmlTask import org.jetbrains.kotlin.gradle.tasks.KotlinCompile import software.aws.toolkits.telemetry.generator.gradle.GenerateTelemetry import toolkits.gradle.changelog.tasks.GeneratePluginChangeLog -// Cannot be removed or else it will fail to compile -import org.jetbrains.intellij.IntelliJPlugin plugins { id("org.jetbrains.intellij") diff --git a/jetbrains-core/it/software/aws/toolkits/jetbrains/services/lambda/java/JavaLocalLambdaRunConfigurationIntegrationTest.kt b/jetbrains-core/it/software/aws/toolkits/jetbrains/services/lambda/java/JavaLocalLambdaRunConfigurationIntegrationTest.kt index f426e51644..5dceeb9cff 100644 --- a/jetbrains-core/it/software/aws/toolkits/jetbrains/services/lambda/java/JavaLocalLambdaRunConfigurationIntegrationTest.kt +++ b/jetbrains-core/it/software/aws/toolkits/jetbrains/services/lambda/java/JavaLocalLambdaRunConfigurationIntegrationTest.kt @@ -35,6 +35,7 @@ class JavaLocalLambdaRunConfigurationIntegrationTest(private val runtime: Runtim @Parameterized.Parameters(name = "{0}") fun data(): Collection> = listOf( arrayOf(Runtime.JAVA8), + arrayOf(Runtime.JAVA8_AL2), arrayOf(Runtime.JAVA11) ) } @@ -66,7 +67,7 @@ class JavaLocalLambdaRunConfigurationIntegrationTest(private val runtime: Runtim ) val compatibility = when (runtime) { - Runtime.JAVA8 -> "1.8" + Runtime.JAVA8, Runtime.JAVA8_AL2 -> "1.8" Runtime.JAVA11 -> "11" else -> throw NotImplementedError() } diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/RuntimeGroup.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/RuntimeGroup.kt index f90c1f78f0..e425cd735b 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/RuntimeGroup.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/RuntimeGroup.kt @@ -16,8 +16,11 @@ import com.intellij.openapi.projectRoots.SdkType import com.intellij.openapi.roots.ModuleRootManager import com.intellij.openapi.roots.ProjectRootManager import com.intellij.openapi.util.KeyedExtensionCollector +import com.intellij.util.text.SemVer import software.amazon.awssdk.services.lambda.model.Runtime import software.aws.toolkits.jetbrains.core.IdBasedExtensionPoint +import software.aws.toolkits.jetbrains.services.lambda.sam.SamExecutable +import software.aws.toolkits.resources.message /** * IDs for built-in runtime groups that ship with toolkit @@ -29,21 +32,36 @@ object BuiltInRuntimeGroups { const val NodeJs = "NODEJS" } +data class RuntimeInfo(val runtime: Runtime, val minimumVersion: SemVer = SamExecutable.minVersion) + /** * Grouping of Lambda [Runtime] by parent language. * * A Lambda [Runtime] belongs to a single [RuntimeGroup], a [RuntimeGroup] may have several Lambda [Runtime]s, [Language]s or [Sdk]s. */ -interface RuntimeGroup { - val id: String - val runtimes: Set - val languageIds: Set +abstract class RuntimeGroup { + abstract val id: String + abstract val languageIds: Set + + val runtimes: List by lazy { + supportedRuntimes.map { it.runtime } + } + + protected abstract val supportedRuntimes: List - fun determineRuntime(project: Project): Runtime? = null - fun determineRuntime(module: Module): Runtime? = null - fun getModuleType(): ModuleType<*>? = null - fun getIdeSdkType(): SdkType? = null - fun supportsSamBuild(): Boolean = true + open fun determineRuntime(project: Project): Runtime? = null + open fun determineRuntime(module: Module): Runtime? = null + open fun getModuleType(): ModuleType<*>? = null + open fun getIdeSdkType(): SdkType? = null + + open fun supportsSamBuild(): Boolean = true + + fun validateSamVersion(runtime: Runtime, samVersion: SemVer) { + val minVersion = supportedRuntimes.first { it.runtime == runtime }.minimumVersion + if (samVersion < minVersion) { + throw RuntimeException(message("sam.executable.minimum_too_low_runtime", runtime, minVersion)) + } + } companion object { private val EP_NAM = ExtensionPointName.create("aws.toolkit.lambda.runtimeGroup") @@ -69,7 +87,7 @@ interface RuntimeGroup { } } -abstract class SdkBasedRuntimeGroup : RuntimeGroup { +abstract class SdkBasedRuntimeGroup : RuntimeGroup() { protected abstract fun runtimeForSdk(sdk: Sdk): Runtime? override fun determineRuntime(project: Project): Runtime? = ProjectRootManager.getInstance(project).projectSdk?.let { runtimeForSdk(it) } diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/execution/local/LocalLambdaRunConfiguration.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/execution/local/LocalLambdaRunConfiguration.kt index 1bf8c3b862..fc7f814e15 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/execution/local/LocalLambdaRunConfiguration.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/execution/local/LocalLambdaRunConfiguration.kt @@ -21,13 +21,13 @@ import com.intellij.psi.PsiElement import com.intellij.psi.util.PsiTreeUtil import com.intellij.refactoring.listeners.RefactoringElementAdapter import com.intellij.refactoring.listeners.RefactoringElementListener +import com.intellij.util.text.SemVer import com.intellij.util.text.nullize import org.jetbrains.concurrency.isPending import software.amazon.awssdk.services.lambda.model.Runtime import software.aws.toolkits.core.credentials.ToolkitCredentialsProvider import software.aws.toolkits.core.region.AwsRegion import software.aws.toolkits.core.utils.getLogger -import software.aws.toolkits.core.utils.info import software.aws.toolkits.core.utils.tryOrNull import software.aws.toolkits.jetbrains.core.executables.ExecutableInstance import software.aws.toolkits.jetbrains.core.executables.ExecutableManager @@ -77,15 +77,16 @@ class LocalLambdaRunConfiguration(project: Project, factory: ConfigurationFactor } override fun checkConfiguration() { - checkSamVersion() + val (handler, runtime) = resolveLambdaInfo(project = project, functionOptions = serializableOptions.functionOptions) + checkSamVersion(runtime) resolveRegion() resolveCredentials() - checkLambdaHandler() + checkLambdaHandler(handler, runtime) checkInput() } - private fun checkSamVersion() { - ExecutableManager.getInstance().getExecutableIfPresent().let { + private fun checkSamVersion(runtime: Runtime) { + val executable = ExecutableManager.getInstance().getExecutableIfPresent().let { when (it) { is ExecutableInstance.Executable -> it is ExecutableInstance.InvalidExecutable, is ExecutableInstance.UnresolvedExecutable -> throw RuntimeConfigurationError( @@ -93,24 +94,36 @@ class LocalLambdaRunConfiguration(project: Project, factory: ConfigurationFactor ) } } + + runtime.runtimeGroup?.let { runtimeGroup -> + SemVer.parseFromText(executable.version)?.let { semVer -> + // TODO: Executable manager should better expose the VersionScheme of the Executable... + try { + runtimeGroup.validateSamVersion(runtime, semVer) + } catch (e: Exception) { + throw RuntimeConfigurationError(e.message) + } + } + } } - private fun checkLambdaHandler() { + private fun checkLambdaHandler(handler: String, runtime: Runtime): Runtime { val handlerValidator = project.service() - val (handler, runtime) = resolveLambdaInfo(project = project, functionOptions = serializableOptions.functionOptions) val promise = handlerValidator.evaluate(LambdaHandlerValidator.LambdaEntry(project, runtime, handler)) if (promise.isPending) { promise.then { isValid -> messageBus.syncPublisher(LambdaHandlerEvaluationListener.TOPIC).handlerValidationFinished(handler, isValid) } - logger.info { "Validation will proceed asynchronously for SAM CLI version" } throw RuntimeConfigurationError(message("lambda.run_configuration.handler.validation.in_progress")) } val isHandlerValid = promise.blockingGet(0)!! - if (!isHandlerValid) + if (!isHandlerValid) { throw RuntimeConfigurationError(message("lambda.run_configuration.handler_not_found", handler)) + } + + return runtime } override fun getState(executor: Executor, environment: ExecutionEnvironment): SamRunningState { diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/java/JavaRuntimeGroup.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/java/JavaRuntimeGroup.kt index bc378d2502..36636035c9 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/java/JavaRuntimeGroup.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/java/JavaRuntimeGroup.kt @@ -11,15 +11,22 @@ import com.intellij.openapi.projectRoots.JavaSdkType import com.intellij.openapi.projectRoots.JavaSdkVersion import com.intellij.openapi.projectRoots.Sdk import com.intellij.openapi.projectRoots.SdkType +import com.intellij.util.text.SemVer import software.amazon.awssdk.services.lambda.model.Runtime import software.aws.toolkits.jetbrains.services.lambda.BuiltInRuntimeGroups +import software.aws.toolkits.jetbrains.services.lambda.RuntimeInfo import software.aws.toolkits.jetbrains.services.lambda.SdkBasedRuntimeGroup class JavaRuntimeGroup : SdkBasedRuntimeGroup() { override val id: String = BuiltInRuntimeGroups.Java - override val runtimes = setOf(Runtime.JAVA8, Runtime.JAVA11) override val languageIds = setOf(JavaLanguage.INSTANCE.id) + override val supportedRuntimes: List = listOf( + RuntimeInfo(Runtime.JAVA8), + RuntimeInfo(Runtime.JAVA8_AL2, SemVer("1.1.0", 1, 1, 0)), + RuntimeInfo(Runtime.JAVA11) + ) + override fun runtimeForSdk(sdk: Sdk): Runtime? { if (sdk.sdkType is JavaSdkType) { val javaSdkVersion = JavaSdk.getInstance().getVersion(sdk) ?: return null @@ -29,7 +36,7 @@ class JavaRuntimeGroup : SdkBasedRuntimeGroup() { } private fun determineRuntimeForSdk(sdk: JavaSdkVersion) = when { - sdk <= JavaSdkVersion.JDK_1_8 -> Runtime.JAVA8 + sdk <= JavaSdkVersion.JDK_1_8 -> Runtime.JAVA8_AL2 sdk <= JavaSdkVersion.JDK_11 -> Runtime.JAVA11 else -> null } diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/java/JavaSamProjectWizard.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/java/JavaSamProjectWizard.kt index 29467710f2..4f51c8ab33 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/java/JavaSamProjectWizard.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/java/JavaSamProjectWizard.kt @@ -54,7 +54,7 @@ class JavaSamProjectWizard : SamProjectWizard { } abstract class JavaSamProjectTemplate : SamProjectTemplate() { - override fun supportedRuntimes() = setOf(Runtime.JAVA8, Runtime.JAVA11) + override fun supportedRuntimes() = setOf(Runtime.JAVA8, Runtime.JAVA8_AL2, Runtime.JAVA11) // Helper method to locate the build file, such as pom.xml in the project content root. protected fun locateBuildFile(contentRoot: VirtualFile, buildFileName: String): VirtualFile? { diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/python/PythonRuntimeGroup.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/python/PythonRuntimeGroup.kt index 35965cc349..f20ca5f56d 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/python/PythonRuntimeGroup.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/python/PythonRuntimeGroup.kt @@ -12,18 +12,20 @@ import com.jetbrains.python.psi.LanguageLevel import com.jetbrains.python.sdk.PythonSdkType import software.amazon.awssdk.services.lambda.model.Runtime import software.aws.toolkits.jetbrains.services.lambda.BuiltInRuntimeGroups +import software.aws.toolkits.jetbrains.services.lambda.RuntimeInfo import software.aws.toolkits.jetbrains.services.lambda.SdkBasedRuntimeGroup class PythonRuntimeGroup : SdkBasedRuntimeGroup() { override val id: String = BuiltInRuntimeGroups.Python - override val runtimes: Set = setOf( - Runtime.PYTHON2_7, - Runtime.PYTHON3_6, - Runtime.PYTHON3_7, - Runtime.PYTHON3_8 - ) override val languageIds: Set = setOf(PythonLanguage.INSTANCE.id) + override val supportedRuntimes = listOf( + RuntimeInfo(Runtime.PYTHON2_7), + RuntimeInfo(Runtime.PYTHON3_6), + RuntimeInfo(Runtime.PYTHON3_7), + RuntimeInfo(Runtime.PYTHON3_8) + ) + override fun runtimeForSdk(sdk: Sdk): Runtime? = when { sdk.sdkType is PythonSdkType && PythonSdkType.getLanguageLevelForSdk(sdk).isAtLeast(LanguageLevel.PYTHON38) -> Runtime.PYTHON3_8 sdk.sdkType is PythonSdkType && PythonSdkType.getLanguageLevelForSdk(sdk).isAtLeast(LanguageLevel.PYTHON37) -> Runtime.PYTHON3_7 diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/sam/SamExecutable.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/sam/SamExecutable.kt index 56f315ef01..04a5f91273 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/sam/SamExecutable.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/sam/SamExecutable.kt @@ -14,14 +14,16 @@ import java.nio.file.Path import java.nio.file.Paths class SamExecutable : ExecutableType, AutoResolvable, Validatable { + companion object { + // inclusive + val minVersion = SemVer("0.47.0", 0, 47, 0) + // exclusive + val maxVersion = SemVer("2.0.0", 2, 0, 0) + } + override val displayName: String = "sam" override val id: String = "samCli" - // inclusive - val samMinVersion = SemVer("0.47.0", 0, 47, 0) - // exclusive - val samMaxVersion = SemVer("2.0.0", 2, 0, 0) - override fun version(path: Path): SemVer = ExecutableCommon.getVersion( path.toString(), SamVersionCache, @@ -32,8 +34,8 @@ class SamExecutable : ExecutableType, AutoResolvable, Validatable { val version = this.version(path) ExecutableCommon.checkSemVerVersion( version, - samMinVersion, - samMaxVersion, + minVersion, + maxVersion, this.displayName ) } diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/ui/wizard/SamInitSelectionPanel.java b/jetbrains-core/src/software/aws/toolkits/jetbrains/ui/wizard/SamInitSelectionPanel.java index 5358b4b4b6..3484c55f5a 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/ui/wizard/SamInitSelectionPanel.java +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/ui/wizard/SamInitSelectionPanel.java @@ -11,6 +11,7 @@ import java.awt.Dimension; import java.awt.event.ItemEvent; import java.util.List; +import java.util.Objects; import java.util.Set; import java.util.function.Predicate; import javax.swing.JButton; @@ -19,6 +20,7 @@ import javax.swing.JList; import javax.swing.JPanel; import javax.swing.JTextField; +import com.intellij.util.text.SemVer; import kotlin.Pair; import kotlin.Unit; import org.jetbrains.annotations.NotNull; @@ -34,6 +36,7 @@ import software.aws.toolkits.jetbrains.core.executables.ExecutableType; import software.aws.toolkits.jetbrains.services.lambda.LambdaBuilder; import software.aws.toolkits.jetbrains.services.lambda.RuntimeGroup; +import software.aws.toolkits.jetbrains.services.lambda.RuntimeGroupUtil; import software.aws.toolkits.jetbrains.services.lambda.SamNewProjectSettings; import software.aws.toolkits.jetbrains.services.lambda.SamProjectTemplate; import software.aws.toolkits.jetbrains.services.lambda.sam.SamExecutable; @@ -336,6 +339,18 @@ public ValidationInfo validate() { ExecutableInstance samExecutable = ExecutableManager.getInstance().getExecutableIfPresent(ExecutableType.getExecutable(SamExecutable.class)); if (samExecutable instanceof ExecutableInstance.BadExecutable) { return new ValidationInfo(((ExecutableInstance.BadExecutable) samExecutable).getValidationError(), samExecutableField); + } else { + Runtime selectedRuntime = (Runtime) runtimeComboBox.getSelectedItem(); + + if(selectedRuntime != null) { + SemVer samVersion = Objects.requireNonNull(SemVer.parseFromText(samExecutable.getVersion())); + RuntimeGroup runtimeGroup = Objects.requireNonNull(RuntimeGroupUtil.getRuntimeGroup(selectedRuntime)); + try { + runtimeGroup.validateSamVersion(selectedRuntime, samVersion); + } catch (Exception e) { + return new ValidationInfo(e.getMessage(), runtimeComboBox); + } + } } if (sdkSelectionUi == null) { diff --git a/jetbrains-core/tst/software/aws/toolkits/jetbrains/services/lambda/execution/local/LocalLambdaRunConfigurationProducerTest.kt b/jetbrains-core/tst/software/aws/toolkits/jetbrains/services/lambda/execution/local/LocalLambdaRunConfigurationProducerTest.kt index b634d71f18..74337f4943 100644 --- a/jetbrains-core/tst/software/aws/toolkits/jetbrains/services/lambda/execution/local/LocalLambdaRunConfigurationProducerTest.kt +++ b/jetbrains-core/tst/software/aws/toolkits/jetbrains/services/lambda/execution/local/LocalLambdaRunConfigurationProducerTest.kt @@ -48,7 +48,7 @@ class LocalLambdaRunConfigurationProducerTest { assertThat(runConfiguration).isNotNull val configuration = runConfiguration?.configuration as LocalLambdaRunConfiguration assertThat(configuration.isUsingTemplate()).isFalse() - assertThat(configuration.runtime()).isEqualTo(Runtime.JAVA8) + assertThat(configuration.runtime()).isEqualTo(Runtime.JAVA8_AL2) assertThat(configuration.handler()).isEqualTo("com.example.LambdaHandler::handleRequest") assertThat(configuration.name).isEqualTo("[Local] LambdaHandler.handleRequest") } diff --git a/jetbrains-core/tst/software/aws/toolkits/jetbrains/services/lambda/java/JavaRuntimeGroupTest.kt b/jetbrains-core/tst/software/aws/toolkits/jetbrains/services/lambda/java/JavaRuntimeGroupTest.kt index ec4ccbb6d9..c7acda4e1f 100644 --- a/jetbrains-core/tst/software/aws/toolkits/jetbrains/services/lambda/java/JavaRuntimeGroupTest.kt +++ b/jetbrains-core/tst/software/aws/toolkits/jetbrains/services/lambda/java/JavaRuntimeGroupTest.kt @@ -39,12 +39,11 @@ class JavaRuntimeGroupTest( } companion object { - @Parameters(name = "{0}") @JvmStatic fun parameters(): Collection> = listOf( - arrayOf("Java 7", { IdeaTestUtil.getMockJdk17() }, Runtime.JAVA8), - arrayOf("Java 8", { IdeaTestUtil.getMockJdk18() }, Runtime.JAVA8), + arrayOf("Java 7", { IdeaTestUtil.getMockJdk17() }, Runtime.JAVA8_AL2), + arrayOf("Java 8", { IdeaTestUtil.getMockJdk18() }, Runtime.JAVA8_AL2), arrayOf("Java 9", { IdeaTestUtil.getMockJdk9() }, Runtime.JAVA11), arrayOf("Java 10", { IdeaTestUtil.getMockJdk(LanguageLevel.JDK_10.toJavaVersion()) }, Runtime.JAVA11), arrayOf("Java 11", { IdeaTestUtil.getMockJdk(LanguageLevel.JDK_11.toJavaVersion()) }, Runtime.JAVA11), diff --git a/jetbrains-core/tst/software/aws/toolkits/jetbrains/services/lambda/sam/SamCommonTestUtils.kt b/jetbrains-core/tst/software/aws/toolkits/jetbrains/services/lambda/sam/SamCommonTestUtils.kt index 8ee2ca3a41..7bbdc1eb39 100644 --- a/jetbrains-core/tst/software/aws/toolkits/jetbrains/services/lambda/sam/SamCommonTestUtils.kt +++ b/jetbrains-core/tst/software/aws/toolkits/jetbrains/services/lambda/sam/SamCommonTestUtils.kt @@ -16,9 +16,9 @@ object SamCommonTestUtils { return SamCommon.mapper.writeValueAsString(tree) } - fun getMinVersionAsJson() = getVersionAsJson(SamExecutable().samMinVersion.toString()) + fun getMinVersionAsJson() = getVersionAsJson(SamExecutable.minVersion.toString()) - fun getMaxVersionAsJson() = getVersionAsJson(SamExecutable().samMaxVersion.toString()) + fun getMaxVersionAsJson() = getVersionAsJson(SamExecutable.maxVersion.toString()) fun makeATestSam(message: String, path: String? = null, exitCode: Int = 0): Path { val sam = path?.let { diff --git a/jetbrains-core/tst/software/aws/toolkits/jetbrains/services/lambda/sam/SamVersionCacheTest.kt b/jetbrains-core/tst/software/aws/toolkits/jetbrains/services/lambda/sam/SamVersionCacheTest.kt index aba57a98b1..45a4d16bfd 100644 --- a/jetbrains-core/tst/software/aws/toolkits/jetbrains/services/lambda/sam/SamVersionCacheTest.kt +++ b/jetbrains-core/tst/software/aws/toolkits/jetbrains/services/lambda/sam/SamVersionCacheTest.kt @@ -42,7 +42,7 @@ class SamVersionCacheTest { fun samCliMinVersion() { val samPath = SamCommonTestUtils.makeATestSam(SamCommonTestUtils.getMinVersionAsJson()).toString() val samVersion = SamVersionCache.evaluateBlocking(samPath).result - assertEquals("Mismatch SAM executable version", samVersion, SamExecutable().samMinVersion) + assertEquals("Mismatch SAM executable version", samVersion, SamExecutable.minVersion) } @Test diff --git a/jetbrains-rider/src/software/aws/toolkits/jetbrains/services/lambda/dotnet/DotNetRuntimeGroup.kt b/jetbrains-rider/src/software/aws/toolkits/jetbrains/services/lambda/dotnet/DotNetRuntimeGroup.kt index 0a68fc3359..c84482d7a3 100644 --- a/jetbrains-rider/src/software/aws/toolkits/jetbrains/services/lambda/dotnet/DotNetRuntimeGroup.kt +++ b/jetbrains-rider/src/software/aws/toolkits/jetbrains/services/lambda/dotnet/DotNetRuntimeGroup.kt @@ -9,22 +9,23 @@ import com.jetbrains.rider.ideaInterop.fileTypes.csharp.CSharpLanguage import com.jetbrains.rider.ideaInterop.fileTypes.vb.VbLanguage import software.amazon.awssdk.services.lambda.model.Runtime import software.aws.toolkits.jetbrains.services.lambda.BuiltInRuntimeGroups +import software.aws.toolkits.jetbrains.services.lambda.RuntimeInfo import software.aws.toolkits.jetbrains.services.lambda.SdkBasedRuntimeGroup import software.aws.toolkits.jetbrains.utils.DotNetRuntimeUtils class DotNetRuntimeGroup : SdkBasedRuntimeGroup() { override val id: String = BuiltInRuntimeGroups.Dotnet - override val runtimes: Set = setOf( - Runtime.DOTNETCORE2_1, - Runtime.DOTNETCORE3_1 - ) - override val languageIds: Set = setOf( CSharpLanguage.id, VbLanguage.id ) + override val supportedRuntimes: List = listOf( + RuntimeInfo(Runtime.DOTNETCORE2_1), + RuntimeInfo(Runtime.DOTNETCORE3_1) + ) + override fun runtimeForSdk(sdk: Sdk): Runtime? = null override fun determineRuntime(project: Project): Runtime? = DotNetRuntimeUtils.getCurrentDotNetCoreRuntime() diff --git a/jetbrains-ultimate/src/software/aws/toolkits/jetbrains/services/lambda/nodejs/NodeJsRuntimeGroup.kt b/jetbrains-ultimate/src/software/aws/toolkits/jetbrains/services/lambda/nodejs/NodeJsRuntimeGroup.kt index 12e8eff954..9314622dd8 100644 --- a/jetbrains-ultimate/src/software/aws/toolkits/jetbrains/services/lambda/nodejs/NodeJsRuntimeGroup.kt +++ b/jetbrains-ultimate/src/software/aws/toolkits/jetbrains/services/lambda/nodejs/NodeJsRuntimeGroup.kt @@ -13,21 +13,21 @@ import com.intellij.openapi.project.Project import com.intellij.openapi.projectRoots.Sdk import software.amazon.awssdk.services.lambda.model.Runtime import software.aws.toolkits.jetbrains.services.lambda.BuiltInRuntimeGroups +import software.aws.toolkits.jetbrains.services.lambda.RuntimeInfo import software.aws.toolkits.jetbrains.services.lambda.SdkBasedRuntimeGroup class NodeJsRuntimeGroup : SdkBasedRuntimeGroup() { override val id: String = BuiltInRuntimeGroups.NodeJs - - override val runtimes: Set = setOf( - Runtime.NODEJS10_X, - Runtime.NODEJS12_X - ) - override val languageIds: Set = setOf( JavascriptLanguage.INSTANCE.id, JavaScriptSupportLoader.ECMA_SCRIPT_6.id ) + override val supportedRuntimes = listOf( + RuntimeInfo(Runtime.NODEJS10_X), + RuntimeInfo(Runtime.NODEJS12_X) + ) + override fun determineRuntime(module: Module): Runtime? = determineRuntime(module.project) override fun determineRuntime(project: Project): Runtime? = diff --git a/resources/resources/software/aws/toolkits/resources/localized_messages.properties b/resources/resources/software/aws/toolkits/resources/localized_messages.properties index a01d6d5ecd..8d8cbbe366 100644 --- a/resources/resources/software/aws/toolkits/resources/localized_messages.properties +++ b/resources/resources/software/aws/toolkits/resources/localized_messages.properties @@ -554,7 +554,7 @@ sam.build.failed=SAM build command failed sam.build.running=Running SAM build sam.build.succeeded=SAM build command succeeded sam.build.title=SAM build -sam.executable.schema_support_version_wrong=Installed SAM executable does not support templates that require Event Schema selection. Required minimum version {0}, but found {1}. +sam.executable.minimum_too_low_runtime=The runtime {0} requires a minimum SAM CLI version of {1} sam.init.description=AWS Serverless Application Model (AWS SAM) prescribes rules for expressing Serverless applications on AWS. sam.init.error.no.project.basepath=Unable to determine project basepath sam.init.error.no.solution.basepath=Unable to determine solution basepath From 7ea07db877d75e22cbc211432b585ac860571b52 Mon Sep 17 00:00:00 2001 From: Austin Brooks Date: Fri, 14 Aug 2020 10:46:51 -0700 Subject: [PATCH 0014/1546] Upgrade 2020.2 EAP to 2020.2 stable (#1988) --- intellijJVersions.gradle | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/intellijJVersions.gradle b/intellijJVersions.gradle index 92e1558420..5a1852e263 100644 --- a/intellijJVersions.gradle +++ b/intellijJVersions.gradle @@ -78,22 +78,22 @@ static def ideProfiles() { "untilVersion": "202.*", "products" : [ "IC": [ - sdkVersion: "IC-202.6250.13-EAP-SNAPSHOT", + sdkVersion: "IC-2020.2", plugins : [ "org.jetbrains.plugins.terminal", "org.jetbrains.plugins.yaml", - "PythonCore:202.6250.13", + "PythonCore:202.6397.124", "java", "com.intellij.gradle", "org.jetbrains.idea.maven", - "Docker:202.6250.6" + "Docker:202.6397.93" ] ], "IU": [ - sdkVersion: "IU-202.6250.13-EAP-SNAPSHOT", + sdkVersion: "IU-2020.2", plugins : [ "org.jetbrains.plugins.terminal", - "Pythonid:202.6250.13", + "Pythonid:202.6397.98", "org.jetbrains.plugins.yaml", "JavaScript", "JavaScriptDebugger", @@ -101,9 +101,9 @@ static def ideProfiles() { ] ], "RD": [ - sdkVersion : "RD-2020.2-SNAPSHOT", + sdkVersion : "RD-2020.2", rdGenVersion: "0.202.113", - nugetVersion: "2020.2.0-eap07", + nugetVersion: "2020.2.0", plugins : [ "org.jetbrains.plugins.yaml" ] From 787d06b97708a00ad133977fde2b1994c7cfa40a Mon Sep 17 00:00:00 2001 From: Austin Brooks Date: Fri, 14 Aug 2020 11:22:33 -0700 Subject: [PATCH 0015/1546] Disable debug for java8.al2 (#1987) --- .../java/JavaLocalLambdaRunConfigurationIntegrationTest.kt | 2 ++ .../jetbrains/services/lambda/java/JavaSamDebugSupport.kt | 3 +++ 2 files changed, 5 insertions(+) diff --git a/jetbrains-core/it/software/aws/toolkits/jetbrains/services/lambda/java/JavaLocalLambdaRunConfigurationIntegrationTest.kt b/jetbrains-core/it/software/aws/toolkits/jetbrains/services/lambda/java/JavaLocalLambdaRunConfigurationIntegrationTest.kt index 5dceeb9cff..7f7caaf51c 100644 --- a/jetbrains-core/it/software/aws/toolkits/jetbrains/services/lambda/java/JavaLocalLambdaRunConfigurationIntegrationTest.kt +++ b/jetbrains-core/it/software/aws/toolkits/jetbrains/services/lambda/java/JavaLocalLambdaRunConfigurationIntegrationTest.kt @@ -8,6 +8,7 @@ import com.intellij.execution.executors.DefaultDebugExecutor import com.intellij.testFramework.runInEdtAndWait import org.assertj.core.api.Assertions.assertThat import org.junit.After +import org.junit.Assume.assumeTrue import org.junit.Before import org.junit.Rule import org.junit.Test @@ -169,6 +170,7 @@ class JavaLocalLambdaRunConfigurationIntegrationTest(private val runtime: Runtim @Test fun samIsExecutedWithDebugger() { + assumeTrue(runtime != Runtime.JAVA8_AL2) projectRule.addBreakpoint() val runConfiguration = createHandlerBasedRunConfiguration( diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/java/JavaSamDebugSupport.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/java/JavaSamDebugSupport.kt index 93716fd92f..ba5b14ff63 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/java/JavaSamDebugSupport.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/java/JavaSamDebugSupport.kt @@ -13,6 +13,7 @@ import com.intellij.xdebugger.XDebugProcess import com.intellij.xdebugger.XDebugProcessStarter import com.intellij.xdebugger.XDebugSession import com.intellij.xdebugger.impl.XDebugSessionImpl +import software.amazon.awssdk.services.lambda.model.Runtime import software.aws.toolkits.jetbrains.services.lambda.execution.local.SamDebugSupport import software.aws.toolkits.jetbrains.services.lambda.execution.local.SamRunningState @@ -43,4 +44,6 @@ class JavaSamDebugSupport : SamDebugSupport { } } } + + override fun isSupported(runtime: Runtime): Boolean = runtime != Runtime.JAVA8_AL2 } From cfad3aa2d1887ba691b2ba2b125165e4f49cef1f Mon Sep 17 00:00:00 2001 From: Hunter Werlla Date: Fri, 14 Aug 2020 12:14:04 -0700 Subject: [PATCH 0016/1546] Better other IDE support (#1984) --- ...-538dc871-7dc2-4fbf-a185-1a69cd31dc93.json | 4 ++ .../jetbrains/services/lambda/RuntimeGroup.kt | 2 +- .../lambda/upload/CreateLambdaFunction.kt | 3 +- .../lambda/upload/EditFunctionDialog.kt | 8 ++-- .../lambda/upload/CreateLambdaFunctionTest.kt | 45 +++++++++++++++++-- .../lambda/upload/EditFunctionDialogTest.kt | 30 ++++++++++++- 6 files changed, 82 insertions(+), 10 deletions(-) create mode 100644 .changes/next-release/bugfix-538dc871-7dc2-4fbf-a185-1a69cd31dc93.json diff --git a/.changes/next-release/bugfix-538dc871-7dc2-4fbf-a185-1a69cd31dc93.json b/.changes/next-release/bugfix-538dc871-7dc2-4fbf-a185-1a69cd31dc93.json new file mode 100644 index 0000000000..c91c10dbd2 --- /dev/null +++ b/.changes/next-release/bugfix-538dc871-7dc2-4fbf-a185-1a69cd31dc93.json @@ -0,0 +1,4 @@ +{ + "type" : "bugfix", + "description" : "Fix several cases where features not supported by the host IDE are shown (#1980)" +} diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/RuntimeGroup.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/RuntimeGroup.kt index e425cd735b..1f68260ef8 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/RuntimeGroup.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/RuntimeGroup.kt @@ -116,7 +116,7 @@ fun AnActionEvent.runtime(): Runtime? { * To be implemented on a companion object of the extension point object to expose factory methods. * See [software.aws.toolkits.jetbrains.services.lambda.LambdaBuilder] */ -abstract class RuntimeGroupExtensionPointObject(private val extensionPointName: ExtensionPointName>) { +abstract class RuntimeGroupExtensionPointObject(val extensionPointName: ExtensionPointName>) { private val collector = KeyedExtensionCollector(extensionPointName.name) fun getInstanceOrNull(runtimeGroup: RuntimeGroup): T? = collector.findSingle(runtimeGroup.id) diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/upload/CreateLambdaFunction.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/upload/CreateLambdaFunction.kt index 0ff4739c96..12ebb4b104 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/upload/CreateLambdaFunction.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/upload/CreateLambdaFunction.kt @@ -61,7 +61,8 @@ class CreateLambdaFunction( val element: PsiElement? = elementPointer?.element if (handlerName == null || element == null || lambdaHandlerResolver == null) { - e.presentation.isVisible = true + // It was created from ActionManager, so only show it if we have supported runtime groups + e.presentation.isVisible = LambdaHandlerResolver.supportedRuntimeGroups().isNotEmpty() return } diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/upload/EditFunctionDialog.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/upload/EditFunctionDialog.kt index 07b05e6826..dd636e71e0 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/upload/EditFunctionDialog.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/upload/EditFunctionDialog.kt @@ -23,12 +23,12 @@ import software.aws.toolkits.jetbrains.core.AwsClientManager import software.aws.toolkits.jetbrains.core.awsClient import software.aws.toolkits.jetbrains.core.credentials.AwsConnectionManager import software.aws.toolkits.jetbrains.core.help.HelpIds -import software.aws.toolkits.jetbrains.core.region.AwsRegionProvider import software.aws.toolkits.jetbrains.services.iam.CreateIamRoleDialog import software.aws.toolkits.jetbrains.services.iam.IamRole import software.aws.toolkits.jetbrains.services.lambda.Lambda.findPsiElementsForHandler import software.aws.toolkits.jetbrains.services.lambda.LambdaBuilder import software.aws.toolkits.jetbrains.services.lambda.LambdaFunction +import software.aws.toolkits.jetbrains.services.lambda.LambdaHandlerResolver import software.aws.toolkits.jetbrains.services.lambda.LambdaLimits.DEFAULT_MEMORY_SIZE import software.aws.toolkits.jetbrains.services.lambda.LambdaLimits.DEFAULT_TIMEOUT import software.aws.toolkits.jetbrains.services.lambda.runtimeGroup @@ -114,10 +114,14 @@ class EditFunctionDialog( view.envVars.envVars = envVariables if (mode == UPDATE_CONFIGURATION) { + // Show a unfiltered list of runtimes since we don't have to filter + view.setRuntimes(Runtime.knownValues()) view.name.isEnabled = false view.deploySettings.isVisible = false view.buildSettings.isVisible = false } else { + // show a filtered list of runtimes to only ones we can build (since we have to build) + view.setRuntimes(LambdaHandlerResolver.supportedRuntimeGroups().flatMap { it.runtimes }) view.createBucket.addActionListener { val bucketDialog = CreateS3BucketDialog( project = project, @@ -140,12 +144,10 @@ class EditFunctionDialog( .forEach { it.isVisible = false } } - view.setRuntimes(Runtime.knownValues()) view.runtime.selectedItem = runtime?.validOrNull view.xrayEnabled.isSelected = xrayEnabled - val regionProvider = AwsRegionProvider.getInstance() val settings = AwsConnectionManager.getInstance(project) view.setXrayControlVisibility(mode != UPDATE_CODE && lambdaTracingConfigIsAvailable(settings.activeRegion)) diff --git a/jetbrains-core/tst/software/aws/toolkits/jetbrains/services/lambda/upload/CreateLambdaFunctionTest.kt b/jetbrains-core/tst/software/aws/toolkits/jetbrains/services/lambda/upload/CreateLambdaFunctionTest.kt index f26ab9431d..05f64dc8eb 100644 --- a/jetbrains-core/tst/software/aws/toolkits/jetbrains/services/lambda/upload/CreateLambdaFunctionTest.kt +++ b/jetbrains-core/tst/software/aws/toolkits/jetbrains/services/lambda/upload/CreateLambdaFunctionTest.kt @@ -6,11 +6,14 @@ package software.aws.toolkits.jetbrains.services.lambda.upload import com.intellij.psi.PsiElement import com.intellij.psi.SmartPointerManager import com.intellij.psi.SmartPsiElementPointer +import com.intellij.testFramework.DisposableRule +import com.intellij.testFramework.ExtensionTestUtil import com.intellij.testFramework.TestActionEvent import com.intellij.testFramework.runInEdtAndWait import com.nhaarman.mockitokotlin2.any import com.nhaarman.mockitokotlin2.doAnswer import com.nhaarman.mockitokotlin2.mock +import org.assertj.core.api.Assertions.assertThat import org.junit.Before import org.junit.Rule import org.junit.Test @@ -26,25 +29,32 @@ class CreateLambdaFunctionTest { @JvmField val projectRule = JavaCodeInsightTestFixtureRule() + @Rule + @JvmField + val disposableRule = DisposableRule() + lateinit var smartElement: SmartPsiElementPointer @Before fun setup() { val fixture = projectRule.fixture - val element = fixture.addClass(""" + val element = fixture.addClass( + """ public class Processor { public void handler() { } } - """).findMethodsByName("handler", false).first() + """ + ).findMethodsByName("handler", false).first() runInEdtAndWait { smartElement = SmartPointerManager.createPointer(element) } - fixture.openFile("template.yaml", """ + fixture.openFile( + "template.yaml", """ Resources: ServerlessFunction: Type: AWS::Serverless::Function @@ -53,7 +63,8 @@ Resources: Handler: helloworld.App::handleRequest Runtime: foo Timeout: 800 -""") +""" + ) } @Test @@ -136,4 +147,30 @@ Resources: assertTrue { actionEvent.presentation.isVisible } } } + + @Test + fun `Supported runtime groups shows action`() { + // With no masking it should be visible because we have runtime groups + runInEdtAndWait { + val action = CreateLambdaFunction() + val actionEvent = TestActionEvent() + action.update(actionEvent) + assertThat(actionEvent.presentation.isVisible).isTrue() + } + } + + @Test + fun `No supported runtime groups hides action`() { + ExtensionTestUtil.maskExtensions( + LambdaHandlerResolver.extensionPointName, + listOf(), + disposableRule.disposable + ) + runInEdtAndWait { + val action = CreateLambdaFunction() + val actionEvent = TestActionEvent() + action.update(actionEvent) + assertThat(actionEvent.presentation.isVisible).isFalse() + } + } } diff --git a/jetbrains-core/tst/software/aws/toolkits/jetbrains/services/lambda/upload/EditFunctionDialogTest.kt b/jetbrains-core/tst/software/aws/toolkits/jetbrains/services/lambda/upload/EditFunctionDialogTest.kt index 3d6835dfcf..dfd3ec6ebf 100644 --- a/jetbrains-core/tst/software/aws/toolkits/jetbrains/services/lambda/upload/EditFunctionDialogTest.kt +++ b/jetbrains-core/tst/software/aws/toolkits/jetbrains/services/lambda/upload/EditFunctionDialogTest.kt @@ -22,6 +22,7 @@ import software.amazon.awssdk.services.iam.model.ListRolesRequest import software.amazon.awssdk.services.iam.model.ListRolesResponse import software.amazon.awssdk.services.iam.model.Role import software.amazon.awssdk.services.iam.paginators.ListRolesIterable +import software.amazon.awssdk.services.lambda.model.Runtime import software.amazon.awssdk.services.s3.S3Client import software.amazon.awssdk.services.s3.model.Bucket import software.amazon.awssdk.services.s3.model.HeadBucketRequest @@ -29,8 +30,9 @@ import software.amazon.awssdk.services.s3.model.HeadBucketResponse import software.amazon.awssdk.services.s3.model.ListBucketsResponse import software.aws.toolkits.core.region.AwsRegion import software.aws.toolkits.jetbrains.core.MockClientManagerRule -import software.aws.toolkits.jetbrains.core.credentials.MockAwsConnectionManager import software.aws.toolkits.jetbrains.core.credentials.AwsConnectionManager +import software.aws.toolkits.jetbrains.core.credentials.MockAwsConnectionManager +import software.aws.toolkits.jetbrains.services.lambda.LambdaHandlerResolver import software.aws.toolkits.jetbrains.utils.rules.JavaCodeInsightTestFixtureRule class EditFunctionDialogTest { @@ -63,6 +65,32 @@ class EditFunctionDialogTest { } } + @Test + fun `On new and update code, dialog only shows runtimes we can build`() { + val dialog = runInEdtAndGet { + EditFunctionDialog(project = projectRule.project, mode = EditFunctionMode.NEW) + } + assertThat(dialog.getViewForTestAssertions().runtime.model.size).isEqualTo(LambdaHandlerResolver.supportedRuntimeGroups().flatMap { it.runtimes }.size) + assertThat(dialog.getViewForTestAssertions().runtime.model.size).isNotEqualTo(Runtime.knownValues().size) + + val dialog2 = runInEdtAndGet { + EditFunctionDialog(project = projectRule.project, mode = EditFunctionMode.UPDATE_CODE) + } + assertThat(dialog2.getViewForTestAssertions().runtime.model.size).isEqualTo(LambdaHandlerResolver.supportedRuntimeGroups().flatMap { it.runtimes }.size) + assertThat(dialog2.getViewForTestAssertions().runtime.model.size).isNotEqualTo(Runtime.knownValues().size) + } + + @Test + fun `On update configuration, dialog shows all runtimes`() { + val dialog = runInEdtAndGet { + EditFunctionDialog(project = projectRule.project, mode = EditFunctionMode.UPDATE_CONFIGURATION) + } + assertThat(dialog.getViewForTestAssertions().runtime.model.size).isNotEqualTo( + LambdaHandlerResolver.supportedRuntimeGroups().flatMap { it.runtimes }.size + ) + assertThat(dialog.getViewForTestAssertions().runtime.model.size).isEqualTo(Runtime.knownValues().size) + } + @Test fun newShowsConfigurationDeploymentAndBuildSettings() { mockBuckets() From 99600ce3e4b52e529a9cc5d4f1d79e3a5ec4cd0e Mon Sep 17 00:00:00 2001 From: Austin Brooks Date: Fri, 14 Aug 2020 16:26:57 -0700 Subject: [PATCH 0017/1546] Improve scaling of explorer error message (#1994) --- .../toolkits/jetbrains/core/explorer/ExplorerToolWindow.kt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/core/explorer/ExplorerToolWindow.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/core/explorer/ExplorerToolWindow.kt index 2c39212b55..0e59329f44 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/core/explorer/ExplorerToolWindow.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/core/explorer/ExplorerToolWindow.kt @@ -97,7 +97,8 @@ class ExplorerToolWindow(project: Project) : SimpleToolWindowPanel(true, true), val gridBag = GridBag() gridBag.defaultAnchor = GridBagConstraints.CENTER - gridBag.defaultInsets = JBUI.insetsBottom(JBUI.scale(6)) + gridBag.defaultWeightX = 1.0 + gridBag.defaultInsets = JBUI.insets(0, JBUI.scale(30), JBUI.scale(6), JBUI.scale(30)) val textPane = JTextPane().apply { val textColor = if (state is ConnectionState.InvalidConnection) { @@ -119,7 +120,7 @@ class ExplorerToolWindow(project: Project) : SimpleToolWindowPanel(true, true), background = UIUtil.getTreeBackground() } - panel.add(textPane, gridBag.nextLine().next()) + panel.add(textPane, gridBag.nextLine().next().fillCell()) state.actions.forEach { panel.add(createActionLabel(it), gridBag.nextLine().next()) From 323981512a806400cbcfaa3ad1ff13d5a8e81eec Mon Sep 17 00:00:00 2001 From: Hunter Werlla Date: Fri, 14 Aug 2020 23:41:24 -0700 Subject: [PATCH 0018/1546] Gradle buildSrc cleanup (#1993) - Move buildSrc from namespace toolkits.* to software.aws.toolkits.* - Move validateLocalizedMessages into buildSrc, convert to Kotlin, and give it better messages --- build.gradle | 30 ++-------- buildSrc/build.gradle.kts | 2 +- .../toolkits/gradle/changelog/ChangeLog.kt | 4 +- .../gradle/changelog/ChangeLogGenerator.kt | 16 +++--- .../gradle/changelog/ChangeLogPlugin.kt | 6 +- .../gradle/changelog/ChangeLogWriter.kt | 4 +- .../toolkits/gradle/changelog/GitStager.kt | 4 +- .../toolkits/gradle/changelog/GithubWriter.kt | 4 +- .../gradle/changelog/JetBrainsWriter.kt | 4 +- .../gradle/changelog/ReleaseCreator.kt | 4 +- .../gradle/changelog/tasks/ChangeLogTask.kt | 4 +- .../gradle/changelog/tasks/CreateRelease.kt | 4 +- .../changelog/tasks/GenerateChangeLog.kt | 12 ++-- .../gradle/changelog/tasks/NewChange.kt | 8 +-- .../gradle/resources/ValidateMessages.kt | 55 +++++++++++++++++++ .../aws}/toolkits/gradle/sdk/GenerateSdk.kt | 4 +- .../changelog/ChangeLogGeneratorTest.kt | 8 +-- .../gradle/changelog/GitStagerTest.kt | 2 +- .../gradle/changelog/GithubWriterTest.kt | 4 +- .../gradle/changelog/JetBrainsWriterTest.kt | 4 +- .../gradle/changelog/ReleaseCreatorTest.kt | 2 +- jetbrains-core/build.gradle.kts | 2 +- telemetry-client/build.gradle.kts | 2 +- 23 files changed, 110 insertions(+), 79 deletions(-) rename buildSrc/src/{ => software/aws}/toolkits/gradle/changelog/ChangeLog.kt (93%) rename buildSrc/src/{ => software/aws}/toolkits/gradle/changelog/ChangeLogGenerator.kt (85%) rename buildSrc/src/{ => software/aws}/toolkits/gradle/changelog/ChangeLogPlugin.kt (86%) rename buildSrc/src/{ => software/aws}/toolkits/gradle/changelog/ChangeLogWriter.kt (87%) rename buildSrc/src/{ => software/aws}/toolkits/gradle/changelog/GitStager.kt (88%) rename buildSrc/src/{ => software/aws}/toolkits/gradle/changelog/GithubWriter.kt (79%) rename buildSrc/src/{ => software/aws}/toolkits/gradle/changelog/JetBrainsWriter.kt (92%) rename buildSrc/src/{ => software/aws}/toolkits/gradle/changelog/ReleaseCreator.kt (89%) rename buildSrc/src/{ => software/aws}/toolkits/gradle/changelog/tasks/ChangeLogTask.kt (89%) rename buildSrc/src/{ => software/aws}/toolkits/gradle/changelog/tasks/CreateRelease.kt (92%) rename buildSrc/src/{ => software/aws}/toolkits/gradle/changelog/tasks/GenerateChangeLog.kt (85%) rename buildSrc/src/{ => software/aws}/toolkits/gradle/changelog/tasks/NewChange.kt (91%) create mode 100644 buildSrc/src/software/aws/toolkits/gradle/resources/ValidateMessages.kt rename buildSrc/src/{ => software/aws}/toolkits/gradle/sdk/GenerateSdk.kt (95%) rename buildSrc/tst/{ => software/aws}/toolkits/gradle/changelog/ChangeLogGeneratorTest.kt (97%) rename buildSrc/tst/{ => software/aws}/toolkits/gradle/changelog/GitStagerTest.kt (98%) rename buildSrc/tst/{ => software/aws}/toolkits/gradle/changelog/GithubWriterTest.kt (94%) rename buildSrc/tst/{ => software/aws}/toolkits/gradle/changelog/JetBrainsWriterTest.kt (97%) rename buildSrc/tst/{ => software/aws}/toolkits/gradle/changelog/ReleaseCreatorTest.kt (97%) diff --git a/build.gradle b/build.gradle index b54eebb066..780eef9ae7 100644 --- a/build.gradle +++ b/build.gradle @@ -2,7 +2,8 @@ // SPDX-License-Identifier: Apache-2.0 import org.jetbrains.kotlin.gradle.tasks.KotlinCompile -import toolkits.gradle.changelog.tasks.GenerateGithubChangeLog +import software.aws.toolkits.gradle.changelog.tasks.GenerateGithubChangeLog +import software.aws.toolkits.gradle.resources.ValidateMessages import java.nio.file.Files import java.nio.file.Paths @@ -278,31 +279,8 @@ task ktlint(type: JavaExec, group: "verification") { outputs.dir("${project.buildDir}/reports/ktlint/") } -task validateLocalizedMessages(group: "verification") { - doLast { - BufferedReader files = Files.newBufferedReader(Paths.get("${project.rootDir}/resources/resources/software/aws/toolkits/resources/localized_messages.properties")) - files - .lines() - .map({ item -> - if (item == null || item.isEmpty()) { - return "" - } - String[] chunks = item.split("=") - if (chunks.length <= 1) { - return "" - } else { - return chunks[0] - } - }) - .filter({ item -> !item.isEmpty() }) - .reduce({ item1, item2 -> - if (item1 > item2) { - throw new GradleException("localization file is not sorted:" + item1 + " > " + item2) - } - - return item2 - }) - } +tasks.register('validateLocalizedMessages', ValidateMessages) { + paths = [file("${project.rootDir}/resources/resources/software/aws/toolkits/resources/localized_messages.properties")] } check.dependsOn ktlint diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index f4d883d576..27c4e8a1fe 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -60,7 +60,7 @@ gradlePlugin { plugins { create("changeLog") { id = "toolkit-change-log" - implementationClass = "toolkits.gradle.changelog.ChangeLogPlugin" + implementationClass = "software.aws.toolkits.gradle.changelog.ChangeLogPlugin" } } } diff --git a/buildSrc/src/toolkits/gradle/changelog/ChangeLog.kt b/buildSrc/src/software/aws/toolkits/gradle/changelog/ChangeLog.kt similarity index 93% rename from buildSrc/src/toolkits/gradle/changelog/ChangeLog.kt rename to buildSrc/src/software/aws/toolkits/gradle/changelog/ChangeLog.kt index 5d0e494181..1945a839fa 100644 --- a/buildSrc/src/toolkits/gradle/changelog/ChangeLog.kt +++ b/buildSrc/src/software/aws/toolkits/gradle/changelog/ChangeLog.kt @@ -1,7 +1,7 @@ -// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -package toolkits.gradle.changelog +package software.aws.toolkits.gradle.changelog import com.fasterxml.jackson.core.JsonGenerator import com.fasterxml.jackson.databind.MapperFeature diff --git a/buildSrc/src/toolkits/gradle/changelog/ChangeLogGenerator.kt b/buildSrc/src/software/aws/toolkits/gradle/changelog/ChangeLogGenerator.kt similarity index 85% rename from buildSrc/src/toolkits/gradle/changelog/ChangeLogGenerator.kt rename to buildSrc/src/software/aws/toolkits/gradle/changelog/ChangeLogGenerator.kt index 591a05fa12..27a6371f05 100644 --- a/buildSrc/src/toolkits/gradle/changelog/ChangeLogGenerator.kt +++ b/buildSrc/src/software/aws/toolkits/gradle/changelog/ChangeLogGenerator.kt @@ -1,9 +1,9 @@ -// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -package toolkits.gradle.changelog +package software.aws.toolkits.gradle.changelog -import org.gradle.api.logging.Logging +import org.gradle.api.logging.Logger import java.nio.file.Path import java.time.LocalDate import java.time.format.DateTimeFormatter @@ -13,20 +13,20 @@ import kotlin.streams.toList /** * Generates a combined change log file based in Markdown syntax */ -class ChangeLogGenerator(private val writers: List) { +class ChangeLogGenerator(private val writers: List, private val logger: Logger) { fun addUnreleasedChanges(unreleasedFiles: List) { val entries = unreleasedFiles.parallelStream() .map { readFile(it.toFile()) } .toList().filterNotNull() val unreleasedEntry = ReleaseEntry(LocalDate.now(), "Pending Release", entries) - LOGGER.info("Adding unreleased entry: $unreleasedEntry") + logger.info("Adding unreleased entry: $unreleasedEntry") generateEntry(unreleasedEntry) } fun addReleasedChanges(changelogFiles: List) { val versions = mutableSetOf() - LOGGER.info("Including release change logs: $changelogFiles") + logger.info("Including release change logs: $changelogFiles") changelogFiles.parallelStream() .map { readFile(it.toFile()) } .toList() @@ -38,7 +38,7 @@ class ChangeLogGenerator(private val writers: List) { } .sortedByDescending { it.date } .forEach { - LOGGER.info("Adding release entry: $it") + logger.info("Adding release entry: $it") generateEntry(it) } } @@ -55,8 +55,6 @@ class ChangeLogGenerator(private val writers: List) { } companion object { - private val LOGGER = Logging.getLogger(ChangeLogGenerator::class.java) - fun renderEntry(releaseEntry: ReleaseEntry): String { val renderedEntry = StringBuilder() diff --git a/buildSrc/src/toolkits/gradle/changelog/ChangeLogPlugin.kt b/buildSrc/src/software/aws/toolkits/gradle/changelog/ChangeLogPlugin.kt similarity index 86% rename from buildSrc/src/toolkits/gradle/changelog/ChangeLogPlugin.kt rename to buildSrc/src/software/aws/toolkits/gradle/changelog/ChangeLogPlugin.kt index 4e50a47816..48470cb41f 100644 --- a/buildSrc/src/toolkits/gradle/changelog/ChangeLogPlugin.kt +++ b/buildSrc/src/software/aws/toolkits/gradle/changelog/ChangeLogPlugin.kt @@ -1,12 +1,12 @@ // Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -package toolkits.gradle.changelog +package software.aws.toolkits.gradle.changelog import org.gradle.api.Plugin import org.gradle.api.Project -import toolkits.gradle.changelog.tasks.CreateRelease -import toolkits.gradle.changelog.tasks.NewChange +import software.aws.toolkits.gradle.changelog.tasks.CreateRelease +import software.aws.toolkits.gradle.changelog.tasks.NewChange @Suppress("unused") // Plugin is created by buildSrc/build.gradle class ChangeLogPlugin : Plugin { diff --git a/buildSrc/src/toolkits/gradle/changelog/ChangeLogWriter.kt b/buildSrc/src/software/aws/toolkits/gradle/changelog/ChangeLogWriter.kt similarity index 87% rename from buildSrc/src/toolkits/gradle/changelog/ChangeLogWriter.kt rename to buildSrc/src/software/aws/toolkits/gradle/changelog/ChangeLogWriter.kt index 7737b85e17..9ab962da05 100644 --- a/buildSrc/src/toolkits/gradle/changelog/ChangeLogWriter.kt +++ b/buildSrc/src/software/aws/toolkits/gradle/changelog/ChangeLogWriter.kt @@ -1,7 +1,7 @@ -// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -package toolkits.gradle.changelog +package software.aws.toolkits.gradle.changelog abstract class ChangeLogWriter(issueUrl: String? = null) { private val issueUrl = issueUrl?.trimEnd('/')?.plus("/") diff --git a/buildSrc/src/toolkits/gradle/changelog/GitStager.kt b/buildSrc/src/software/aws/toolkits/gradle/changelog/GitStager.kt similarity index 88% rename from buildSrc/src/toolkits/gradle/changelog/GitStager.kt rename to buildSrc/src/software/aws/toolkits/gradle/changelog/GitStager.kt index e9c87cb753..0a63f97f52 100644 --- a/buildSrc/src/toolkits/gradle/changelog/GitStager.kt +++ b/buildSrc/src/software/aws/toolkits/gradle/changelog/GitStager.kt @@ -1,7 +1,7 @@ -// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -package toolkits.gradle.changelog +package software.aws.toolkits.gradle.changelog import org.eclipse.jgit.api.Git import java.io.File diff --git a/buildSrc/src/toolkits/gradle/changelog/GithubWriter.kt b/buildSrc/src/software/aws/toolkits/gradle/changelog/GithubWriter.kt similarity index 79% rename from buildSrc/src/toolkits/gradle/changelog/GithubWriter.kt rename to buildSrc/src/software/aws/toolkits/gradle/changelog/GithubWriter.kt index ee4ca5d7d9..acbe0d8402 100644 --- a/buildSrc/src/toolkits/gradle/changelog/GithubWriter.kt +++ b/buildSrc/src/software/aws/toolkits/gradle/changelog/GithubWriter.kt @@ -1,7 +1,7 @@ -// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -package toolkits.gradle.changelog +package software.aws.toolkits.gradle.changelog import java.nio.file.Path diff --git a/buildSrc/src/toolkits/gradle/changelog/JetBrainsWriter.kt b/buildSrc/src/software/aws/toolkits/gradle/changelog/JetBrainsWriter.kt similarity index 92% rename from buildSrc/src/toolkits/gradle/changelog/JetBrainsWriter.kt rename to buildSrc/src/software/aws/toolkits/gradle/changelog/JetBrainsWriter.kt index 986ca9d226..1d94c61c0e 100644 --- a/buildSrc/src/toolkits/gradle/changelog/JetBrainsWriter.kt +++ b/buildSrc/src/software/aws/toolkits/gradle/changelog/JetBrainsWriter.kt @@ -1,7 +1,7 @@ -// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -package toolkits.gradle.changelog +package software.aws.toolkits.gradle.changelog import org.commonmark.node.AbstractVisitor import org.commonmark.node.Heading diff --git a/buildSrc/src/toolkits/gradle/changelog/ReleaseCreator.kt b/buildSrc/src/software/aws/toolkits/gradle/changelog/ReleaseCreator.kt similarity index 89% rename from buildSrc/src/toolkits/gradle/changelog/ReleaseCreator.kt rename to buildSrc/src/software/aws/toolkits/gradle/changelog/ReleaseCreator.kt index 3fee200808..0a1c76bd83 100644 --- a/buildSrc/src/toolkits/gradle/changelog/ReleaseCreator.kt +++ b/buildSrc/src/software/aws/toolkits/gradle/changelog/ReleaseCreator.kt @@ -1,7 +1,7 @@ -// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -package toolkits.gradle.changelog +package software.aws.toolkits.gradle.changelog import java.io.File import java.time.LocalDate diff --git a/buildSrc/src/toolkits/gradle/changelog/tasks/ChangeLogTask.kt b/buildSrc/src/software/aws/toolkits/gradle/changelog/tasks/ChangeLogTask.kt similarity index 89% rename from buildSrc/src/toolkits/gradle/changelog/tasks/ChangeLogTask.kt rename to buildSrc/src/software/aws/toolkits/gradle/changelog/tasks/ChangeLogTask.kt index 7a6f0baeee..cdbe0309e4 100644 --- a/buildSrc/src/toolkits/gradle/changelog/tasks/ChangeLogTask.kt +++ b/buildSrc/src/software/aws/toolkits/gradle/changelog/tasks/ChangeLogTask.kt @@ -1,7 +1,7 @@ // Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -package toolkits.gradle.changelog.tasks +package software.aws.toolkits.gradle.changelog.tasks import org.gradle.api.DefaultTask import org.gradle.api.file.DirectoryProperty @@ -9,7 +9,7 @@ import org.gradle.api.file.FileTree import org.gradle.api.tasks.InputDirectory import org.gradle.api.tasks.InputFiles import org.gradle.api.tasks.Internal -import toolkits.gradle.changelog.GitStager +import software.aws.toolkits.gradle.changelog.GitStager abstract class ChangeLogTask : DefaultTask() { @Internal diff --git a/buildSrc/src/toolkits/gradle/changelog/tasks/CreateRelease.kt b/buildSrc/src/software/aws/toolkits/gradle/changelog/tasks/CreateRelease.kt similarity index 92% rename from buildSrc/src/toolkits/gradle/changelog/tasks/CreateRelease.kt rename to buildSrc/src/software/aws/toolkits/gradle/changelog/tasks/CreateRelease.kt index 8ca71867f1..bff48d79d8 100644 --- a/buildSrc/src/toolkits/gradle/changelog/tasks/CreateRelease.kt +++ b/buildSrc/src/software/aws/toolkits/gradle/changelog/tasks/CreateRelease.kt @@ -1,14 +1,14 @@ // Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -package toolkits.gradle.changelog.tasks +package software.aws.toolkits.gradle.changelog.tasks import org.gradle.api.file.RegularFileProperty import org.gradle.api.provider.Property import org.gradle.api.tasks.Input import org.gradle.api.tasks.OutputFile import org.gradle.api.tasks.TaskAction -import toolkits.gradle.changelog.ReleaseCreator +import software.aws.toolkits.gradle.changelog.ReleaseCreator import java.time.LocalDate import java.time.format.DateTimeFormatter diff --git a/buildSrc/src/toolkits/gradle/changelog/tasks/GenerateChangeLog.kt b/buildSrc/src/software/aws/toolkits/gradle/changelog/tasks/GenerateChangeLog.kt similarity index 85% rename from buildSrc/src/toolkits/gradle/changelog/tasks/GenerateChangeLog.kt rename to buildSrc/src/software/aws/toolkits/gradle/changelog/tasks/GenerateChangeLog.kt index 363658b3f8..6bf4addec9 100644 --- a/buildSrc/src/toolkits/gradle/changelog/tasks/GenerateChangeLog.kt +++ b/buildSrc/src/software/aws/toolkits/gradle/changelog/tasks/GenerateChangeLog.kt @@ -1,7 +1,7 @@ // Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -package toolkits.gradle.changelog.tasks +package software.aws.toolkits.gradle.changelog.tasks import org.gradle.api.file.RegularFileProperty import org.gradle.api.provider.Property @@ -10,10 +10,10 @@ import org.gradle.api.tasks.Input import org.gradle.api.tasks.Optional import org.gradle.api.tasks.OutputFile import org.gradle.api.tasks.TaskAction -import toolkits.gradle.changelog.ChangeLogGenerator -import toolkits.gradle.changelog.ChangeLogWriter -import toolkits.gradle.changelog.GithubWriter -import toolkits.gradle.changelog.JetBrainsWriter +import software.aws.toolkits.gradle.changelog.ChangeLogGenerator +import software.aws.toolkits.gradle.changelog.ChangeLogWriter +import software.aws.toolkits.gradle.changelog.GithubWriter +import software.aws.toolkits.gradle.changelog.JetBrainsWriter /* ktlint-disable custom-ktlint-rules:log-not-lazy */ abstract class GenerateChangeLog(private val shouldStage: Boolean) : ChangeLogTask() { @@ -31,7 +31,7 @@ abstract class GenerateChangeLog(private val shouldStage: Boolean) : ChangeLogTa fun generate() { val writer = createWriter() logger.info("Generating Changelog with $writer") - val generator = ChangeLogGenerator(listOf(writer)) + val generator = ChangeLogGenerator(listOf(writer), logger) if (includeUnreleased.get()) { val unreleasedEntries = nextReleaseDirectory.jsonFiles().files diff --git a/buildSrc/src/toolkits/gradle/changelog/tasks/NewChange.kt b/buildSrc/src/software/aws/toolkits/gradle/changelog/tasks/NewChange.kt similarity index 91% rename from buildSrc/src/toolkits/gradle/changelog/tasks/NewChange.kt rename to buildSrc/src/software/aws/toolkits/gradle/changelog/tasks/NewChange.kt index 1457bd3971..a99078b7da 100644 --- a/buildSrc/src/toolkits/gradle/changelog/tasks/NewChange.kt +++ b/buildSrc/src/software/aws/toolkits/gradle/changelog/tasks/NewChange.kt @@ -1,13 +1,13 @@ // Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -package toolkits.gradle.changelog.tasks +package software.aws.toolkits.gradle.changelog.tasks import org.gradle.api.tasks.Internal import org.gradle.api.tasks.TaskAction -import toolkits.gradle.changelog.ChangeType -import toolkits.gradle.changelog.Entry -import toolkits.gradle.changelog.MAPPER +import software.aws.toolkits.gradle.changelog.ChangeType +import software.aws.toolkits.gradle.changelog.Entry +import software.aws.toolkits.gradle.changelog.MAPPER import java.io.File import java.util.Scanner import java.util.UUID diff --git a/buildSrc/src/software/aws/toolkits/gradle/resources/ValidateMessages.kt b/buildSrc/src/software/aws/toolkits/gradle/resources/ValidateMessages.kt new file mode 100644 index 0000000000..7e63e6d2cd --- /dev/null +++ b/buildSrc/src/software/aws/toolkits/gradle/resources/ValidateMessages.kt @@ -0,0 +1,55 @@ +// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package software.aws.toolkits.gradle.resources + +import org.gradle.api.DefaultTask +import org.gradle.api.GradleException +import org.gradle.api.provider.ListProperty +import org.gradle.api.tasks.InputFiles +import org.gradle.api.tasks.TaskAction +import org.gradle.language.base.plugins.LifecycleBasePlugin.VERIFICATION_GROUP +import java.io.File + +open class ValidateMessages : DefaultTask() { + @InputFiles + val paths: ListProperty = project.objects.listProperty(File::class.java) + + init { + group = VERIFICATION_GROUP + } + + @TaskAction + fun validateMessage() { + var hasError = false + paths + .get() + .map { + it.absolutePath to it.readLines() + }.forEach { (filePath, fileLines) -> + fileLines + // filter out blank lines and comments + .filter { it.isNotBlank() && it.trim().firstOrNull() != '#' } + .mapNotNull { + if (it.contains("=")) { + it + } else { + logger.warn(""""$filePath contains invalid message missing a '=': "$it"""") + null + } + } + .map { it.split("=").first() } + .reduce { item1, item2 -> + if (item1 > item2) { + logger.error("""$filePath is not sorted:"$item1" > "$item2"""") + hasError = true + } + + item2 + } + if (hasError) { + throw GradleException("$filePath has one or more out of order items!") + } + } + } +} diff --git a/buildSrc/src/toolkits/gradle/sdk/GenerateSdk.kt b/buildSrc/src/software/aws/toolkits/gradle/sdk/GenerateSdk.kt similarity index 95% rename from buildSrc/src/toolkits/gradle/sdk/GenerateSdk.kt rename to buildSrc/src/software/aws/toolkits/gradle/sdk/GenerateSdk.kt index d94f280fe9..77ec1a439a 100644 --- a/buildSrc/src/toolkits/gradle/sdk/GenerateSdk.kt +++ b/buildSrc/src/software/aws/toolkits/gradle/sdk/GenerateSdk.kt @@ -1,7 +1,7 @@ -// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -package toolkits.gradle.sdk +package software.aws.toolkits.gradle.sdk import org.gradle.api.DefaultTask import org.gradle.api.tasks.InputDirectory diff --git a/buildSrc/tst/toolkits/gradle/changelog/ChangeLogGeneratorTest.kt b/buildSrc/tst/software/aws/toolkits/gradle/changelog/ChangeLogGeneratorTest.kt similarity index 97% rename from buildSrc/tst/toolkits/gradle/changelog/ChangeLogGeneratorTest.kt rename to buildSrc/tst/software/aws/toolkits/gradle/changelog/ChangeLogGeneratorTest.kt index 4cfab03746..78e3922a7f 100644 --- a/buildSrc/tst/toolkits/gradle/changelog/ChangeLogGeneratorTest.kt +++ b/buildSrc/tst/software/aws/toolkits/gradle/changelog/ChangeLogGeneratorTest.kt @@ -1,7 +1,7 @@ // Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -package toolkits.gradle.changelog +package software.aws.toolkits.gradle.changelog import com.nhaarman.mockitokotlin2.argumentCaptor import com.nhaarman.mockitokotlin2.inOrder @@ -14,9 +14,9 @@ import org.junit.Rule import org.junit.Test import org.junit.rules.TemporaryFolder import org.mockito.ArgumentMatchers.anyString -import toolkits.gradle.changelog.ChangeLogGenerator.Companion.renderEntry -import toolkits.gradle.changelog.ChangeType.BUGFIX -import toolkits.gradle.changelog.ChangeType.FEATURE +import software.aws.toolkits.gradle.changelog.ChangeLogGenerator.Companion.renderEntry +import software.aws.toolkits.gradle.changelog.ChangeType.BUGFIX +import software.aws.toolkits.gradle.changelog.ChangeType.FEATURE import java.nio.file.Path import java.time.LocalDate diff --git a/buildSrc/tst/toolkits/gradle/changelog/GitStagerTest.kt b/buildSrc/tst/software/aws/toolkits/gradle/changelog/GitStagerTest.kt similarity index 98% rename from buildSrc/tst/toolkits/gradle/changelog/GitStagerTest.kt rename to buildSrc/tst/software/aws/toolkits/gradle/changelog/GitStagerTest.kt index e273dd747c..a398707533 100644 --- a/buildSrc/tst/toolkits/gradle/changelog/GitStagerTest.kt +++ b/buildSrc/tst/software/aws/toolkits/gradle/changelog/GitStagerTest.kt @@ -1,7 +1,7 @@ // Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -package toolkits.gradle.changelog +package software.aws.toolkits.gradle.changelog import org.assertj.core.api.Assertions.assertThat import org.eclipse.jgit.api.Git diff --git a/buildSrc/tst/toolkits/gradle/changelog/GithubWriterTest.kt b/buildSrc/tst/software/aws/toolkits/gradle/changelog/GithubWriterTest.kt similarity index 94% rename from buildSrc/tst/toolkits/gradle/changelog/GithubWriterTest.kt rename to buildSrc/tst/software/aws/toolkits/gradle/changelog/GithubWriterTest.kt index 22b51e417f..5c0cd13c4b 100644 --- a/buildSrc/tst/toolkits/gradle/changelog/GithubWriterTest.kt +++ b/buildSrc/tst/software/aws/toolkits/gradle/changelog/GithubWriterTest.kt @@ -1,13 +1,13 @@ // Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -package toolkits.gradle.changelog +package software.aws.toolkits.gradle.changelog import org.assertj.core.api.Assertions.assertThat import org.junit.Rule import org.junit.Test import org.junit.rules.TemporaryFolder -import toolkits.gradle.changelog.ChangeLogGenerator.Companion.renderEntry +import software.aws.toolkits.gradle.changelog.ChangeLogGenerator.Companion.renderEntry import java.time.LocalDate class GithubWriterTest { diff --git a/buildSrc/tst/toolkits/gradle/changelog/JetBrainsWriterTest.kt b/buildSrc/tst/software/aws/toolkits/gradle/changelog/JetBrainsWriterTest.kt similarity index 97% rename from buildSrc/tst/toolkits/gradle/changelog/JetBrainsWriterTest.kt rename to buildSrc/tst/software/aws/toolkits/gradle/changelog/JetBrainsWriterTest.kt index e147c8dbc3..888a8f2fc0 100644 --- a/buildSrc/tst/toolkits/gradle/changelog/JetBrainsWriterTest.kt +++ b/buildSrc/tst/software/aws/toolkits/gradle/changelog/JetBrainsWriterTest.kt @@ -1,13 +1,13 @@ // Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -package toolkits.gradle.changelog +package software.aws.toolkits.gradle.changelog import org.assertj.core.api.Assertions.assertThat import org.junit.Rule import org.junit.Test import org.junit.rules.TemporaryFolder -import toolkits.gradle.changelog.ChangeLogGenerator.Companion.renderEntry +import software.aws.toolkits.gradle.changelog.ChangeLogGenerator.Companion.renderEntry import java.time.LocalDate class JetBrainsWriterTest { diff --git a/buildSrc/tst/toolkits/gradle/changelog/ReleaseCreatorTest.kt b/buildSrc/tst/software/aws/toolkits/gradle/changelog/ReleaseCreatorTest.kt similarity index 97% rename from buildSrc/tst/toolkits/gradle/changelog/ReleaseCreatorTest.kt rename to buildSrc/tst/software/aws/toolkits/gradle/changelog/ReleaseCreatorTest.kt index a51dfc04c8..17c6edc904 100644 --- a/buildSrc/tst/toolkits/gradle/changelog/ReleaseCreatorTest.kt +++ b/buildSrc/tst/software/aws/toolkits/gradle/changelog/ReleaseCreatorTest.kt @@ -1,7 +1,7 @@ // Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -package toolkits.gradle.changelog +package software.aws.toolkits.gradle.changelog import org.assertj.core.api.Assertions.assertThat import org.junit.Rule diff --git a/jetbrains-core/build.gradle.kts b/jetbrains-core/build.gradle.kts index f7ee063ed4..504f2a61dc 100644 --- a/jetbrains-core/build.gradle.kts +++ b/jetbrains-core/build.gradle.kts @@ -5,7 +5,7 @@ import groovy.lang.Closure import org.jetbrains.intellij.tasks.PatchPluginXmlTask import org.jetbrains.kotlin.gradle.tasks.KotlinCompile import software.aws.toolkits.telemetry.generator.gradle.GenerateTelemetry -import toolkits.gradle.changelog.tasks.GeneratePluginChangeLog +import software.aws.toolkits.gradle.changelog.tasks.GeneratePluginChangeLog plugins { id("org.jetbrains.intellij") diff --git a/telemetry-client/build.gradle.kts b/telemetry-client/build.gradle.kts index 09392c25eb..2e94862386 100644 --- a/telemetry-client/build.gradle.kts +++ b/telemetry-client/build.gradle.kts @@ -1,7 +1,7 @@ // Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import toolkits.gradle.sdk.GenerateSdk +import software.aws.toolkits.gradle.sdk.GenerateSdk val awsSdkVersion: String by project From c4820165785c96a1e732325c708654df2cc96dcf Mon Sep 17 00:00:00 2001 From: Ran Yoo <30319724+ranyoo2367@users.noreply.github.com> Date: Mon, 17 Aug 2020 12:59:22 -0400 Subject: [PATCH 0019/1546] SQS: Add SNS subscription feature (#1990) * SNS Subscription dialog setup * Added tests * UI fix * PR changes, added new test for subscribe action * Fix style * PR nit changes --- jetbrains-core/build.gradle.kts | 1 + jetbrains-core/resources/META-INF/plugin.xml | 3 + .../services/sqs/SubscribeSnsDialog.kt | 83 ++++++++++++++++++ .../services/sqs/SubscribeSnsPanel.form | 41 +++++++++ .../services/sqs/SubscribeSnsPanel.kt | 37 ++++++++ .../sqs/actions/SubscribeSnsAction.kt | 24 ++++++ .../services/sqs/resources/SnsResources.kt | 17 ++++ .../services/sqs/SubscribeSnsActionTest.kt | 52 ++++++++++++ .../services/sqs/SubscribeSnsDialogTest.kt | 85 +++++++++++++++++++ .../resources/localized_messages.properties | 10 +++ 10 files changed, 353 insertions(+) create mode 100644 jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/SubscribeSnsDialog.kt create mode 100644 jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/SubscribeSnsPanel.form create mode 100644 jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/SubscribeSnsPanel.kt create mode 100644 jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/actions/SubscribeSnsAction.kt create mode 100644 jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/resources/SnsResources.kt create mode 100644 jetbrains-core/tst/software/aws/toolkits/jetbrains/services/sqs/SubscribeSnsActionTest.kt create mode 100644 jetbrains-core/tst/software/aws/toolkits/jetbrains/services/sqs/SubscribeSnsDialogTest.kt diff --git a/jetbrains-core/build.gradle.kts b/jetbrains-core/build.gradle.kts index 29d26aeb95..6d5706f225 100644 --- a/jetbrains-core/build.gradle.kts +++ b/jetbrains-core/build.gradle.kts @@ -96,6 +96,7 @@ dependencies { api("software.amazon.awssdk:rds:$awsSdkVersion") api("software.amazon.awssdk:redshift:$awsSdkVersion") api("software.amazon.awssdk:secretsmanager:$awsSdkVersion") + api("software.amazon.awssdk:sns:$awsSdkVersion") api("software.amazon.awssdk:sqs:$awsSdkVersion") testImplementation(project(path = ":core", configuration = "testArtifacts")) diff --git a/jetbrains-core/resources/META-INF/plugin.xml b/jetbrains-core/resources/META-INF/plugin.xml index 8f2f25b45a..105fef4e3f 100644 --- a/jetbrains-core/resources/META-INF/plugin.xml +++ b/jetbrains-core/resources/META-INF/plugin.xml @@ -348,6 +348,9 @@ + +
diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/SubscribeSnsDialog.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/SubscribeSnsDialog.kt new file mode 100644 index 0000000000..8847e01bfc --- /dev/null +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/SubscribeSnsDialog.kt @@ -0,0 +1,83 @@ +// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package software.aws.toolkits.jetbrains.services.sqs + +import com.intellij.openapi.application.ModalityState +import com.intellij.openapi.application.runInEdt +import com.intellij.openapi.project.Project +import com.intellij.openapi.ui.DialogWrapper +import com.intellij.openapi.ui.ValidationInfo +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.launch +import software.amazon.awssdk.services.sns.SnsClient +import software.aws.toolkits.core.utils.getLogger +import software.aws.toolkits.core.utils.warn +import software.aws.toolkits.jetbrains.core.awsClient +import software.aws.toolkits.jetbrains.utils.ApplicationThreadPoolScope +import software.aws.toolkits.jetbrains.utils.notifyInfo +import software.aws.toolkits.resources.message +import javax.swing.JComponent + +class SubscribeSnsDialog( + private val project: Project, + private val queue: Queue +) : DialogWrapper(project), CoroutineScope by ApplicationThreadPoolScope("SubscribeSnsDialog") { + private val snsClient: SnsClient = project.awsClient() + val view = SubscribeSnsPanel(project) + + init { + title = message("sqs.subscribe.sns") + setOKButtonText(message("sqs.subscribe.sns.subscribe")) + + init() + } + + override fun createCenterPanel(): JComponent? = view.component + + override fun getPreferredFocusedComponent(): JComponent? = view.topicSelector + + override fun doValidate(): ValidationInfo? { + if (topicSelected().isEmpty()) { + return ValidationInfo(message("sqs.subscribe.sns.validation.empty_topic"), view.topicSelector) + } + return null + } + + override fun doOKAction() { + if (isOKActionEnabled) { + setOKButtonText(message("sqs.subscribe.sns.in_progress")) + isOKActionEnabled = false + + launch { + try { + subscribe(topicSelected()) + runInEdt(ModalityState.any()) { + close(OK_EXIT_CODE) + } + notifyInfo(message("sqs.service_name"), message("sqs.subscribe.sns.success", topicSelected()), project) + } catch (e: Exception) { + LOG.warn(e) { message("sqs.subscribe.sns.failed", queue.queueName, topicSelected()) } + setErrorText(e.message) + setOKButtonText(message("sqs.subscribe.sns.subscribe")) + isOKActionEnabled = true + } + } + } + } + + private fun topicSelected(): String = view.topicSelector.selected()?.topicArn() ?: "" + + internal fun subscribe(arn: String) { + snsClient.subscribe { + it.topicArn(arn) + it.protocol(PROTOCOL) + it.endpoint(queue.arn) + } + } + + private companion object { + val LOG = getLogger() + const val PROTOCOL = "sqs" + } +} diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/SubscribeSnsPanel.form b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/SubscribeSnsPanel.form new file mode 100644 index 0000000000..2760581807 --- /dev/null +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/SubscribeSnsPanel.form @@ -0,0 +1,41 @@ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/SubscribeSnsPanel.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/SubscribeSnsPanel.kt new file mode 100644 index 0000000000..13207b0a65 --- /dev/null +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/SubscribeSnsPanel.kt @@ -0,0 +1,37 @@ +// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +package software.aws.toolkits.jetbrains.services.sqs + +import com.intellij.icons.AllIcons +import com.intellij.ide.HelpTooltip +import com.intellij.openapi.project.Project +import com.intellij.ui.IdeBorderFactory +import software.amazon.awssdk.services.sns.model.Topic +import software.aws.toolkits.jetbrains.services.sqs.resources.SnsResources +import software.aws.toolkits.jetbrains.services.sqs.resources.getName +import software.aws.toolkits.jetbrains.ui.ResourceSelector +import software.aws.toolkits.resources.message +import javax.swing.JLabel +import javax.swing.JPanel + +class SubscribeSnsPanel(private val project: Project) { + lateinit var component: JPanel + lateinit var topicSelector: ResourceSelector + lateinit var selectContextHelp: JLabel + + init { + component.border = IdeBorderFactory.createTitledBorder(message("sqs.subscribe.sns.select")) + selectContextHelp.icon = AllIcons.General.ContextHelp + HelpTooltip().apply { + setDescription(message("sqs.subscribe.sns.select.tooltip")) + installOn(selectContextHelp) + } + } + + private fun createUIComponents() { + topicSelector = ResourceSelector.builder(project) + .resource(SnsResources.LIST_TOPICS) + .customRenderer { value, component -> component.append(value.getName()); component } + .build() + } +} diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/actions/SubscribeSnsAction.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/actions/SubscribeSnsAction.kt new file mode 100644 index 0000000000..bf2e1863e7 --- /dev/null +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/actions/SubscribeSnsAction.kt @@ -0,0 +1,24 @@ +// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package software.aws.toolkits.jetbrains.services.sqs.actions + +import com.intellij.openapi.actionSystem.AnActionEvent +import com.intellij.openapi.project.DumbAware +import software.aws.toolkits.jetbrains.core.explorer.actions.SingleResourceNodeAction +import software.aws.toolkits.jetbrains.services.sqs.SqsQueueNode +import software.aws.toolkits.jetbrains.services.sqs.SubscribeSnsDialog +import software.aws.toolkits.resources.message + +class SubscribeSnsAction : SingleResourceNodeAction(message("sqs.subscribe.sns")), DumbAware { + override fun update(selected: SqsQueueNode, e: AnActionEvent) { + // TODO: Amazon SNS isn't currently compatible with FIFO queues. + if (selected.queue.isFifo) { + e.presentation.isVisible = false + } + } + + override fun actionPerformed(selected: SqsQueueNode, e: AnActionEvent) { + SubscribeSnsDialog(selected.nodeProject, selected.queue).show() + } +} diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/resources/SnsResources.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/resources/SnsResources.kt new file mode 100644 index 0000000000..75a151aafd --- /dev/null +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/resources/SnsResources.kt @@ -0,0 +1,17 @@ +// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package software.aws.toolkits.jetbrains.services.sqs.resources + +import software.amazon.awssdk.services.sns.SnsClient +import software.amazon.awssdk.services.sns.model.Topic +import software.aws.toolkits.jetbrains.core.ClientBackedCachedResource +import software.aws.toolkits.jetbrains.core.Resource + +object SnsResources { + val LIST_TOPICS: Resource.Cached> = ClientBackedCachedResource(SnsClient::class, "sns.list_topics") { + listTopicsPaginator().topics().toList() + } +} + +fun Topic.getName(): String = topicArn().substringAfterLast(':') diff --git a/jetbrains-core/tst/software/aws/toolkits/jetbrains/services/sqs/SubscribeSnsActionTest.kt b/jetbrains-core/tst/software/aws/toolkits/jetbrains/services/sqs/SubscribeSnsActionTest.kt new file mode 100644 index 0000000000..2d4dfcabdb --- /dev/null +++ b/jetbrains-core/tst/software/aws/toolkits/jetbrains/services/sqs/SubscribeSnsActionTest.kt @@ -0,0 +1,52 @@ +// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package software.aws.toolkits.jetbrains.services.sqs + +import com.intellij.testFramework.ProjectRule +import com.intellij.testFramework.TestActionEvent +import org.assertj.core.api.Assertions.assertThat +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import software.amazon.awssdk.services.sqs.SqsClient +import software.aws.toolkits.jetbrains.core.MockClientManagerRule +import software.aws.toolkits.jetbrains.services.sqs.actions.SubscribeSnsAction + +class SubscribeSnsActionTest { + lateinit var client: SqsClient + + @Rule + @JvmField + val projectRule = ProjectRule() + + @JvmField + @Rule + val mockClientManagerRule = MockClientManagerRule(projectRule) + + @Before + fun setup() { + client = mockClientManagerRule.create() + } + + @Test + fun `Standard queue shows subscribe action`() { + val mockNode = SqsQueueNode(projectRule.project, STANDARD_QUEUE_URL) + val action = TestActionEvent() + SubscribeSnsAction().update(mockNode, action) + assertThat(action.presentation.isVisible).isTrue() + } + + @Test + fun `FIFO queue doesn't show subscribe action`() { + val mockNode = SqsQueueNode(projectRule.project, FIFO_QUEUE_URL) + val action = TestActionEvent() + SubscribeSnsAction().update(mockNode, action) + assertThat(action.presentation.isVisible).isFalse() + } + + private companion object { + const val STANDARD_QUEUE_URL = "https://sqs.us-east-1.amazonaws.com/123456789012/standard" + const val FIFO_QUEUE_URL = "https://sqs.us-east-1.amazonaws.com/123456789012/fifo.fifo" + } +} diff --git a/jetbrains-core/tst/software/aws/toolkits/jetbrains/services/sqs/SubscribeSnsDialogTest.kt b/jetbrains-core/tst/software/aws/toolkits/jetbrains/services/sqs/SubscribeSnsDialogTest.kt new file mode 100644 index 0000000000..a9f9e28379 --- /dev/null +++ b/jetbrains-core/tst/software/aws/toolkits/jetbrains/services/sqs/SubscribeSnsDialogTest.kt @@ -0,0 +1,85 @@ +// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package software.aws.toolkits.jetbrains.services.sqs + +import com.intellij.testFramework.ProjectRule +import com.intellij.testFramework.runInEdtAndWait +import com.nhaarman.mockitokotlin2.argumentCaptor +import com.nhaarman.mockitokotlin2.doReturn +import com.nhaarman.mockitokotlin2.doThrow +import com.nhaarman.mockitokotlin2.stub +import org.assertj.core.api.Assertions.assertThat +import org.assertj.core.api.Assertions.assertThatThrownBy +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import software.amazon.awssdk.services.sns.SnsClient +import software.amazon.awssdk.services.sns.model.InternalErrorException +import software.amazon.awssdk.services.sns.model.SubscribeRequest +import software.amazon.awssdk.services.sns.model.SubscribeResponse +import software.aws.toolkits.core.region.AwsRegion +import software.aws.toolkits.jetbrains.core.MockClientManagerRule +import software.aws.toolkits.jetbrains.core.region.MockRegionProvider + +class SubscribeSnsDialogTest { + lateinit var snsClient: SnsClient + lateinit var region: AwsRegion + lateinit var queue: Queue + + @Rule + @JvmField + val projectRule = ProjectRule() + + @JvmField + @Rule + val mockClientManagerRule = MockClientManagerRule(projectRule) + + @Before + fun setup() { + snsClient = mockClientManagerRule.create() + region = MockRegionProvider.getInstance().defaultRegion() + queue = Queue("https://sqs.us-east-1.amazonaws.com/123456789012/test", region) + } + + @Test + fun `No topic specified fails`() { + runInEdtAndWait { + val dialog = SubscribeSnsDialog(projectRule.project, queue) + val validationInfo = dialog.validate() + assertThat(validationInfo).isNotNull() + } + } + + @Test + fun `Error subscribing to topic`() { + val subscribeCaptor = argumentCaptor() + snsClient.stub { + on { subscribe(subscribeCaptor.capture()) } doThrow InternalErrorException.builder().message(ERROR_MESSAGE).build() + } + + runInEdtAndWait { + val dialog = SubscribeSnsDialog(projectRule.project, queue) + assertThatThrownBy { dialog.subscribe(TOPIC_ARN) }.hasMessage(ERROR_MESSAGE) + } + assertThat(subscribeCaptor.firstValue.topicArn()).isEqualTo(TOPIC_ARN) + } + + @Test + fun `Subscribing to topic succeeds`() { + val subscribeCaptor = argumentCaptor() + snsClient.stub { + on { subscribe(subscribeCaptor.capture()) } doReturn SubscribeResponse.builder().build() + } + + runInEdtAndWait { + SubscribeSnsDialog(projectRule.project, queue).subscribe(TOPIC_ARN) + } + assertThat(subscribeCaptor.firstValue.topicArn()).isEqualTo(TOPIC_ARN) + } + + private companion object { + const val TOPIC_ARN = "arn:aws:sns:us-east-1:123456789012:MyTopic" + const val ERROR_MESSAGE = "Network Error" + } +} diff --git a/resources/resources/software/aws/toolkits/resources/localized_messages.properties b/resources/resources/software/aws/toolkits/resources/localized_messages.properties index 911b16ad15..c50845a156 100644 --- a/resources/resources/software/aws/toolkits/resources/localized_messages.properties +++ b/resources/resources/software/aws/toolkits/resources/localized_messages.properties @@ -746,6 +746,16 @@ sqs.send.message.deduplication_id=Message Deduplication ID: sqs.send.message.group_id=Message Group ID: sqs.send.message.send.button=Send sqs.send.message.success=Sent message ID: {0} +sqs.service_name=Amazon SQS sqs.standard.queue.tooltip=Supports at-least-once delivery and message ordering is not preserved. +sqs.subscribe.sns=Subscribe to SNS topic +sqs.subscribe.sns.failed=Failed to subscribe {0} to {0} +sqs.subscribe.sns.in_progress=Subscribing... +sqs.subscribe.sns.select=Select SNS topic +sqs.subscribe.sns.select.tooltip=Messages from the selected topic are sent to the queue. +sqs.subscribe.sns.subscribe=Subscribe +sqs.subscribe.sns.success=Subscribed successfully to topic {0}. +sqs.subscribe.sns.topic=SNS topic: +sqs.subscribe.sns.validation.empty_topic=Topic must be specified. sqs.toolwindow=SQS sqs.url.parse_error=Error parsing queue URL From 38c12c8dd41c6e01603a157ea3f161fed016431b Mon Sep 17 00:00:00 2001 From: manodnyab <66754471+manodnyab@users.noreply.github.com> Date: Mon, 17 Aug 2020 12:05:42 -0700 Subject: [PATCH 0020/1546] CloudWatch Logs Insights : Results Table (#1979) * Results tab opens * String parsing done * Query Results * ColumnInfo updated * ResultsTable * Column Info upated * Results Table * Display results table after complete query executed * Results table without tests * Results table with tests * Accepted fields change * List of Result Fields mapped * Messages updated * Mutable components converted to Immutable * Opens Query Editor from Results Table * Open Query Editor updated * Updated Query EditorDialog * Query Editor Dialog Parameter name changed --- jetbrains-core/resources/META-INF/plugin.xml | 1 + .../logs/insights/ColumnInfoDetails.kt | 35 ++++ .../cloudwatch/logs/insights/QueryActor.kt | 176 ++++++++++++++++++ .../cloudwatch/logs/insights/QueryEditor.kt | 8 +- .../logs/insights/QueryEditorDialog.kt | 44 ++++- .../logs/insights/QueryEditorSavedState.kt | 44 +++++ .../logs/insights/QueryResultList.form | 50 +++++ .../logs/insights/QueryResultList.kt | 50 +++++ .../logs/insights/QueryResultsTable.kt | 59 ++++++ .../logs/insights/QueryResultsWindow.kt | 41 ++++ .../logs/insights/QueryingLogGroups.kt | 45 ++++- .../logs/insights/SelectedLogGroup.kt | 9 + .../logs/insights/actions/QueryGroupAction.kt | 5 +- .../insights/GetFieldsFromEnteredQueryTest.kt | 30 +++ .../logs/insights/QueryResultsReturnedTest.kt | 93 +++++++++ .../logs/insights/QueryingLogGroupsTest.kt | 2 +- .../resources/localized_messages.properties | 6 + 17 files changed, 680 insertions(+), 18 deletions(-) create mode 100644 jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/insights/ColumnInfoDetails.kt create mode 100644 jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/insights/QueryActor.kt create mode 100644 jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/insights/QueryEditorSavedState.kt create mode 100644 jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/insights/QueryResultList.form create mode 100644 jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/insights/QueryResultList.kt create mode 100644 jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/insights/QueryResultsTable.kt create mode 100644 jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/insights/QueryResultsWindow.kt create mode 100644 jetbrains-core/tst/software/aws/toolkits/jetbrains/services/cloudwatch/logs/insights/GetFieldsFromEnteredQueryTest.kt create mode 100644 jetbrains-core/tst/software/aws/toolkits/jetbrains/services/cloudwatch/logs/insights/QueryResultsReturnedTest.kt diff --git a/jetbrains-core/resources/META-INF/plugin.xml b/jetbrains-core/resources/META-INF/plugin.xml index 6299a6ff49..0c1a7edc74 100644 --- a/jetbrains-core/resources/META-INF/plugin.xml +++ b/jetbrains-core/resources/META-INF/plugin.xml @@ -188,6 +188,7 @@ + diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/insights/ColumnInfoDetails.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/insights/ColumnInfoDetails.kt new file mode 100644 index 0000000000..0449cd83da --- /dev/null +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/insights/ColumnInfoDetails.kt @@ -0,0 +1,35 @@ +// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package software.aws.toolkits.jetbrains.services.cloudwatch.logs.insights + +import com.intellij.ui.SimpleColoredComponent +import com.intellij.util.ui.ColumnInfo +import software.aws.toolkits.jetbrains.utils.ui.setSelectionHighlighting +import java.awt.Component +import javax.swing.JTable +import javax.swing.table.TableCellRenderer + +class ColumnInfoDetails(private val fieldName: String) : ColumnInfo, String>(fieldName) { + private val renderer = FieldColumnRenderer() + override fun valueOf(item: Map?): String? { + if (item != null) { + return item[fieldName] + } + return null + } + override fun isCellEditable(item: Map?): Boolean = false + override fun getRenderer(item: Map?): TableCellRenderer? = renderer +} + +class FieldColumnRenderer : TableCellRenderer { + override fun getTableCellRendererComponent(table: JTable?, value: Any?, isSelected: Boolean, hasFocus: Boolean, row: Int, column: Int): Component { + val component = SimpleColoredComponent() + component.append((value as? String)?.trim() ?: "") + if (table == null) { + return component + } + component.setSelectionHighlighting(table, isSelected) + return component + } +} diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/insights/QueryActor.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/insights/QueryActor.kt new file mode 100644 index 0000000000..1f5b500daa --- /dev/null +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/insights/QueryActor.kt @@ -0,0 +1,176 @@ +// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package software.aws.toolkits.jetbrains.services.cloudwatch.logs.insights + +import com.intellij.openapi.Disposable +import com.intellij.openapi.project.Project +import com.intellij.ui.table.TableView +import kotlinx.coroutines.CoroutineExceptionHandler +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.channels.Channel +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext +import software.amazon.awssdk.services.cloudwatchlogs.CloudWatchLogsClient +import software.amazon.awssdk.services.cloudwatchlogs.model.GetQueryResultsRequest +import software.amazon.awssdk.services.cloudwatchlogs.model.QueryStatus +import software.amazon.awssdk.services.cloudwatchlogs.model.ResourceNotFoundException +import software.amazon.awssdk.services.cloudwatchlogs.model.ResultField +import software.aws.toolkits.core.utils.error +import software.aws.toolkits.core.utils.getLogger +import software.aws.toolkits.jetbrains.utils.ApplicationThreadPoolScope +import software.aws.toolkits.jetbrains.utils.getCoroutineUiContext +import software.aws.toolkits.jetbrains.utils.notifyError +import software.aws.toolkits.jetbrains.utils.notifyInfo +import software.aws.toolkits.resources.message + +sealed class QueryActor( + private val project: Project, + protected val client: CloudWatchLogsClient, + private val table: TableView +) : CoroutineScope by ApplicationThreadPoolScope("InsightsResultTable"), Disposable { + val channel = Channel() + protected var moreResultsAvailable: Boolean = false + protected abstract val emptyText: String + protected abstract val tableErrorMessage: String + protected abstract val notFoundText: String + + private val edtContext = getCoroutineUiContext(disposable = this@QueryActor) + private val exceptionHandler = CoroutineExceptionHandler { _, e -> + LOG.error(e) { "Exception thrown in Query Results not handled:" } + notifyError(title = message("general.unknown_error"), project = project) + channel.close() + } + + sealed class MessageLoadQueryResults { + object LoadInitialQueryResults : MessageLoadQueryResults() + object LoadNextQueryBatch : MessageLoadQueryResults() + } + + init { + launch(exceptionHandler) { + for (message in channel) { + handleMessages(message) + } + } + } + + private suspend fun handleMessages(message: MessageLoadQueryResults) { + when (message) { + is MessageLoadQueryResults.LoadNextQueryBatch -> { + if (moreResultsAvailable) { + withContext(edtContext) { table.setPaintBusy(true) } + val items = loadNext() + withContext(edtContext) { + table.listTableModel.addRows(items) + table.setPaintBusy(false) + } + } else { + notifyInfo(message("cloudwatch.logs.query_result_completion_status"), message("cloudwatch.logs.query_result_completion_successful")) + } + } + is MessageLoadQueryResults.LoadInitialQueryResults -> { + loadInitialQueryResults() + val rect = table.getCellRect(0, 0, true) + withContext(edtContext) { + table.scrollRectToVisible(rect) + } + } + } + } + + protected suspend fun loadAndPopulateResultsTable(loadBlock: suspend() -> List) { + try { + tableLoading() + val items = loadBlock() + table.listTableModel.items = items + table.emptyText.text = emptyText + } catch (e: ResourceNotFoundException) { + withContext(edtContext) { + table.emptyText.text = notFoundText + } + } catch (e: Exception) { + LOG.error(e) { tableErrorMessage } + withContext(edtContext) { + table.emptyText.text = tableErrorMessage + notifyError(title = tableErrorMessage, project = project) + } + } finally { + tableDoneLoading() + } + } + + protected open suspend fun loadInitialQueryResults() { + throw IllegalStateException("Table does not support loadInitialQueryResults") + } + + protected abstract suspend fun loadNext(): List + + private suspend fun tableLoading() = withContext(edtContext) { + table.setPaintBusy(true) + table.emptyText.text = message("loading_resource.loading") + } + + private suspend fun tableDoneLoading() = withContext(edtContext) { + table.tableViewModel.fireTableDataChanged() + table.setPaintBusy(false) + } + + override fun dispose() { + channel.close() + } + companion object { + private val LOG = getLogger>() + } +} + +class QueryResultsActor( + project: Project, + client: CloudWatchLogsClient, + table: TableView>, + private val queryId: String +) : QueryActor>(project, client, table) { + override val emptyText = message("cloudwatch.logs.no_results_found") + override val tableErrorMessage = message("cloudwatch.logs.query_results_table_error") + override val notFoundText = message("cloudwatch.logs.no_results_found") + + override suspend fun loadInitialQueryResults() { + var request = GetQueryResultsRequest.builder().queryId(queryId).build() + var response = client.getQueryResults(request) + while (response.results().size == 0) { + request = GetQueryResultsRequest.builder().queryId(queryId).build() + response = client.getQueryResults(request) + } + val queryResults = response.results().filterNotNull() + val listOfResults = queryResults.map { + result -> result.map { it.field().toString() to it.value().toString() }.toMap() } + listOfResults.iterator().forEach { it["@ptr"]?.let { it1 -> queryResultsIdentifierList.add(it1) } } + moreResultsAvailable = response.status() != QueryStatus.COMPLETE + + loadAndPopulateResultsTable { listOfResults } + } + + override suspend fun loadNext(): List> { + val request = GetQueryResultsRequest.builder().queryId(queryId).build() + val response = client.getQueryResults(request) + moreResultsAvailable = response.status() != QueryStatus.COMPLETE + return checkIfNewResult(response.results().filterNotNull()) + } + + fun checkIfNewResult(queryResultList: List>): List> { + // Since the results are cumulative, if the order of results change, this function ensures that the same results are not displayed repeatedly + val listOfResults = arrayListOf>() + for (result in queryResultList) { + var fieldToValueMap = result.map { it.field() to it.value() }.toMap() + // @ptr is a unique identifier for each resultant log event which is used here to ensure results are not repeatedly displayed + if (fieldToValueMap["@ptr"] !in queryResultsIdentifierList) { + fieldToValueMap["@ptr"]?.let { queryResultsIdentifierList.add(it) } + listOfResults.add(fieldToValueMap) + } + } + return listOfResults + } + companion object { + val queryResultsIdentifierList = arrayListOf() + } +} diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/insights/QueryEditor.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/insights/QueryEditor.kt index a4de75e35e..5230667a7b 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/insights/QueryEditor.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/insights/QueryEditor.kt @@ -43,7 +43,7 @@ class QueryEditor internal constructor(private val project: Project) { tablePanel = SimpleToolWindowPanel(false, true) showLogGroupTable = AddRemoveLogGroupTable(project) initArLogGroupTable() - tablePanel.setContent(showLogGroupTable.component) + // tablePanel.setContent(showLogGroupTable.component) val timeUnits = arrayOf( message("cloudwatch.logs.time_minutes"), message("cloudwatch.logs.time_hours"), @@ -55,9 +55,9 @@ class QueryEditor internal constructor(private val project: Project) { init { startDate.isEnabled = false endDate.isEnabled = false - relativeTimeNumber.isEnabled = false - relativeTimeUnit.isEnabled = false - querySearchTerm.isEnabled = false + relativeTimeNumber.isEnabled = true + relativeTimeUnit.isEnabled = true + querySearchTerm.isEnabled = true queryBox.isEnabled = false saveQueryButton.isEnabled = false queryBox.text = default_query diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/insights/QueryEditorDialog.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/insights/QueryEditorDialog.kt index 1747d3927b..9390cda502 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/insights/QueryEditorDialog.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/insights/QueryEditorDialog.kt @@ -25,10 +25,11 @@ val relativeTimeUnit = mapOf( class QueryEditorDialog( private val project: Project, private val lGroupName: String, - private val client: CloudWatchLogsClient + private val client: CloudWatchLogsClient, + private val displayInitialParameters: Boolean ) : DialogWrapper(project) { - constructor(project: Project, logGroupName: String) : - this(project = project, lGroupName = logGroupName, client = project.awsClient()) + constructor(project: Project, logGroupName: String, initialParametersDisplayed: Boolean) : + this(project = project, lGroupName = logGroupName, client = project.awsClient(), displayInitialParameters = initialParametersDisplayed) private val view = QueryEditor(project) private val queryingLogGroupApiCall = QueryingLogGroups(project) @@ -37,7 +38,14 @@ class QueryEditorDialog( init { super.init() + title = message("cloudwatch.logs.query_editor_title") + if (displayInitialParameters) { + setView(QueryEditorSavedState.currentQueryEditorState, QueryEditorSavedState.enabledDisabledOptionsState) + } else { + setView(QueryEditorSavedState().getQueryEditorState(), QueryEditorSavedState().getEnabledDisabledOptionsState()) + } + view.absoluteTimeRadioButton.addActionListener { view.startDate.isEnabled = true view.endDate.isEnabled = true @@ -77,6 +85,26 @@ class QueryEditorDialog( // Do nothing, close logic is handled separately } + private fun setView(queryDetails: QueryDetails, enabledComponentsState: EnabledComponentsState) { + view.startDate.isEnabled = enabledComponentsState.startDateEnabled + view.endDate.isEnabled = enabledComponentsState.endDateEnabled + view.relativeTimeNumber.isEnabled = enabledComponentsState.relativeTimeNumberEnabled + view.relativeTimeUnit.isEnabled = enabledComponentsState.relativeTimeUnitEnabled + view.querySearchTerm.isEnabled = enabledComponentsState.querySearchTermEnabled + view.queryBox.isEnabled = enabledComponentsState.queryBoxEnabled + view.logGroupLabel.text = "Log Group : $lGroupName" + view.absoluteTimeRadioButton.isSelected = queryDetails.absoluteTimeSelected + view.startDate.date = queryDetails.startDateAbsolute + view.endDate.date = queryDetails.endDateAbsolute + view.relativeTimeRadioButton.isSelected = queryDetails.relativeTimeSelected + view.relativeTimeUnit.selectedItem = queryDetails.relativeTimeUnit + view.relativeTimeNumber.text = queryDetails.relativeTimeNumber + view.searchTerm.isSelected = queryDetails.searchTermSelected + view.querySearchTerm.text = queryDetails.searchTerm + view.queryLogGroupsRadioButton.isSelected = queryDetails.enterQuery + view.queryBox.text = queryDetails.query + } + private fun getCurrentTime() = Calendar.getInstance().toInstant() private fun getRelativeTime(unitOfTime: ChronoUnit?, relTimeNumber: Long): StartEndDate = StartEndDate(getCurrentTime().minus(relTimeNumber, unitOfTime), @@ -107,10 +135,20 @@ class QueryEditorDialog( funDetails.query } else { getFilterQuery(funDetails.searchTerm) } + QueryEditorSavedState().setQueryEditorState(getFunctionDetails(), getEnabledDisabledComponentsState()) close(OK_EXIT_CODE) queryingLogGroupApiCall.executeStartQuery(queryStartEndDate, funDetails.logGroupName, query, client) } + private fun getEnabledDisabledComponentsState(): EnabledComponentsState = EnabledComponentsState( + startDateEnabled = view.startDate.isEnabled, + endDateEnabled = view.endDate.isEnabled, + relativeTimeNumberEnabled = view.relativeTimeNumber.isEnabled, + relativeTimeUnitEnabled = view.relativeTimeUnit.isEnabled, + querySearchTermEnabled = view.querySearchTerm.isEnabled, + queryBoxEnabled = view.queryBox.isEnabled + ) + private fun getFunctionDetails(): QueryDetails = QueryDetails( logGroupName = logGroupNames, absoluteTimeSelected = view.absoluteTimeRadioButton.isSelected, diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/insights/QueryEditorSavedState.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/insights/QueryEditorSavedState.kt new file mode 100644 index 0000000000..aed82356b8 --- /dev/null +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/insights/QueryEditorSavedState.kt @@ -0,0 +1,44 @@ +// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package software.aws.toolkits.jetbrains.services.cloudwatch.logs.insights + +import java.util.Calendar + +class QueryEditorSavedState { + fun setQueryEditorState(queryDetails: QueryDetails, enabledComponentsDisabledComponents: EnabledComponentsState) { + currentQueryEditorState = queryDetails + enabledDisabledOptionsState = enabledComponentsDisabledComponents + } + + fun getQueryEditorState(): QueryDetails = currentQueryEditorState + + fun getEnabledDisabledOptionsState(): EnabledComponentsState = enabledDisabledOptionsState + + companion object { + var currentQueryEditorState = QueryDetails( + listOf("Default log"), + false, + Calendar.getInstance().time, + Calendar.getInstance().time, + true, + "Minutes", + "10", + true, + "Error", + false, + "fields @timestamp, @message\n" + + "| sort @timestamp desc\n" + + "| limit 20" + + ) + var enabledDisabledOptionsState = EnabledComponentsState( + startDateEnabled = false, + endDateEnabled = false, + relativeTimeNumberEnabled = true, + relativeTimeUnitEnabled = true, + querySearchTermEnabled = true, + queryBoxEnabled = false + ) + } +} diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/insights/QueryResultList.form b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/insights/QueryResultList.form new file mode 100644 index 0000000000..5df97b4fdd --- /dev/null +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/insights/QueryResultList.form @@ -0,0 +1,50 @@ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/insights/QueryResultList.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/insights/QueryResultList.kt new file mode 100644 index 0000000000..88cc1d4b19 --- /dev/null +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/insights/QueryResultList.kt @@ -0,0 +1,50 @@ +// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +package software.aws.toolkits.jetbrains.services.cloudwatch.logs.insights + +import com.intellij.openapi.Disposable +import com.intellij.openapi.project.Project +import com.intellij.openapi.ui.SimpleToolWindowPanel +import com.intellij.openapi.util.Disposer +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.launch +import software.amazon.awssdk.services.cloudwatchlogs.CloudWatchLogsClient +import software.aws.toolkits.jetbrains.core.awsClient +import software.aws.toolkits.jetbrains.utils.ApplicationThreadPoolScope +import software.aws.toolkits.resources.message +import javax.swing.JButton +import javax.swing.JLabel +import javax.swing.JPanel + +class QueryResultList( + private val project: Project, + private val fieldList: List, + private val queryId: String, + private val selectedLogGroup: String +) : CoroutineScope by ApplicationThreadPoolScope("CloudWatchLogsGroup"), Disposable { + lateinit var resultsPanel: JPanel + private lateinit var tablePanel: SimpleToolWindowPanel + private lateinit var openQueryEditor: JButton + private lateinit var resultsTitle: JLabel + val client: CloudWatchLogsClient = project.awsClient() + private val resultsTable: QueryResultsTable = QueryResultsTable(project, client, fieldList, queryId) + private fun createUIComponents() { + // TODO: place custom component creation code here + tablePanel = SimpleToolWindowPanel(false, true) + } + init { + openQueryEditor.text = message("cloudwatch.logs.query") + resultsTitle.text = message("cloudwatch.logs.query_result") + Disposer.register(this, resultsTable) + tablePanel.setContent(resultsTable.component) + loadInitialResultsTable() + openQueryEditor.addActionListener { + QueryEditorDialog(project, selectedLogGroup, initialParametersDisplayed = false).show() + } + } + private fun loadInitialResultsTable() { + launch { resultsTable.channel.send(QueryActor.MessageLoadQueryResults.LoadInitialQueryResults) } + } + override fun dispose() { + } +} diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/insights/QueryResultsTable.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/insights/QueryResultsTable.kt new file mode 100644 index 0000000000..df8bf39c37 --- /dev/null +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/insights/QueryResultsTable.kt @@ -0,0 +1,59 @@ +// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package software.aws.toolkits.jetbrains.services.cloudwatch.logs.insights + +import com.intellij.openapi.Disposable +import com.intellij.openapi.project.Project +import com.intellij.ui.ScrollPaneFactory +import com.intellij.ui.table.TableView +import com.intellij.util.ui.ListTableModel +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.channels.Channel +import kotlinx.coroutines.launch +import software.amazon.awssdk.services.cloudwatchlogs.CloudWatchLogsClient +import software.aws.toolkits.jetbrains.utils.ApplicationThreadPoolScope +import software.aws.toolkits.jetbrains.utils.ui.bottomReached +import software.aws.toolkits.resources.message +import javax.swing.JComponent + +class QueryResultsTable( + private val project: Project, + private val client: CloudWatchLogsClient, + private val fieldList: List, + private val queryId: String +) : CoroutineScope by ApplicationThreadPoolScope("QueryResultsTable"), Disposable { + val component: JComponent + val channel: Channel + private val resultsTable: TableView> + private val queryActor: QueryActor> + + init { + val columnInfoList: ArrayList = arrayListOf() + for (field in fieldList) { + columnInfoList.add(ColumnInfoDetails(field)) + } + val columnInfoArray = columnInfoList.toTypedArray() + val tableModel = ListTableModel( + columnInfoArray, listOf>() + ) + resultsTable = TableView(tableModel).apply { + setPaintBusy(true) + autoscrolls = true + emptyText.text = message("loading_resource.loading") + tableHeader.reorderingAllowed = false + tableHeader.resizingAllowed = true + } + + queryActor = QueryResultsActor(project, client, resultsTable, queryId) + channel = queryActor.channel + component = ScrollPaneFactory.createScrollPane(resultsTable).also { + it.bottomReached { + if (resultsTable.rowCount != 0) { + launch { queryActor.channel.send(QueryActor.MessageLoadQueryResults.LoadNextQueryBatch) } + } + } + } + } + override fun dispose() {} +} diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/insights/QueryResultsWindow.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/insights/QueryResultsWindow.kt new file mode 100644 index 0000000000..fb0e2c3545 --- /dev/null +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/insights/QueryResultsWindow.kt @@ -0,0 +1,41 @@ +// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package software.aws.toolkits.jetbrains.services.cloudwatch.logs.insights + +import com.intellij.openapi.components.ServiceManager +import com.intellij.openapi.project.Project +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext +import software.aws.toolkits.jetbrains.core.toolwindow.ToolkitToolWindowManager +import software.aws.toolkits.jetbrains.core.toolwindow.ToolkitToolWindowType +import software.aws.toolkits.jetbrains.utils.ApplicationThreadPoolScope +import software.aws.toolkits.jetbrains.utils.getCoroutineUiContext +import software.aws.toolkits.resources.message + +class QueryResultsWindow(private val project: Project) : CoroutineScope by ApplicationThreadPoolScope("openResultsWindow") { + private val toolWindow = ToolkitToolWindowManager.getInstance(project, QueryResultsWindow.INSIGHTS_RESULTS_TOOL_WINDOW) + private val edtContext = getCoroutineUiContext() + fun showResults(queryId: String, fieldList: List, selectedlogGroup: String) = launch { + val existingWindow = toolWindow.find(queryId) + if (existingWindow != null) { + withContext(edtContext) { + existingWindow.show() + } + return@launch + } + val queryResult = QueryResultList(project, fieldList, queryId, selectedlogGroup) + withContext(edtContext) { + toolWindow.addTab(queryId, queryResult.resultsPanel, activate = true, id = queryId, disposable = queryResult) + } + } + + companion object { + internal val INSIGHTS_RESULTS_TOOL_WINDOW = ToolkitToolWindowType( + "AWS.InsightsResultsTable", + message("cloudwatch.logs.results_window_title") + ) + fun getInstance(project: Project) = ServiceManager.getService(project, QueryResultsWindow::class.java) + } +} diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/insights/QueryingLogGroups.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/insights/QueryingLogGroups.kt index 4f67c45b00..a66c5ee417 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/insights/QueryingLogGroups.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/insights/QueryingLogGroups.kt @@ -9,18 +9,45 @@ import kotlinx.coroutines.launch import software.amazon.awssdk.services.cloudwatchlogs.CloudWatchLogsClient import software.amazon.awssdk.services.cloudwatchlogs.model.StartQueryRequest import software.aws.toolkits.jetbrains.utils.ApplicationThreadPoolScope +import software.aws.toolkits.jetbrains.utils.notifyError +import software.aws.toolkits.resources.message +const val fields = "fields" class QueryingLogGroups(private val project: Project) : CoroutineScope by ApplicationThreadPoolScope("ExecutingQuery") { + fun executeStartQuery(queryStartEndDate: StartEndDate, logGroupNames: List, query: String, client: CloudWatchLogsClient) = launch { // TODO: Multiple log groups queried (currently only a single log group can be selected and queried) - val request = StartQueryRequest.builder() - .endTime(queryStartEndDate.endDate.epochSecond) - .logGroupName(logGroupNames[0]) - .queryString(query) - .startTime(queryStartEndDate.startDate.epochSecond) - .build() - val response = client.startQuery(request) - val queryId = response.queryId() - // TODO: Get the results of the query with qid + try { + val request = StartQueryRequest.builder() + .endTime(queryStartEndDate.endDate.epochSecond) + .logGroupName(logGroupNames[0]) + .queryString(query) + .startTime(queryStartEndDate.startDate.epochSecond) + .build() + val response = client.startQuery(request) + val queryId: String = response.queryId() + val fieldList = getFields(query) + QueryResultsWindow.getInstance(project).showResults(queryId, fieldList, logGroupNames[0]) + } catch (e: Exception) { + notifyError(message("cloudwatch.logs.query_result_completion_status"), e.toString()) + throw e + } + } + + fun getFields(query: String): List { + var fieldList = mutableListOf>() + query.replace("\\|", "") + val queries = query.split("|") + for (item in queries) { + val splitQuery = item.trim() + if (splitQuery.startsWith(fields, ignoreCase = false)) { + var fields = splitQuery.substring(fields.length + 1) + fieldList.add(fields.split(",").map { it.trim() }) + } + } + if (fieldList.isEmpty()) { + return listOf("@message", "@timestamp") + } + return fieldList.flatten() } } diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/insights/SelectedLogGroup.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/insights/SelectedLogGroup.kt index 2dd12aa543..658384be22 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/insights/SelectedLogGroup.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/insights/SelectedLogGroup.kt @@ -29,3 +29,12 @@ data class StartEndDate( val startDate: Instant, val endDate: Instant ) + +data class EnabledComponentsState( + val startDateEnabled: Boolean, + val endDateEnabled: Boolean, + val relativeTimeNumberEnabled: Boolean, + val relativeTimeUnitEnabled: Boolean, + val querySearchTermEnabled: Boolean, + val queryBoxEnabled: Boolean +) diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/insights/actions/QueryGroupAction.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/insights/actions/QueryGroupAction.kt index 68fa51aa12..e4ddc1ba67 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/insights/actions/QueryGroupAction.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/insights/actions/QueryGroupAction.kt @@ -11,5 +11,8 @@ import software.aws.toolkits.jetbrains.services.cloudwatch.logs.insights.QueryEd import software.aws.toolkits.resources.message class QueryGroupAction : SingleResourceNodeAction(message("cloudwatch.logs.query")), DumbAware { - override fun actionPerformed(selected: CloudWatchLogsNode, e: AnActionEvent) = QueryEditorDialog(selected.nodeProject, selected.logGroupName).show() + override fun actionPerformed(selected: CloudWatchLogsNode, e: AnActionEvent) = QueryEditorDialog( + selected.nodeProject, + selected.logGroupName, + initialParametersDisplayed = true).show() } diff --git a/jetbrains-core/tst/software/aws/toolkits/jetbrains/services/cloudwatch/logs/insights/GetFieldsFromEnteredQueryTest.kt b/jetbrains-core/tst/software/aws/toolkits/jetbrains/services/cloudwatch/logs/insights/GetFieldsFromEnteredQueryTest.kt new file mode 100644 index 0000000000..3b789855b1 --- /dev/null +++ b/jetbrains-core/tst/software/aws/toolkits/jetbrains/services/cloudwatch/logs/insights/GetFieldsFromEnteredQueryTest.kt @@ -0,0 +1,30 @@ +// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package software.aws.toolkits.jetbrains.services.cloudwatch.logs.insights + +import org.assertj.core.api.Assertions.assertThat +import org.junit.Rule +import org.junit.Test +import software.aws.toolkits.jetbrains.utils.rules.JavaCodeInsightTestFixtureRule + +class GetFieldsFromEnteredQueryTest { + @JvmField + @Rule + val projectRule = JavaCodeInsightTestFixtureRule() + + @Test + fun `Fields extracted correctly from query string`() { + val query = QueryingLogGroups(projectRule.project) + val fieldsAsSecondPartOfQuery = "filter @message like /Error/ | fields @message" + val noFieldsQuery = "filter @message like /Error/" + val onlyFieldsQuery = "fields @logStream, @timestamp" + val twoFieldsQuery = "fields @timestamp, @logStream | limit 10 | fields @message" + val fieldsInFilterQuery = "filter @message like /fields/ | fields @logStream" + assertThat(query.getFields(fieldsAsSecondPartOfQuery)).isEqualTo(listOf("@message")) + assertThat(query.getFields(noFieldsQuery)).isEqualTo(listOf("@message", "@timestamp")) + assertThat(query.getFields(onlyFieldsQuery)).isEqualTo(listOf("@logStream", "@timestamp")) + assertThat(query.getFields(twoFieldsQuery)).isEqualTo(listOf("@timestamp", "@logStream", "@message")) + assertThat(query.getFields(fieldsInFilterQuery)).isEqualTo(listOf("@logStream")) + } +} diff --git a/jetbrains-core/tst/software/aws/toolkits/jetbrains/services/cloudwatch/logs/insights/QueryResultsReturnedTest.kt b/jetbrains-core/tst/software/aws/toolkits/jetbrains/services/cloudwatch/logs/insights/QueryResultsReturnedTest.kt new file mode 100644 index 0000000000..8ba1db1f3c --- /dev/null +++ b/jetbrains-core/tst/software/aws/toolkits/jetbrains/services/cloudwatch/logs/insights/QueryResultsReturnedTest.kt @@ -0,0 +1,93 @@ +// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package software.aws.toolkits.jetbrains.services.cloudwatch.logs.insights + +import com.intellij.ui.table.TableView +import com.intellij.util.ui.ListTableModel +import com.nhaarman.mockitokotlin2.whenever +import kotlinx.coroutines.runBlocking +import org.assertj.core.api.Assertions.assertThat +import org.junit.Test +import org.mockito.Mockito +import software.amazon.awssdk.services.cloudwatchlogs.CloudWatchLogsClient +import software.amazon.awssdk.services.cloudwatchlogs.model.GetQueryResultsRequest +import software.amazon.awssdk.services.cloudwatchlogs.model.GetQueryResultsResponse +import software.amazon.awssdk.services.cloudwatchlogs.model.ResultField +import software.aws.toolkits.jetbrains.utils.BaseCoroutineTest +import software.aws.toolkits.jetbrains.utils.waitForModelToBeAtLeast + +class QueryResultsReturnedTest : BaseCoroutineTest() { + + private lateinit var client: CloudWatchLogsClient + private lateinit var tableModel: ListTableModel> + private lateinit var table: TableView> + private lateinit var queryactor: QueryActor> + + @Test + fun `Check if already displayed identifier list is getting updated`() { + getTableModelDetails() + val sampleResult1 = ResultField.builder() + .field("@message") + .value("First Sample Message") + .field("@ptr") + .value("1234") + .build() + val queryResultsActor = QueryResultsActor(projectRule.project, client, table, "abcdef") + queryResultsActor.checkIfNewResult(listOf(mutableListOf(sampleResult1))) + assertThat(QueryResultsActor.queryResultsIdentifierList).contains("1234") + } + + @Test + fun `Initial log events are loaded in the table model`() { + getTableModelDetails() + val sampleResult = ResultField.builder().field("@message").value("Sample Message").build() + val sampleResultList = listOf(sampleResult) + whenever(client.getQueryResults(Mockito.any())) + .thenReturn( + GetQueryResultsResponse.builder().results(sampleResultList).build() + ) + runBlocking { + queryactor.channel.send(QueryActor.MessageLoadQueryResults.LoadInitialQueryResults) + tableModel.waitForModelToBeAtLeast(1) + } + assertThat(tableModel.items.size).isOne() + assertThat(tableModel.items.first().keys).isEqualTo(setOf("@message")) + } + + @Test + fun `Loading more log events in table model after the initial results`() { + getTableModelDetails() + val sampleResult1 = ResultField.builder() + .field("@ptr") + .value("1234") + .build() + val firstSampleResultList = listOf(sampleResult1) + val sampleResult2 = ResultField.builder() + .field("@ptr").value("5678") + .build() + val secondSampleResultList = listOf(sampleResult2) + whenever(client.getQueryResults(Mockito.any())) + .thenReturn( + GetQueryResultsResponse.builder().results(firstSampleResultList).build() + ) + .thenReturn( + GetQueryResultsResponse.builder().results(firstSampleResultList, secondSampleResultList).build() + ) + runBlocking { + queryactor.channel.send(QueryActor.MessageLoadQueryResults.LoadInitialQueryResults) + queryactor.channel.send(QueryActor.MessageLoadQueryResults.LoadNextQueryBatch) + tableModel.waitForModelToBeAtLeast(2) + } + assertThat(tableModel.items.size).isEqualTo(2) + assertThat(tableModel.items[0].keys).isEqualTo(setOf("@ptr")) + assertThat(tableModel.items[0].keys).isEqualTo(setOf("@ptr")) + } + + private fun getTableModelDetails() { + client = mockClientManagerRule.create() + tableModel = ListTableModel>() + table = TableView(tableModel) + queryactor = QueryResultsActor(projectRule.project, client, table, "1234") + } +} diff --git a/jetbrains-core/tst/software/aws/toolkits/jetbrains/services/cloudwatch/logs/insights/QueryingLogGroupsTest.kt b/jetbrains-core/tst/software/aws/toolkits/jetbrains/services/cloudwatch/logs/insights/QueryingLogGroupsTest.kt index ee7502f21c..7106ca6ee4 100644 --- a/jetbrains-core/tst/software/aws/toolkits/jetbrains/services/cloudwatch/logs/insights/QueryingLogGroupsTest.kt +++ b/jetbrains-core/tst/software/aws/toolkits/jetbrains/services/cloudwatch/logs/insights/QueryingLogGroupsTest.kt @@ -132,6 +132,6 @@ class QueryingLogGroupsTest { val project = projectRule.project view = QueryEditor(project) client = mockClientManagerRule.create() - validator = QueryEditorDialog(project, "log1", client) + validator = QueryEditorDialog(project, "log1", client, displayInitialParameters = true) } } diff --git a/resources/resources/software/aws/toolkits/resources/localized_messages.properties b/resources/resources/software/aws/toolkits/resources/localized_messages.properties index 768ce2243a..087f04d243 100644 --- a/resources/resources/software/aws/toolkits/resources/localized_messages.properties +++ b/resources/resources/software/aws/toolkits/resources/localized_messages.properties @@ -216,6 +216,7 @@ cloudwatch.logs.no_log_streams=No Log Stream found cloudwatch.logs.no_query_entered=Query must be specified cloudwatch.logs.no_query_selected=Either a query or search term must be entered cloudwatch.logs.no_relative_time_number=Number must be specified +cloudwatch.logs.no_results_found=No results found cloudwatch.logs.no_start_date=Start Date must be specified cloudwatch.logs.no_term_entered=Search Term must be specified cloudwatch.logs.open=View Log Streams @@ -227,7 +228,12 @@ cloudwatch.logs.query.form.ok_Button=Execute cloudwatch.logs.query_editor_title=Query Log Groups cloudwatch.logs.query_name=Query name is missing cloudwatch.logs.query_not_saved=Query could not be saved. Try saving with a different name. +cloudwatch.logs.query_result=Query Results +cloudwatch.logs.query_result_completion_status=Result +cloudwatch.logs.query_result_completion_successful=Query Execution Completed +cloudwatch.logs.query_results_table_error=Failed to load Query Results cloudwatch.logs.query_saved_successfully=Query has been saved to your account! +cloudwatch.logs.results_window_title=CloudWatch Logs Insights cloudwatch.logs.save_action=Save to a File cloudwatch.logs.save_query=Save Query cloudwatch.logs.save_query_dialog_name=Enter Query Name From ee5c4ca35f829f9d1b56c1b0e9de77cbb34fd145 Mon Sep 17 00:00:00 2001 From: Hunter Werlla Date: Tue, 18 Aug 2020 08:39:23 -0700 Subject: [PATCH 0021/1546] Add KTlint rule to ban Kotlin test asserts (#1969) --- .../core/credentials/CredentialManagerTest.kt | 5 +-- .../jetbrains/services/RoleValidationTest.kt | 15 ++++--- .../yaml/YamlCloudFormationTemplateTest.kt | 42 ++++++++++++------- .../RemoteLambdaRunConfigurationTest.kt | 16 +++---- .../services/lambda/sam/SamCommonTest.kt | 2 +- .../lambda/upload/CreateLambdaFunctionTest.kt | 16 ++++--- ...esourceOperationAgainstCodePipelineTest.kt | 11 +++-- .../lambda/nodejs/NodeJsHelperTest.kt | 8 ++-- .../ktlint/rules/BannedImportsRule.kt | 6 +++ .../ktlint/rules/BannedImportsRuleTest.kt | 27 ++++++++++++ 10 files changed, 93 insertions(+), 55 deletions(-) diff --git a/jetbrains-core/tst/software/aws/toolkits/jetbrains/core/credentials/CredentialManagerTest.kt b/jetbrains-core/tst/software/aws/toolkits/jetbrains/core/credentials/CredentialManagerTest.kt index d4357a4000..e3af655952 100644 --- a/jetbrains-core/tst/software/aws/toolkits/jetbrains/core/credentials/CredentialManagerTest.kt +++ b/jetbrains-core/tst/software/aws/toolkits/jetbrains/core/credentials/CredentialManagerTest.kt @@ -15,15 +15,14 @@ import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider import software.amazon.awssdk.http.SdkHttpClient import software.aws.toolkits.core.credentials.CredentialIdentifier +import software.aws.toolkits.core.credentials.CredentialIdentifierBase import software.aws.toolkits.core.credentials.CredentialProviderFactory import software.aws.toolkits.core.credentials.CredentialProviderNotFoundException import software.aws.toolkits.core.credentials.CredentialsChangeEvent import software.aws.toolkits.core.credentials.CredentialsChangeListener -import software.aws.toolkits.core.credentials.CredentialIdentifierBase import software.aws.toolkits.core.region.AwsRegion import software.aws.toolkits.jetbrains.core.region.MockRegionProvider import kotlin.test.assertNotNull -import kotlin.test.assertNull class CredentialManagerTest { @Rule @@ -191,7 +190,7 @@ class CredentialManagerTest { ).resolveCredentials() }.isInstanceOf(CredentialProviderNotFoundException::class.java) - assertNull(credentialManager.getCredentialIdentifierById("testFoo1")) + assertThat(credentialManager.getCredentialIdentifierById("testFoo1")).isNull() } private fun addFactories(vararg factories: CredentialProviderFactory) { diff --git a/jetbrains-core/tst/software/aws/toolkits/jetbrains/services/RoleValidationTest.kt b/jetbrains-core/tst/software/aws/toolkits/jetbrains/services/RoleValidationTest.kt index 00d929da30..2d346633f7 100644 --- a/jetbrains-core/tst/software/aws/toolkits/jetbrains/services/RoleValidationTest.kt +++ b/jetbrains-core/tst/software/aws/toolkits/jetbrains/services/RoleValidationTest.kt @@ -3,9 +3,8 @@ package software.aws.toolkits.jetbrains.services +import org.assertj.core.api.Assertions.assertThat import org.junit.Test -import kotlin.test.assertFalse -import kotlin.test.assertTrue class RoleValidationTest { @Test @@ -26,7 +25,7 @@ class RoleValidationTest { ] } """.trimIndent() - assertTrue { RoleValidation.isRolePolicyValidForCloudDebug(rolePolicy) } + assertThat(RoleValidation.isRolePolicyValidForCloudDebug(rolePolicy)).isTrue() } @Test @@ -50,7 +49,7 @@ class RoleValidationTest { ] } """.trimIndent() - assertTrue { RoleValidation.isRolePolicyValidForCloudDebug(rolePolicy) } + assertThat(RoleValidation.isRolePolicyValidForCloudDebug(rolePolicy)).isTrue() } @Test @@ -71,7 +70,7 @@ class RoleValidationTest { ] } """.trimIndent() - assertFalse { RoleValidation.isRolePolicyValidForCloudDebug(rolePolicy) } + assertThat(RoleValidation.isRolePolicyValidForCloudDebug(rolePolicy)).isFalse() } @Test @@ -92,7 +91,7 @@ class RoleValidationTest { ] } """.trimIndent() - assertFalse { RoleValidation.isRolePolicyValidForCloudDebug(rolePolicy) } + assertThat(RoleValidation.isRolePolicyValidForCloudDebug(rolePolicy)).isFalse() } @Test @@ -113,7 +112,7 @@ class RoleValidationTest { ] } """.trimIndent() - assertFalse { RoleValidation.isRolePolicyValidForCloudDebug(rolePolicy) } + assertThat(RoleValidation.isRolePolicyValidForCloudDebug(rolePolicy)).isFalse() } @Test @@ -137,6 +136,6 @@ class RoleValidationTest { ] } """.trimIndent() - assertFalse { RoleValidation.isRolePolicyValidForCloudDebug(rolePolicy) } + assertThat(RoleValidation.isRolePolicyValidForCloudDebug(rolePolicy)).isFalse() } } diff --git a/jetbrains-core/tst/software/aws/toolkits/jetbrains/services/cloudformation/yaml/YamlCloudFormationTemplateTest.kt b/jetbrains-core/tst/software/aws/toolkits/jetbrains/services/cloudformation/yaml/YamlCloudFormationTemplateTest.kt index cea2b93495..37212b38fb 100644 --- a/jetbrains-core/tst/software/aws/toolkits/jetbrains/services/cloudformation/yaml/YamlCloudFormationTemplateTest.kt +++ b/jetbrains-core/tst/software/aws/toolkits/jetbrains/services/cloudformation/yaml/YamlCloudFormationTemplateTest.kt @@ -18,14 +18,13 @@ import org.jetbrains.annotations.NotNull import org.junit.Rule import org.junit.Test import org.junit.rules.TemporaryFolder +import software.aws.toolkits.core.utils.test.notNull import software.aws.toolkits.jetbrains.services.cloudformation.CloudFormationTemplate import software.aws.toolkits.jetbrains.services.cloudformation.LambdaFunction import software.aws.toolkits.jetbrains.services.cloudformation.SamFunction import software.aws.toolkits.jetbrains.utils.rules.CodeInsightTestFixtureRule import software.aws.toolkits.resources.message import java.io.File -import kotlin.test.assertNotNull -import kotlin.test.assertNull class YamlCloudFormationTemplateTest { @Rule @@ -54,9 +53,11 @@ class YamlCloudFormationTemplateTest { @Test fun noResourcesReturnsEmpty() { - val template = yamlTemplate(""" + val template = yamlTemplate( + """ Description: "Some description" - """.trimIndent()) + """.trimIndent() + ) runInEdtAndWait { assertThat(template.resources().toList()).isEmpty() } @@ -64,12 +65,14 @@ Description: "Some description" @Test fun emptyResourcesReturnsEmpty() { - val template = yamlTemplate(""" + val template = yamlTemplate( + """ Description: "Some description" Resources: - """.trimIndent()) + """.trimIndent() + ) runInEdtAndWait { assertThat(template.resources().toList()).isEmpty() } @@ -77,11 +80,13 @@ Resources: @Test fun partialResourceIsIgnored() { - val template = yamlTemplate(""" + val template = yamlTemplate( + """ Description: "Some description" Resources: Foo: - """.trimIndent()) + """.trimIndent() + ) runInEdtAndWait { assertThat(template.resources().toList()).isEmpty() } @@ -97,9 +102,11 @@ Resources: @Test fun noParametersReturnsEmpty() { - val template = yamlTemplate(""" + val template = yamlTemplate( + """ Description: "Some description" - """.trimIndent()) + """.trimIndent() + ) runInEdtAndWait { assertThat(template.parameters().toList()).isEmpty() } @@ -107,12 +114,14 @@ Description: "Some description" @Test fun emptyParametersReturnsEmpty() { - val template = yamlTemplate(""" + val template = yamlTemplate( + """ Description: "Some description" Parameters: - """.trimIndent()) + """.trimIndent() + ) runInEdtAndWait { assertThat(template.parameters().toList()).isEmpty() } @@ -124,10 +133,11 @@ Parameters: runInEdtAndWait { assertThat(template.parameters().toList()).hasSize(2) val tableTag = template.parameters().firstOrNull { it.logicalName == "TableTag" } - assertNotNull(tableTag) - assertNull(tableTag.defaultValue()) - assertNotNull(tableTag.description()) - assertNull(tableTag.constraintDescription()) + assertThat(tableTag).notNull.satisfies { tag -> + assertThat(tag.defaultValue()).isNull() + assertThat(tag.description()).isNotNull() + assertThat(tag.constraintDescription()).isNull() + } } } diff --git a/jetbrains-core/tst/software/aws/toolkits/jetbrains/services/lambda/execution/remote/RemoteLambdaRunConfigurationTest.kt b/jetbrains-core/tst/software/aws/toolkits/jetbrains/services/lambda/execution/remote/RemoteLambdaRunConfigurationTest.kt index c775818b33..bbd405e40d 100644 --- a/jetbrains-core/tst/software/aws/toolkits/jetbrains/services/lambda/execution/remote/RemoteLambdaRunConfigurationTest.kt +++ b/jetbrains-core/tst/software/aws/toolkits/jetbrains/services/lambda/execution/remote/RemoteLambdaRunConfigurationTest.kt @@ -59,14 +59,14 @@ class RemoteLambdaRunConfigurationTest { @Test fun invalidRegion() { - val runConfiguration = createRunConfiguration( - project = projectRule.project, - regionId = null - ) - assertThat(runConfiguration).isNotNull - assertThatThrownBy { runConfiguration.checkConfiguration() } - .isInstanceOf(RuntimeConfigurationError::class.java) - .hasMessage(message("configure.validate.no_region_specified")) + val runConfiguration = createRunConfiguration( + project = projectRule.project, + regionId = null + ) + assertThat(runConfiguration).isNotNull + assertThatThrownBy { runConfiguration.checkConfiguration() } + .isInstanceOf(RuntimeConfigurationError::class.java) + .hasMessage(message("configure.validate.no_region_specified")) } @Test diff --git a/jetbrains-core/tst/software/aws/toolkits/jetbrains/services/lambda/sam/SamCommonTest.kt b/jetbrains-core/tst/software/aws/toolkits/jetbrains/services/lambda/sam/SamCommonTest.kt index b022dab0c3..bb6340de6b 100644 --- a/jetbrains-core/tst/software/aws/toolkits/jetbrains/services/lambda/sam/SamCommonTest.kt +++ b/jetbrains-core/tst/software/aws/toolkits/jetbrains/services/lambda/sam/SamCommonTest.kt @@ -64,7 +64,7 @@ class SamCommonTest { runReadAction { val dir = file.containingDirectory.virtualFile val templateFile = SamCommon.getTemplateFromDirectory(dir) - assertNotNull(templateFile) + assertThat(templateFile).isNotNull } } diff --git a/jetbrains-core/tst/software/aws/toolkits/jetbrains/services/lambda/upload/CreateLambdaFunctionTest.kt b/jetbrains-core/tst/software/aws/toolkits/jetbrains/services/lambda/upload/CreateLambdaFunctionTest.kt index 05f64dc8eb..cc073d52b5 100644 --- a/jetbrains-core/tst/software/aws/toolkits/jetbrains/services/lambda/upload/CreateLambdaFunctionTest.kt +++ b/jetbrains-core/tst/software/aws/toolkits/jetbrains/services/lambda/upload/CreateLambdaFunctionTest.kt @@ -14,15 +14,13 @@ import com.nhaarman.mockitokotlin2.any import com.nhaarman.mockitokotlin2.doAnswer import com.nhaarman.mockitokotlin2.mock import org.assertj.core.api.Assertions.assertThat +import org.assertj.core.api.Assertions.assertThatThrownBy import org.junit.Before import org.junit.Rule import org.junit.Test import software.aws.toolkits.jetbrains.services.lambda.LambdaHandlerResolver import software.aws.toolkits.jetbrains.utils.rules.JavaCodeInsightTestFixtureRule import software.aws.toolkits.jetbrains.utils.rules.openFile -import kotlin.test.assertFails -import kotlin.test.assertFalse -import kotlin.test.assertTrue class CreateLambdaFunctionTest { @Rule @@ -72,7 +70,7 @@ Resources: val handlerName = "helloworld.App::handleRequest" runInEdtAndWait { - assertFails { CreateLambdaFunction(handlerName, null, null) } + assertThatThrownBy { CreateLambdaFunction(handlerName, null, null) }.isInstanceOf(java.lang.IllegalArgumentException::class.java) } } @@ -84,7 +82,7 @@ Resources: } runInEdtAndWait { - assertFails { CreateLambdaFunction(handlerName, null, handlerResolver) } + assertThatThrownBy { CreateLambdaFunction(handlerName, null, handlerResolver) }.isInstanceOf(java.lang.IllegalArgumentException::class.java) } } @@ -93,7 +91,7 @@ Resources: val handlerName = "helloworld.App::handleRequest" runInEdtAndWait { - assertFails { CreateLambdaFunction(handlerName, smartElement, null) } + assertThatThrownBy { CreateLambdaFunction(handlerName, smartElement, null) }.isInstanceOf(java.lang.IllegalArgumentException::class.java) } } @@ -110,7 +108,7 @@ Resources: val actionEvent = TestActionEvent() action.update(actionEvent) - assertFalse { actionEvent.presentation.isVisible } + assertThat(actionEvent.presentation.isVisible).isFalse() } } @@ -127,7 +125,7 @@ Resources: val actionEvent = TestActionEvent() action.update(actionEvent) - assertTrue { actionEvent.presentation.isVisible } + assertThat(actionEvent.presentation.isVisible).isTrue() } } @@ -144,7 +142,7 @@ Resources: val actionEvent = TestActionEvent() action.update(actionEvent) - assertTrue { actionEvent.presentation.isVisible } + assertThat(actionEvent.presentation.isVisible).isTrue() } } diff --git a/jetbrains-core/tst/software/aws/toolkits/jetbrains/utils/ResourceOperationAgainstCodePipelineTest.kt b/jetbrains-core/tst/software/aws/toolkits/jetbrains/utils/ResourceOperationAgainstCodePipelineTest.kt index 2f9600e041..df0c10adbb 100644 --- a/jetbrains-core/tst/software/aws/toolkits/jetbrains/utils/ResourceOperationAgainstCodePipelineTest.kt +++ b/jetbrains-core/tst/software/aws/toolkits/jetbrains/utils/ResourceOperationAgainstCodePipelineTest.kt @@ -6,6 +6,7 @@ package software.aws.toolkits.jetbrains.utils import com.intellij.testFramework.ProjectRule import com.nhaarman.mockitokotlin2.any import com.nhaarman.mockitokotlin2.whenever +import org.assertj.core.api.Assertions.assertThat import org.junit.Rule import org.junit.Test import software.amazon.awssdk.services.resourcegroupstaggingapi.ResourceGroupsTaggingApiClient @@ -15,8 +16,6 @@ import software.amazon.awssdk.services.resourcegroupstaggingapi.model.ResourceTa import software.amazon.awssdk.services.resourcegroupstaggingapi.model.Tag import software.amazon.awssdk.services.resourcegroupstaggingapi.paginators.GetResourcesIterable import software.aws.toolkits.jetbrains.core.MockClientManagerRule -import kotlin.test.assertEquals -import kotlin.test.assertNull class ResourceOperationAgainstCodePipelineTest { @@ -45,7 +44,7 @@ class ResourceOperationAgainstCodePipelineTest { } ) - assertNull(getCodePipelineArnForResource(projectRule.project, RESOURCE_ARN, RESOURCE_TYPE_FILTER)) + assertThat(getCodePipelineArnForResource(projectRule.project, RESOURCE_ARN, RESOURCE_TYPE_FILTER)).isNull() } @Test @@ -63,7 +62,7 @@ class ResourceOperationAgainstCodePipelineTest { } ) - assertNull(getCodePipelineArnForResource(projectRule.project, RESOURCE_ARN, RESOURCE_TYPE_FILTER)) + assertThat(getCodePipelineArnForResource(projectRule.project, RESOURCE_ARN, RESOURCE_TYPE_FILTER)).isNull() } @Test @@ -81,7 +80,7 @@ class ResourceOperationAgainstCodePipelineTest { } ) - assertNull(getCodePipelineArnForResource(projectRule.project, RESOURCE_ARN, RESOURCE_TYPE_FILTER)) + assertThat(getCodePipelineArnForResource(projectRule.project, RESOURCE_ARN, RESOURCE_TYPE_FILTER)).isNull() } @Test @@ -99,7 +98,7 @@ class ResourceOperationAgainstCodePipelineTest { } ) - assertEquals(CODEPIPELINE_ARN, getCodePipelineArnForResource(projectRule.project, RESOURCE_ARN, RESOURCE_TYPE_FILTER)) + assertThat(CODEPIPELINE_ARN).isEqualTo(getCodePipelineArnForResource(projectRule.project, RESOURCE_ARN, RESOURCE_TYPE_FILTER)) } private fun getResourceTagMapping(resourceARN: String, tagKey: String, tagValue: String) = ResourceTagMapping.builder() diff --git a/jetbrains-ultimate/tst/software/aws/toolkits/jetbrains/services/lambda/nodejs/NodeJsHelperTest.kt b/jetbrains-ultimate/tst/software/aws/toolkits/jetbrains/services/lambda/nodejs/NodeJsHelperTest.kt index af4b78c8ef..522c71bd91 100644 --- a/jetbrains-ultimate/tst/software/aws/toolkits/jetbrains/services/lambda/nodejs/NodeJsHelperTest.kt +++ b/jetbrains-ultimate/tst/software/aws/toolkits/jetbrains/services/lambda/nodejs/NodeJsHelperTest.kt @@ -6,12 +6,12 @@ package software.aws.toolkits.jetbrains.services.lambda.nodejs import com.intellij.openapi.roots.ProjectFileIndex import com.intellij.openapi.vfs.VfsUtilCore import com.intellij.testFramework.runInEdtAndWait +import org.assertj.core.api.Assertions.assertThat import org.junit.Rule import org.junit.Test import software.aws.toolkits.jetbrains.utils.rules.NodeJsCodeInsightTestFixtureRule import software.aws.toolkits.jetbrains.utils.rules.addLambdaHandler import software.aws.toolkits.jetbrains.utils.rules.addPackageJsonFile -import kotlin.test.assertEquals class NodeJsHelperTest { @@ -30,7 +30,7 @@ class NodeJsHelperTest { runInEdtAndWait { val contentRoot = ProjectFileIndex.getInstance(projectRule.project).getContentRootForFile(element.containingFile.virtualFile) val sourceRoot = inferSourceRoot(projectRule.project, element.containingFile.virtualFile) - assertEquals(contentRoot, sourceRoot) + assertThat(contentRoot).isEqualTo(sourceRoot) } } @@ -49,7 +49,7 @@ class NodeJsHelperTest { runInEdtAndWait { val contentRoot = ProjectFileIndex.getInstance(projectRule.project).getContentRootForFile(element.containingFile.virtualFile) val sourceRoot = inferSourceRoot(projectRule.project, element.containingFile.virtualFile) - assertEquals(VfsUtilCore.findRelativeFile("foo", contentRoot), sourceRoot) + assertThat(VfsUtilCore.findRelativeFile("foo", contentRoot)).isEqualTo(sourceRoot) } } @@ -68,7 +68,7 @@ class NodeJsHelperTest { runInEdtAndWait { val contentRoot = ProjectFileIndex.getInstance(projectRule.project).getContentRootForFile(element.containingFile.virtualFile) val sourceRoot = inferSourceRoot(projectRule.project, element.containingFile.virtualFile) - assertEquals(contentRoot, sourceRoot) + assertThat(contentRoot).isEqualTo(sourceRoot) } } } diff --git a/ktlint-rules/src/software/aws/toolkits/ktlint/rules/BannedImportsRule.kt b/ktlint-rules/src/software/aws/toolkits/ktlint/rules/BannedImportsRule.kt index 55e1b9fe10..95ea777d6e 100644 --- a/ktlint-rules/src/software/aws/toolkits/ktlint/rules/BannedImportsRule.kt +++ b/ktlint-rules/src/software/aws/toolkits/ktlint/rules/BannedImportsRule.kt @@ -22,6 +22,12 @@ class BannedImportsRule : Rule("banned-imports") { if (element.importedFqName?.asString()?.startsWith("org.hamcrest") == true) { emit(node.startOffset, "Use AssertJ instead of Hamcrest assertions", false) } + + if (element.importedFqName?.asString()?.startsWith("kotlin.test.assert") == true && + element.importedFqName?.asString()?.startsWith("kotlin.test.assertNotNull") == false + ) { + emit(node.startOffset, "Use AssertJ instead of Kotlin test assertions", false) + } } } } diff --git a/ktlint-rules/tst/software/aws/toolkits/ktlint/rules/BannedImportsRuleTest.kt b/ktlint-rules/tst/software/aws/toolkits/ktlint/rules/BannedImportsRuleTest.kt index a930e13a0a..18d0c3a7a2 100644 --- a/ktlint-rules/tst/software/aws/toolkits/ktlint/rules/BannedImportsRuleTest.kt +++ b/ktlint-rules/tst/software/aws/toolkits/ktlint/rules/BannedImportsRuleTest.kt @@ -37,6 +37,33 @@ class BannedImportsRuleTest { ) } + @Test + fun `Importing Kotlin test assert fails`() { + assertThat(rule.lint("import kotlin.test.assertTrue")) + .containsExactly( + LintError( + 1, + 1, + "banned-imports", + "Use AssertJ instead of Kotlin test assertions" + ) + ) + assertThat(rule.lint("import kotlin.test.assertFalse")) + .containsExactly( + LintError( + 1, + 1, + "banned-imports", + "Use AssertJ instead of Kotlin test assertions" + ) + ) + } + + @Test + fun `Importing kotlin test notNull succeeds`() { + assertThat(rule.lint("import kotlin.test.assertNotNull")).isEmpty() + } + @Test fun `Importing Assert assertThat succeeds`() { assertThat(rule.lint("import org.assertj.core.api.Assertions.assertThat")).isEmpty() From 3ed44c0f9b03643560080af390fdc3618248b705 Mon Sep 17 00:00:00 2001 From: Hunter Werlla Date: Tue, 18 Aug 2020 10:27:05 -0700 Subject: [PATCH 0022/1546] Finish the conversion of Gradle to Kotlin (#1995) - Convert build.gradle to build.gradle.kts - Move IntelliJVersions into buildSrc so we get type checking and completion - Clean up code that was used for the Groovy->Kotlin conversion - Add helpers to buildSrc to help mask dynamic things --- build.gradle | 333 ---------------- build.gradle.kts | 371 ++++++++++++++++++ buildSrc/build.gradle.kts | 12 +- .../aws/toolkits/gradle/BuildScriptUtils.kt | 39 ++ .../aws/toolkits/gradle/IdeVersions.kt | 183 +++++++++ .../aws/toolkits/gradle}/SourcesUtils.kt | 8 +- .../gradle/resources/ValidateMessages.kt | 3 +- intellijJVersions.gradle | 207 ---------- jetbrains-core/build.gradle.kts | 26 +- jetbrains-rider/build.gradle.kts | 37 +- jetbrains-ultimate/build.gradle.kts | 13 +- resources/build.gradle.kts | 4 +- telemetry-client/build.gradle.kts | 4 +- ui-tests/build.gradle.kts | 4 +- 14 files changed, 655 insertions(+), 589 deletions(-) delete mode 100644 build.gradle create mode 100644 build.gradle.kts create mode 100644 buildSrc/src/software/aws/toolkits/gradle/BuildScriptUtils.kt create mode 100644 buildSrc/src/software/aws/toolkits/gradle/IdeVersions.kt rename buildSrc/src/{ => software/aws/toolkits/gradle}/SourcesUtils.kt (91%) delete mode 100644 intellijJVersions.gradle diff --git a/build.gradle b/build.gradle deleted file mode 100644 index 780eef9ae7..0000000000 --- a/build.gradle +++ /dev/null @@ -1,333 +0,0 @@ -// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import org.jetbrains.kotlin.gradle.tasks.KotlinCompile -import software.aws.toolkits.gradle.changelog.tasks.GenerateGithubChangeLog -import software.aws.toolkits.gradle.resources.ValidateMessages -import java.nio.file.Files -import java.nio.file.Paths - -buildscript { - repositories { - maven { url "https://plugins.gradle.org/m2/" } - mavenCentral() - jcenter() - } - dependencies { - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion" - classpath "gradle.plugin.org.jetbrains.intellij.plugins:gradle-intellij-plugin:$ideaPluginVersion" - classpath "com.adarshr:gradle-test-logger-plugin:1.7.0" - } -} - -plugins { - id "de.undercouch.download" version "4.1.1" apply false -} - -apply from: 'intellijJVersions.gradle' - -def ideVersion = shortenVersion(resolveIdeProfileName()) - -group 'software.aws.toolkits' -// please check changelog generation logic if this format is changed -version "$toolkitVersion-$ideVersion".toString() - -repositories { - maven { url "https://www.jetbrains.com/intellij-repository/snapshots/" } -} - -allprojects { - repositories { - mavenLocal() - mavenCentral() - jcenter() - } - - apply plugin: "com.adarshr.test-logger" - apply plugin: 'java' - apply plugin: 'jacoco' - - sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = JavaVersion.VERSION_1_8 - - tasks.withType(JavaExec) { - systemProperty("aws.toolkits.enableTelemetry", false) - } - - tasks.withType(org.jetbrains.intellij.tasks.RunIdeTask) { - intellij { - if (System.env.ALTERNATIVE_IDE) { - if (file(System.env.ALTERNATIVE_IDE).exists()) { - alternativeIdePath = System.env.ALTERNATIVE_IDE - } else { - throw new GradleException("ALTERNATIVE_IDE path not found" - + (System.env.ALTERNATIVE_IDE ==~ /.*[\/\\] *$/ - ? " (HINT: remove trailing slash '/')" - : ": ${System.env.ALTERNATIVE_IDE}")) - } - } - } - } - - configurations { - runtimeClasspath.exclude group: "org.slf4j" - runtimeClasspath.exclude group: "org.jetbrains.kotlin" - runtimeClasspath.exclude group: "org.jetbrains.kotlinx" - runtimeClasspath.exclude group: "software.amazon.awssdk", module: "netty-nio-client" - } -} - -// Kotlin plugin seems to be bugging out when there are no kotlin sources -configure(subprojects - project(":telemetry-client")) { - apply plugin: 'kotlin' - - sourceSets { - integrationTest { - kotlin.srcDir 'it' - } - } -} - -subprojects { - group = parent.group - version = parent.version - - apply plugin: 'java' - apply plugin: 'idea' - - sourceSets { - main.java.srcDirs = SourceUtils.findFolders(project, "src", ideVersion) - main.resources.srcDirs = SourceUtils.findFolders(project, "resources", ideVersion) - test.java.srcDirs = SourceUtils.findFolders(project, "tst", ideVersion) - test.resources.srcDirs = SourceUtils.findFolders(project, "tst-resources", ideVersion) - integrationTest { - compileClasspath += main.output + test.output - runtimeClasspath += main.output + test.output - java.srcDirs = SourceUtils.findFolders(project, "it", ideVersion) - resources.srcDirs = SourceUtils.findFolders(project, "it-resources", ideVersion) - } - } - - configurations { - testArtifacts - - integrationTestImplementation.extendsFrom testImplementation - integrationTestRuntimeOnly.extendsFrom testRuntimeOnly - } - - dependencies { - implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlinVersion" - implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlinVersion" - testImplementation "com.nhaarman.mockitokotlin2:mockito-kotlin:$mockitoKotlinVersion" - testImplementation "org.mockito:mockito-core:$mockitoVersion" - testImplementation "org.assertj:assertj-core:$assertjVersion" - testImplementation "junit:junit:$junitVersion" - } - - testlogger { - showFullStackTraces true - showStandardStreams true - showPassedStandardStreams false - showSkippedStandardStreams true - showFailedStandardStreams true - } - - test { - jacoco { - // don't instrument sdk, icons, ktlint, etc. - includes = ["software.aws.toolkits.*"] - excludes = ["software.aws.toolkits.ktlint.*"] - } - - reports { - junitXml.enabled = true - html.enabled = true - } - } - - idea { - module { - sourceDirs += sourceSets.main.java.srcDirs - resourceDirs += sourceSets.main.resources.srcDirs - testSourceDirs += file("tst-$ideVersion") - testResourceDirs += file("tst-resources-$ideVersion") - - sourceDirs -= file("it") - testSourceDirs += file("it") - testSourceDirs += file("it-$ideVersion") - - resourceDirs -= file("it-resources") - testResourceDirs += file("it-resources") - testResourceDirs += file("it-resources-$ideVersion") - } - } - - task integrationTest(type: Test) { - group = LifecycleBasePlugin.VERIFICATION_GROUP - description = "Runs the integration tests." - testClassesDirs = sourceSets.integrationTest.output.classesDirs - classpath = sourceSets.integrationTest.runtimeClasspath - - jacoco { - // don't instrument sdk, icons, ktlint, etc. - includes = ["software.aws.toolkits.*"] - excludes = ["software.aws.toolkits.ktlint.*"] - } - - project.plugins.withId("org.jetbrains.intellij") { - systemProperty("log.dir", "${project.intellij.sandboxDirectory}-test/logs") - } - - mustRunAfter tasks.test - } - - project.plugins.withId("org.jetbrains.intellij") { - downloadRobotServerPlugin.version = remoteRobotVersion - - tasks.withType(org.jetbrains.intellij.tasks.RunIdeForUiTestTask).all { - systemProperty "robot-server.port", remoteRobotPort - systemProperty "ide.mac.file.chooser.native", "false" - systemProperty "jb.consents.confirmation.enabled", "false" - // This does some magic in EndUserAgreement.java to make it not show the privacy policy - systemProperty "jb.privacy.policy.text", "" - if (System.getenv("CI") != null) { - systemProperty("aws.sharedCredentialsFile", "/tmp/.aws/credentials") - } - } - - jacoco.applyTo(runIdeForUiTests) - } - - tasks.withType(KotlinCompile).all { - kotlinOptions.jvmTarget = "1.8" - } - - // Force us to compile the integration tests even during check even though we don't run them - check.dependsOn(integrationTestClasses) - - task testJar(type: Jar) { - baseName = "${project.name}-test" - from sourceSets.test.output - from sourceSets.integrationTest.output - } - - artifacts { - testArtifacts testJar - } - - // Remove the tasks added in by gradle-intellij-plugin so that we don't publish/verify multiple times - project.afterEvaluate { - removeTask(tasks, org.jetbrains.intellij.tasks.PublishTask) - removeTask(tasks, org.jetbrains.intellij.tasks.VerifyPluginTask) - removeTask(tasks, org.jetbrains.intellij.tasks.BuildSearchableOptionsTask) - } -} - -configurations { - ktlint -} - -def removeTask(TaskContainer tasks, Class takeType) { - tasks.withType(takeType).configureEach { - setEnabled(false) - } -} - -apply plugin: 'org.jetbrains.intellij' -apply plugin: 'toolkit-change-log' - -intellij { - version ideSdkVersion("IC") - pluginName 'aws-jetbrains-toolkit' - updateSinceUntilBuild false - downloadSources = System.getenv("CI") == null -} - -prepareSandbox { - tasks.findByPath(":jetbrains-rider:prepareSandbox")?.collect { - from(it) - } -} - -publishPlugin { - token publishToken - channels publishChannel ? publishChannel.split(',').collect { it.trim() } : [] -} - -tasks.register('generateChangeLog', GenerateGithubChangeLog) { - changeLogFile = project.file("CHANGELOG.md") -} - -task ktlint(type: JavaExec, group: "verification") { - description = "Check Kotlin code style." - classpath = configurations.ktlint - main = "com.pinterest.ktlint.Main" - - def isWindows = System.properties['os.name'].toLowerCase().contains('windows') - - def toInclude = project.rootDir.relativePath(project.projectDir) + "/**/*.kt" - def toExclude = project.rootDir.relativePath(new File(project.projectDir, "jetbrains-rider")) + "/**/*.Generated.kt" - - if (isWindows) { - toInclude = toInclude.replace("/", "\\") - toExclude = toExclude.replace("/", "\\") - } - - args "-v", toInclude, "!${toExclude}", "!/**/generated-src/**/*.kt" - - inputs.files(project.fileTree(dir: ".", include: "**/*.kt")) - outputs.dir("${project.buildDir}/reports/ktlint/") -} - -tasks.register('validateLocalizedMessages', ValidateMessages) { - paths = [file("${project.rootDir}/resources/resources/software/aws/toolkits/resources/localized_messages.properties")] -} - -check.dependsOn ktlint -check.dependsOn validateLocalizedMessages -check.dependsOn verifyPlugin - -task coverageReport(type: JacocoReport) { - executionData fileTree(project.rootDir.absolutePath).include("**/build/jacoco/*.exec") - - getAdditionalSourceDirs().from(subprojects.sourceSets.main.java.srcDirs) - getSourceDirectories().from(subprojects.sourceSets.main.java.srcDirs) - getClassDirectories().from(subprojects.sourceSets.main.output.classesDirs) - - reports { - html.enabled true - xml.enabled true - } -} -subprojects.forEach { - coverageReport.mustRunAfter(it.tasks.withType(Test)) -} -check.dependsOn coverageReport - -// Workaround for runIde being defined in multiple projects, if we request the root project runIde, "alias" it to -// community edition -if (gradle.startParameter.taskNames.contains("runIde")) { - // Only disable this if running from root project - if (gradle.startParameter.projectDir == project.rootProject.rootDir - || System.properties.containsKey("idea.gui.tests.gradle.runner")) { - println("Top level runIde selected, excluding sub-projects' runIde") - gradle.taskGraph.whenReady { graph -> - graph.allTasks.forEach { - if (it.name == "runIde" && - it.project != project(':jetbrains-core')) { - it.enabled = false - } - } - } - } -} - -dependencies { - implementation project(':jetbrains-ultimate') - project.findProject(':jetbrains-rider')?.collect { - implementation it - } - - ktlint "com.pinterest:ktlint:$ktlintVersion" - ktlint project(":ktlint-rules") -} diff --git a/build.gradle.kts b/build.gradle.kts new file mode 100644 index 0000000000..8f4d2739ec --- /dev/null +++ b/build.gradle.kts @@ -0,0 +1,371 @@ +// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import com.adarshr.gradle.testlogger.TestLoggerExtension +import com.adarshr.gradle.testlogger.TestLoggerPlugin +import org.jetbrains.intellij.tasks.BuildSearchableOptionsTask +import org.jetbrains.intellij.tasks.DownloadRobotServerPluginTask +import org.jetbrains.intellij.tasks.PrepareSandboxTask +import org.jetbrains.intellij.tasks.PublishTask +import org.jetbrains.intellij.tasks.RunIdeForUiTestTask +import org.jetbrains.intellij.tasks.RunIdeTask +import org.jetbrains.intellij.tasks.VerifyPluginTask +import org.jetbrains.kotlin.gradle.tasks.KotlinCompile +import software.aws.toolkits.gradle.IdeVersions +import software.aws.toolkits.gradle.ProductCode +import software.aws.toolkits.gradle.changelog.tasks.GenerateGithubChangeLog +import software.aws.toolkits.gradle.findFolders +import software.aws.toolkits.gradle.getOrCreate +import software.aws.toolkits.gradle.intellij +import software.aws.toolkits.gradle.removeTask +import software.aws.toolkits.gradle.resources.ValidateMessages + +buildscript { + repositories { + maven("https://plugins.gradle.org/m2/") + mavenCentral() + jcenter() + } + val kotlinVersion: String by project + val ideaPluginVersion: String by project + dependencies { + classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion") + classpath("gradle.plugin.org.jetbrains.intellij.plugins:gradle-intellij-plugin:$ideaPluginVersion") + classpath("com.adarshr:gradle-test-logger-plugin:1.7.0") + } +} + +val ideVersions = IdeVersions(project) +val ideVersion = ideVersions.resolveShortenedIdeProfileName() +val toolkitVersion: String by project +val kotlinVersion: String by project +val mockitoVersion: String by project +val mockitoKotlinVersion: String by project +val assertjVersion: String by project +val junitVersion: String by project +val remoteRobotPort: String by project +val ktlintVersion: String by project +val remoteRobotVersion: String by project +val ideaPluginVersion: String by project +// publishing +val publishToken: String by project +val publishChannel: String by project + +plugins { + java + id("de.undercouch.download") apply false +} + +group = "software.aws.toolkits" +// please check changelog generation logic if this format is changed +version = "$toolkitVersion-$ideVersion".toString() + +repositories { + maven("https://www.jetbrains.com/intellij-repository/snapshots/") +} + +allprojects { + repositories { + mavenLocal() + mavenCentral() + jcenter() + } + + apply(plugin = "com.adarshr.test-logger") + apply(plugin = "java") + apply(plugin = "jacoco") + + java.sourceCompatibility = JavaVersion.VERSION_1_8 + java.targetCompatibility = JavaVersion.VERSION_1_8 + + tasks.withType(JavaExec::class.java) { + systemProperty("aws.toolkits.enableTelemetry", false) + } + + tasks.withType(RunIdeTask::class.java) { + val alternativeIde = System.getenv("ALTERNATIVE_IDE") + if (alternativeIde != null) { + if (File(alternativeIde).exists()) { + intellij { + alternativeIdePath = System.getenv("ALTERNATIVE_IDE") + } + } else { + throw GradleException("ALTERNATIVE_IDE path not found $alternativeIde ${if (alternativeIde.endsWith("/")) "remove the trailing slash" else ""}") + } + } + } + + configurations { + runtimeClasspath { + exclude(group = "org.slf4j") + exclude(group = "org.jetbrains.kotlin") + exclude(group = "org.jetbrains.kotlinx") + exclude(group = "software.amazon.awssdk", module = "netty-nio-client") + } + } +} + +// Kotlin plugin seems to be bugging out when there are no kotlin sources +configure(subprojects.filter { it.name != "telemetry-client" }) { + apply(plugin = "kotlin") + + sourceSets { + getOrCreate("integrationTest") { + java.srcDir("it") + } + } +} + +subprojects { + parent?.let { + group = it.group + version = it.version + } ?: throw IllegalStateException("Subproject $name parent is null!") + + apply(plugin = "java") + apply(plugin = "idea") + apply(plugin = "com.adarshr.test-logger") + + sourceSets { + main { + java.srcDirs(findFolders(project, "src", ideVersion)) + resources.srcDirs(findFolders(project, "resources", ideVersion)) + } + test { + java.srcDirs(findFolders(project, "tst", ideVersion)) + resources.srcDirs(findFolders(project, "tst-resources", ideVersion)) + } + getOrCreate("integrationTest") { + compileClasspath += main.get().output + test.get().output + runtimeClasspath += main.get().output + test.get().output + java.srcDirs(findFolders(project, "it", ideVersion)) + resources.srcDirs(findFolders(project, "it-resources", ideVersion)) + } + } + + val testArtifacts by configurations.creating + configurations.getByName("integrationTestImplementation").apply { + extendsFrom(configurations.getByName("testImplementation")) + } + configurations.getByName("integrationTestRuntimeOnly").apply { + extendsFrom(configurations.getByName("testRuntimeOnly")) + } + + dependencies { + implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlinVersion") + implementation("org.jetbrains.kotlin:kotlin-reflect:$kotlinVersion") + testImplementation("com.nhaarman.mockitokotlin2:mockito-kotlin:$mockitoKotlinVersion") + testImplementation("org.mockito:mockito-core:$mockitoVersion") + testImplementation("org.assertj:assertj-core:$assertjVersion") + testImplementation("junit:junit:$junitVersion") + } + + plugins.withType { + configure { + showFullStackTraces = true + showStandardStreams = true + showPassedStandardStreams = false + showSkippedStandardStreams = true + showFailedStandardStreams = true + } + } + + tasks.test { + configure { + // don't instrument sdk, icons, ktlint, etc. + includes = listOf("software.aws.toolkits.*") + excludes = listOf("software.aws.toolkits.ktlint.*") + } + + reports { + junitXml.isEnabled = true + html.isEnabled = true + } + } + + plugins.withType { + model.module.apply { + sourceDirs.plusAssign(sourceSets.main.get().java.srcDirs) + resourceDirs.plusAssign(sourceSets.main.get().resources.srcDirs) + testSourceDirs.plusAssign(File("tst-$ideVersion")) + testResourceDirs.plusAssign(File("tst-resources-$ideVersion")) + + sourceDirs.minusAssign(File("it")) + testSourceDirs.plusAssign(File("it")) + testSourceDirs.plusAssign(File("it-$ideVersion")) + + resourceDirs.minusAssign(File("it-resources")) + testResourceDirs.plusAssign(File("it-resources")) + testResourceDirs.plusAssign(File("it-resources-$ideVersion")) + } + } + + tasks.register("integrationTest") { + group = LifecycleBasePlugin.VERIFICATION_GROUP + description = "Runs the integration tests." + testClassesDirs = sourceSets["integrationTest"].output.classesDirs + classpath = sourceSets["integrationTest"].runtimeClasspath + + configure { + // don"t instrument sdk, icons, ktlint, etc. + includes = listOf("software.aws.toolkits.*") + excludes = listOf("software.aws.toolkits.ktlint.*") + } + + project.plugins.withId("org.jetbrains.intellij") { + systemProperty("log.dir", "${(project.extensions["intellij"] as org.jetbrains.intellij.IntelliJPluginExtension).sandboxDirectory}-test/logs") + } + + mustRunAfter(tasks.test) + } + + project.plugins.withId("org.jetbrains.intellij") { + tasks.withType(DownloadRobotServerPluginTask::class.java) { + this.version = remoteRobotVersion + } + + tasks.withType(RunIdeForUiTestTask::class.java).all { + systemProperty("robot-server.port", remoteRobotPort) + systemProperty("ide.mac.file.chooser.native", "false") + systemProperty("jb.consents.confirmation.enabled", "false") + // This does some magic in EndUserAgreement.java to make it not show the privacy policy + systemProperty("jb.privacy.policy.text", "") + if (System.getenv("CI") != null) { + systemProperty("aws.sharedCredentialsFile", "/tmp/.aws/credentials") + } + } + + extensions.getByType().applyTo(tasks.getByName("runIdeForUiTests")) + } + + tasks.withType().all { + kotlinOptions.jvmTarget = "1.8" + } + + // Force us to compile the integration tests even during check even though we don't run them + tasks.named("check") { + dependsOn.add(sourceSets.getByName("integrationTest").compileJavaTaskName) + } + + val testJar = tasks.register("testJar") { + baseName = "${project.name}-test" + from(sourceSets.test.get().output) + from(sourceSets.getByName("integrationTest").output) + } + + artifacts { + add("testArtifacts", testJar) + } + + // Remove the tasks added in by gradle-intellij-plugin so that we don"t publish/verify multiple times + project.afterEvaluate { + removeTask() + removeTask() + removeTask() + } +} + +val ktlint: Configuration by configurations.creating + +apply(plugin = "org.jetbrains.intellij") +apply(plugin = "toolkit-change-log") + +intellij { + version = ideVersions.ideSdkVersion(ProductCode.IC) + pluginName = "aws-jetbrains-toolkit" + updateSinceUntilBuild = false + downloadSources = System.getenv("CI") == null +} + +tasks.getByName("prepareSandbox") { + tasks.findByPath(":jetbrains-rider:prepareSandbox")?.let { + from(it) + } +} + +tasks.getByName("publishPlugin") { + token(publishToken) + channels(if (publishChannel != null) publishChannel.split(",").map { it.trim() } else listOf()) +} + +tasks.register("generateChangeLog") { + changeLogFile.set(project.file("CHANGELOG.md")) +} + +val ktlintTask = tasks.register("ktlint") { + description = "Check Kotlin code style." + classpath = configurations.getByName("ktlint") + group = "verification" + main = "com.pinterest.ktlint.Main" + + val isWindows = System.getProperty("os.name")?.toLowerCase()?.contains("windows") == true + + // Must be relative or else Windows will fail + var toInclude = project.projectDir.toRelativeString(project.rootDir) + "/**/*.kt" + var toExclude = File(project.projectDir, "jetbrains-rider").toRelativeString(project.rootDir) + "/**/*.Generated.kt" + + if (isWindows) { + toInclude = toInclude.replace("/", "\\") + toExclude = toExclude.replace("/", "\\") + } + + args = listOf("-v", toInclude, "!${toExclude}", "!/**/generated-src/**/*.kt") + + inputs.files(fileTree(".") { include("**/*.kt") }) + outputs.dirs("${project.buildDir}/reports/ktlint/") +} + +val validateLocalizedMessages = tasks.register("validateLocalizedMessages") { + paths.set(listOf("${project.rootDir}/resources/resources/software/aws/toolkits/resources/localized_messages.properties")) +} + +val coverageReport = tasks.register("coverageReport") { + executionData.setFrom(fileTree(project.rootDir.absolutePath) { include("**/build/jacoco/*.exec") }) + + subprojects.forEach { + additionalSourceDirs.from(it.sourceSets.main.get().java.srcDirs) + sourceDirectories.from(it.sourceSets.main.get().java.srcDirs) + classDirectories.from(it.sourceSets.main.get().output.classesDirs) + } + + reports { + html.isEnabled = true + xml.isEnabled = true + } +} + +subprojects.forEach { + coverageReport.get().mustRunAfter(it.tasks.withType(Test::class.java)) +} + +val check = tasks.getByName("check") +check.dependsOn(ktlintTask) +check.dependsOn(validateLocalizedMessages) +check.dependsOn(tasks.getByName("verifyPlugin")) +check.dependsOn(coverageReport) + +// Workaround for runIde being defined in multiple projects, if we request the root project runIde, "alias" it to +// community edition +if (gradle.startParameter.taskNames.contains("runIde")) { + // Only disable this if running from root project + if (gradle.startParameter.projectDir == project.rootProject.rootDir || System.getProperty("idea.gui.tests.gradle.runner") != null) { + println("Top level runIde selected, excluding sub-projects") + gradle.taskGraph.whenReady { + allTasks.forEach { it -> + if (it.name == "runIde" && it.project != project(":jetbrains-core")) { + it.enabled = false + } + } + } + } +} + +dependencies { + implementation(project(":jetbrains-ultimate")) + project.findProject(":jetbrains-rider")?.let { + implementation(it) + } + + ktlint("com.pinterest:ktlint:$ktlintVersion") + ktlint(project(":ktlint-rules")) +} diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index 27c4e8a1fe..48550add1c 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -10,6 +10,7 @@ val assertjVersion: String by project val junitVersion: String by project val mockitoVersion: String by project val mockitoKotlinVersion: String by project +val ideaPluginVersion: String by project buildscript { // This has to be here otherwise properties are not loaded and nothing works @@ -24,6 +25,7 @@ buildscript { } repositories { + maven("https://plugins.gradle.org/m2/") mavenLocal() mavenCentral() jcenter() @@ -38,8 +40,12 @@ plugins { } sourceSets { - main.get().java.srcDir("src") - test.get().java.srcDir("src") + main { + java.srcDir("src") + } + test { + java.srcDir("src") + } } dependencies { @@ -50,6 +56,8 @@ dependencies { api("com.atlassian.commonmark:commonmark:0.11.0") api("software.amazon.awssdk:codegen:$awsSdkVersion") + implementation("gradle.plugin.org.jetbrains.intellij.plugins:gradle-intellij-plugin:$ideaPluginVersion") + testImplementation("org.assertj:assertj-core:$assertjVersion") testImplementation("junit:junit:$junitVersion") testImplementation("com.nhaarman.mockitokotlin2:mockito-kotlin:$mockitoKotlinVersion") diff --git a/buildSrc/src/software/aws/toolkits/gradle/BuildScriptUtils.kt b/buildSrc/src/software/aws/toolkits/gradle/BuildScriptUtils.kt new file mode 100644 index 0000000000..8368a522c7 --- /dev/null +++ b/buildSrc/src/software/aws/toolkits/gradle/BuildScriptUtils.kt @@ -0,0 +1,39 @@ +// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package software.aws.toolkits.gradle + +import org.gradle.api.GradleException +import org.gradle.api.Project +import org.gradle.api.Task +import org.gradle.api.UnknownDomainObjectException +import org.gradle.api.tasks.SourceSet +import org.gradle.api.tasks.SourceSetContainer +import org.jetbrains.intellij.IntelliJPluginExtension + +inline fun Project.removeTask() { + // For some reason in buildSrc, we can't use the <> version + tasks.withType(T::class.java) { + it.enabled = false + } +} + +/* When we dynamically apply(plugin = "org.jetbrains.intellij"), we do not get the nice extension functions + * pulled into scope. This function hides that fact, and gives a better error message when it fails. + */ +fun Project.intellij(block: IntelliJPluginExtension.() -> Unit) { + val intellij = try { + project.extensions.getByType(IntelliJPluginExtension::class.java) + } catch (e: Exception) { + throw GradleException("Unable to get extension intellij, did you apply(plugin = \"org.jetbrains.intellij\")?", e) + } + intellij.block() +} + +fun SourceSetContainer.getOrCreate(sourceSet: String, block: SourceSet.() -> Unit) { + try { + getByName(sourceSet).block() + } catch (e: UnknownDomainObjectException) { + create(sourceSet).block() + } +} diff --git a/buildSrc/src/software/aws/toolkits/gradle/IdeVersions.kt b/buildSrc/src/software/aws/toolkits/gradle/IdeVersions.kt new file mode 100644 index 0000000000..d3949edaeb --- /dev/null +++ b/buildSrc/src/software/aws/toolkits/gradle/IdeVersions.kt @@ -0,0 +1,183 @@ +// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package software.aws.toolkits.gradle + +import org.gradle.api.Project + +enum class ProductCode { + IC, + IU, + RD +} + +open class ProductProfile( + val sdkVersion: String, + val plugins: List +) + +class RiderProfile( + sdkVersion: String, + plugins: List, + val rdGenVersion: String, + val nugetVersion: String +) : ProductProfile(sdkVersion, plugins) + +data class Profile( + val sinceVersion: String, + val untilVersion: String, + val products: Map +) + +class IdeVersions(private val project: Project) { + private val ideProfiles = mapOf( + "2019.3" to Profile( + sinceVersion = "193", + untilVersion = "193.*", + products = mapOf( + ProductCode.IC to ProductProfile( + sdkVersion = "IC-2019.3", + plugins = listOf( + "org.jetbrains.plugins.terminal", + "org.jetbrains.plugins.yaml", + "PythonCore:193.5233.139", + "java", + "com.intellij.gradle", + "org.jetbrains.idea.maven", + "Docker:193.5233.140" + ) + ), + ProductCode.IU to ProductProfile( + sdkVersion = "IU-2019.3", + plugins = listOf( + "org.jetbrains.plugins.terminal", + "Pythonid:193.5233.109", + "org.jetbrains.plugins.yaml", + "JavaScript", + "JavaScriptDebugger" + ) + ), + ProductCode.RD to RiderProfile( + sdkVersion = "RD-2019.3.4", + rdGenVersion = "0.193.146", + nugetVersion = "2019.3.4", + plugins = listOf( + "org.jetbrains.plugins.yaml" + ) + ) + ) + ), + "2020.1" to Profile( + sinceVersion = "201", + untilVersion = "201.*", + products = mapOf( + ProductCode.IC to ProductProfile( + sdkVersion = "IC-2020.1", + plugins = listOf( + "org.jetbrains.plugins.terminal", + "org.jetbrains.plugins.yaml", + "PythonCore:201.6668.31", + "java", + "com.intellij.gradle", + "org.jetbrains.idea.maven", + "Docker:201.6668.30" + ) + ), + ProductCode.IU to ProductProfile( + sdkVersion = "IU-2020.1", + plugins = listOf( + "org.jetbrains.plugins.terminal", + "Pythonid:201.6668.31", + "org.jetbrains.plugins.yaml", + "JavaScript", + "JavaScriptDebugger", + "com.intellij.database" + ) + ), + ProductCode.RD to RiderProfile( + sdkVersion = "RD-2020.1.0", + rdGenVersion = "0.201.69", + nugetVersion = "2020.1.0", + plugins = listOf( + "org.jetbrains.plugins.yaml" + ) + ) + ) + ), + "2020.2" to Profile( + sinceVersion = "202", + untilVersion = "202.*", + products = mapOf( + ProductCode.IC to ProductProfile( + sdkVersion = "IC-2020.2", + plugins = listOf( + "org.jetbrains.plugins.terminal", + "org.jetbrains.plugins.yaml", + "PythonCore:202.6397.124", + "java", + "com.intellij.gradle", + "org.jetbrains.idea.maven", + "Docker:202.6397.93" + ) + ), + ProductCode.IU to ProductProfile( + sdkVersion = "IU-2020.2", + plugins = listOf( + "org.jetbrains.plugins.terminal", + "Pythonid:202.6397.98", + "org.jetbrains.plugins.yaml", + "JavaScript", + "JavaScriptDebugger", + "com.intellij.database" + ) + ), + ProductCode.RD to RiderProfile( + sdkVersion = "RD-2020.2", + rdGenVersion = "0.202.113", + nugetVersion = "2020.2.0", + plugins = listOf( + "org.jetbrains.plugins.yaml" + ) + ) + ) + ) + ) + + fun sinceVersion(): String = getProfile().sinceVersion + fun untilVersion() = getProfile().untilVersion + + fun sdkVersion(code: ProductCode): String = getProductProfile(code).sdkVersion + fun plugins(code: ProductCode): List = getProductProfile(code).plugins + + fun rdGenVersion(): String = getRiderProfile().rdGenVersion + fun nugetSdkVersion(): String = getRiderProfile().nugetVersion + + // Convert (as an example) 2020.2 -> 202 + fun resolveShortenedIdeProfileName(): String { + val profileName = resolveIdeProfileName().trim() + val parts = profileName.split(".") + return parts[0].substring(2) + parts[1] + } + + fun ideSdkVersion(code: ProductCode): String = ideProfiles[resolveIdeProfileName()] + ?.products + ?.get(code) + ?.sdkVersion + ?: throw IllegalArgumentException("Product not in map of IDE versions: ${resolveIdeProfileName()}, $code") + + fun resolveIdeProfileName(): String = if (System.getenv()["ALTERNATIVE_IDE_PROFILE_NAME"] != null) { + System.getenv("ALTERNATIVE_IDE_PROFILE_NAME") + } else { + project.properties["ideProfileName"]?.toString() ?: throw IllegalStateException("No ideProfileName property set") + } + + private fun getProfile(): Profile = + ideProfiles[resolveIdeProfileName()] ?: throw IllegalStateException("Unable to resolve profile ${resolveIdeProfileName()}") + + private fun getProductProfile(code: ProductCode): ProductProfile = + ideProfiles[resolveIdeProfileName()]?.products?.get(code) + ?: throw IllegalStateException("Unable to get profile ${resolveIdeProfileName()} code $code") + + private fun getRiderProfile(): RiderProfile = ideProfiles[resolveIdeProfileName()]?.products?.get(ProductCode.RD) as? RiderProfile + ?: throw IllegalStateException("Failed to get Rider profile for ${resolveIdeProfileName()}!") +} diff --git a/buildSrc/src/SourcesUtils.kt b/buildSrc/src/software/aws/toolkits/gradle/SourcesUtils.kt similarity index 91% rename from buildSrc/src/SourcesUtils.kt rename to buildSrc/src/software/aws/toolkits/gradle/SourcesUtils.kt index d34e6e182a..a0acc41769 100644 --- a/buildSrc/src/SourcesUtils.kt +++ b/buildSrc/src/software/aws/toolkits/gradle/SourcesUtils.kt @@ -1,8 +1,10 @@ // Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -@file:JvmName("SourceUtils") + +package software.aws.toolkits.gradle import org.gradle.api.Project +import java.io.File import java.io.FileFilter /** @@ -12,9 +14,9 @@ import java.io.FileFilter * [type] is the type of the source folder (e.g. 'src', 'tst', 'resources') * [ideVersion] is the 3 digit numerical version of the JetBrains SDK (e.g. 192, 201 etc) */ -fun findFolders(project: Project, type: String, ideVersion: String): List = project.projectDir.listFiles(FileFilter { +fun findFolders(project: Project, type: String, ideVersion: String): Set = project.projectDir.listFiles(FileFilter { it.isDirectory && includeFolder(type, ideVersion, it.name) -})?.map { it.name } ?: emptyList() +})?.map { File(it.name) }?.toSet() ?: setOf() /** * Determines if a folder should be included based on the ideVersion being targeted diff --git a/buildSrc/src/software/aws/toolkits/gradle/resources/ValidateMessages.kt b/buildSrc/src/software/aws/toolkits/gradle/resources/ValidateMessages.kt index 7e63e6d2cd..692966840c 100644 --- a/buildSrc/src/software/aws/toolkits/gradle/resources/ValidateMessages.kt +++ b/buildSrc/src/software/aws/toolkits/gradle/resources/ValidateMessages.kt @@ -13,7 +13,7 @@ import java.io.File open class ValidateMessages : DefaultTask() { @InputFiles - val paths: ListProperty = project.objects.listProperty(File::class.java) + val paths: ListProperty = project.objects.listProperty(String::class.java) init { group = VERIFICATION_GROUP @@ -24,6 +24,7 @@ open class ValidateMessages : DefaultTask() { var hasError = false paths .get() + .map { File(it) } .map { it.absolutePath to it.readLines() }.forEach { (filePath, fileLines) -> diff --git a/intellijJVersions.gradle b/intellijJVersions.gradle deleted file mode 100644 index 5a1852e263..0000000000 --- a/intellijJVersions.gradle +++ /dev/null @@ -1,207 +0,0 @@ -static def ideProfiles() { - return [ - "2019.3": [ - "sinceVersion": "193", - "untilVersion": "193.*", - "products" : [ - "IC": [ - sdkVersion: "IC-2019.3", - plugins : [ - "org.jetbrains.plugins.terminal", - "org.jetbrains.plugins.yaml", - "PythonCore:193.5233.139", - "java", - "com.intellij.gradle", - "org.jetbrains.idea.maven", - "Docker:193.5233.140" - ] - ], - "IU": [ - sdkVersion: "IU-2019.3", - plugins : [ - "org.jetbrains.plugins.terminal", - "Pythonid:193.5233.109", - "org.jetbrains.plugins.yaml", - "JavaScript", - "JavaScriptDebugger", - ] - ], - "RD": [ - sdkVersion : "RD-2019.3.4", - rdGenVersion: "0.193.146", - nugetVersion: "2019.3.4", - plugins : [ - "org.jetbrains.plugins.yaml" - ] - ] - ] - ], - "2020.1": [ - "sinceVersion": "201", - "untilVersion": "201.*", - "products" : [ - "IC": [ - sdkVersion: "IC-2020.1", - plugins : [ - "org.jetbrains.plugins.terminal", - "org.jetbrains.plugins.yaml", - "PythonCore:201.6668.31", - "java", - "com.intellij.gradle", - "org.jetbrains.idea.maven", - "Docker:201.6668.30" - ] - ], - "IU": [ - sdkVersion: "IU-2020.1", - plugins : [ - "org.jetbrains.plugins.terminal", - "Pythonid:201.6668.31", - "org.jetbrains.plugins.yaml", - "JavaScript", - "JavaScriptDebugger", - "com.intellij.database", - ] - ], - "RD": [ - sdkVersion : "RD-2020.1.0", - rdGenVersion: "0.201.69", - nugetVersion: "2020.1.0", - plugins : [ - "org.jetbrains.plugins.yaml" - ] - ] - ] - ], - "2020.2": [ - "sinceVersion": "202", - "untilVersion": "202.*", - "products" : [ - "IC": [ - sdkVersion: "IC-2020.2", - plugins : [ - "org.jetbrains.plugins.terminal", - "org.jetbrains.plugins.yaml", - "PythonCore:202.6397.124", - "java", - "com.intellij.gradle", - "org.jetbrains.idea.maven", - "Docker:202.6397.93" - ] - ], - "IU": [ - sdkVersion: "IU-2020.2", - plugins : [ - "org.jetbrains.plugins.terminal", - "Pythonid:202.6397.98", - "org.jetbrains.plugins.yaml", - "JavaScript", - "JavaScriptDebugger", - "com.intellij.database", - ] - ], - "RD": [ - sdkVersion : "RD-2020.2", - rdGenVersion: "0.202.113", - nugetVersion: "2020.2.0", - plugins : [ - "org.jetbrains.plugins.yaml" - ] - ] - ] - ] - ] -} - -def idePlugins(String productCode) { - return ideProduct(productCode).plugins -} - -def ideSdkVersion(String productCode) { - return ideProduct(productCode).sdkVersion -} - -private def ideProduct(String productCode) { - def product = ideProfile()["products"][productCode] - if (product == null) { - throw new IllegalArgumentException("Unknown IDE product `$productCode` for ${resolveIdeProfileName()}") - } - return product -} - -def ideSinceVersion() { - def guiVersion = ideProfile()["sinceVersion"] - if (guiVersion == null) { - throw new IllegalArgumentException("Missing 'sinceVersion' key for ${resolveIdeProfileName()}") - } - return guiVersion -} - -def ideUntilVersion() { - def guiVersion = ideProfile()["untilVersion"] - if (guiVersion == null) { - throw new IllegalArgumentException("Missing 'untilVersion' key for ${resolveIdeProfileName()}") - } - return guiVersion -} - -// https://www.myget.org/feed/rd-snapshots/package/maven/com.jetbrains.rd/rd-gen -def rdGenVersion() { - def rdGen = ideProduct("RD").rdGenVersion - if (rdGen == null) { - throw new IllegalArgumentException("Missing 'rdGenVersion' in 'RD' product for ${resolveIdeProfileName()}") - } - return rdGen -} - -// https://www.nuget.org/packages/JetBrains.Rider.SDK/ -def riderNugetSdkVersion() { - def rdGen = ideProduct("RD").nugetVersion - if (rdGen == null) { - throw new IllegalArgumentException("Missing 'nugetVersion' in 'RD' product for ${resolveIdeProfileName()}") - } - return rdGen -} - -def ideProfile() { - def profileName = resolveIdeProfileName() - def profile = ideProfiles()[profileName] - if (profile == null) { - throw new IllegalArgumentException("Unknown ideProfile `$profileName`") - } - - return profile -} - -def resolveIdeProfileName() { - if (System.env.ALTERNATIVE_IDE_PROFILE_NAME) { - return System.env.ALTERNATIVE_IDE_PROFILE_NAME - } - - return project.ideProfileName -} - - -static def shortenVersion(String ver) { - try { - def result = ver =~ /^\d\d(\d{2})[\\.](\d)/ - if (result) { - return result.group(1) + result.group(2) - } - } catch (Exception ignored) { - } - return ver -} - -ext { - ideProfiles = this.&ideProfiles - idePlugins = this.&idePlugins - ideSdkVersion = this.&ideSdkVersion - ideSinceVersion = this.&ideSinceVersion - ideUntilVersion = this.&ideUntilVersion - ideProfile = this.&ideProfile - rdGenVersion = this.&rdGenVersion - riderNugetSdkVersion = this.&riderNugetSdkVersion - resolveIdeProfileName = this.&resolveIdeProfileName - shortenVersion = this.&shortenVersion -} diff --git a/jetbrains-core/build.gradle.kts b/jetbrains-core/build.gradle.kts index 504f2a61dc..fa481a6a19 100644 --- a/jetbrains-core/build.gradle.kts +++ b/jetbrains-core/build.gradle.kts @@ -1,16 +1,16 @@ // Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import groovy.lang.Closure import org.jetbrains.intellij.tasks.PatchPluginXmlTask import org.jetbrains.kotlin.gradle.tasks.KotlinCompile import software.aws.toolkits.telemetry.generator.gradle.GenerateTelemetry import software.aws.toolkits.gradle.changelog.tasks.GeneratePluginChangeLog +import software.aws.toolkits.gradle.IdeVersions +import software.aws.toolkits.gradle.ProductCode plugins { id("org.jetbrains.intellij") } -apply(from = "../intellijJVersions.gradle") buildscript { val telemetryVersion: String by project @@ -23,29 +23,27 @@ buildscript { } } +val ideVersions = IdeVersions(project) val telemetryVersion: String by project val awsSdkVersion: String by project val coroutinesVersion: String by project -val ideSdkVersion: Closure by ext -val idePlugins: Closure> by ext -val ideSinceVersion: Closure by ext -val ideUntilVersion: Closure by ext - val compileKotlin: KotlinCompile by tasks -val patchPluginXml: PatchPluginXmlTask by tasks intellij { val rootIntelliJTask = rootProject.intellij - version = ideSdkVersion("IC") - setPlugins(*(idePlugins("IC").toArray())) + version = ideVersions.sdkVersion(ProductCode.IC) + setPlugins(*ideVersions.plugins(ProductCode.IC).toTypedArray()) pluginName = rootIntelliJTask.pluginName updateSinceUntilBuild = rootIntelliJTask.updateSinceUntilBuild downloadSources = rootIntelliJTask.downloadSources + } -patchPluginXml.setSinceBuild(ideSinceVersion()) -patchPluginXml.setUntilBuild(ideUntilVersion()) +tasks.patchPluginXml { + setSinceBuild(ideVersions.sinceVersion()) + setUntilBuild(ideVersions.untilVersion()) +} configurations { testArtifacts @@ -58,7 +56,9 @@ val generateTelemetry = tasks.register("generateTelemetry") { compileKotlin.dependsOn(generateTelemetry) sourceSets { - main.get().java.srcDir("${project.buildDir}/generated-src") + main { + java.srcDir("${project.buildDir}/generated-src") + } } tasks.test { diff --git a/jetbrains-rider/build.gradle.kts b/jetbrains-rider/build.gradle.kts index 84813ce4bf..d02fb81ffd 100644 --- a/jetbrains-rider/build.gradle.kts +++ b/jetbrains-rider/build.gradle.kts @@ -4,11 +4,11 @@ import com.jetbrains.rd.generator.gradle.RdgenParams import com.jetbrains.rd.generator.gradle.RdgenTask import org.jetbrains.intellij.tasks.PrepareSandboxTask +import software.aws.toolkits.gradle.IdeVersions +import software.aws.toolkits.gradle.ProductCode buildscript { - val rdGenVersion: groovy.lang.Closure by project - val rdversion = rdGenVersion() - project.extra["rd_version"] = rdversion + val rdversion = software.aws.toolkits.gradle.IdeVersions(project).rdGenVersion() logger.info("Using rd-gen: $rdversion") @@ -28,12 +28,7 @@ plugins { apply(plugin = "com.jetbrains.rdgen") -// IntellijVerison things -val rdGenVersion: groovy.lang.Closure by project -val ideSdkVersion: groovy.lang.Closure by project -val riderNugetSdkVersion: groovy.lang.Closure by project -val resolveIdeProfileName: groovy.lang.Closure by project -val idePlugins: groovy.lang.Closure> by project +val ideVersions = IdeVersions(project) val resharperPluginPath = File(projectDir, "ReSharper.AWS") val resharperBuildPath = File(project.buildDir, "dotnetBuild") @@ -56,13 +51,13 @@ rdgenDir.mkdirs() intellij { val parentIntellijTask = rootProject.intellij - version = ideSdkVersion("RD") + version = ideVersions.sdkVersion(ProductCode.RD) pluginName = parentIntellijTask.pluginName updateSinceUntilBuild = parentIntellijTask.updateSinceUntilBuild // Workaround for https://youtrack.jetbrains.com/issue/IDEA-179607 val extraPlugins = arrayOf("rider-plugins-appender") - setPlugins(*(idePlugins("RD") + extraPlugins).toTypedArray()) + setPlugins(*(ideVersions.plugins(ProductCode.RD) + extraPlugins).toTypedArray()) // Disable downloading source to avoid issues related to Rider SDK naming that is missed in Idea // snapshots repository. The task is failed because if is unable to find related IC sources. @@ -74,7 +69,7 @@ val generateDaemonModel = tasks.register("generateDaemonModel") { val daemonModelSource = File(modelDir, "daemon").canonicalPath val ktOutput = File(riderGeneratedSources, "DaemonProtocol") - inputs.property("rdgen", rdGenVersion()) + inputs.property("rdgen", ideVersions.rdGenVersion()) inputs.dir(daemonModelSource) outputs.dirs(ktOutput, csDaemonGeneratedOutput) @@ -118,7 +113,7 @@ val generatePsiModel = tasks.register("generatePsiModel") { val psiModelSource = File(modelDir, "psi").canonicalPath val ktOutput = File(riderGeneratedSources, "PsiProtocol") - inputs.property("rdgen", rdGenVersion()) + inputs.property("rdgen", ideVersions.rdGenVersion()) inputs.dir(psiModelSource) outputs.dirs(ktOutput, csPsiGeneratedOutput) @@ -162,7 +157,7 @@ val generateAwsSettingModel = tasks.register("generateAwsSettingModel val settingModelSource = File(modelDir, "setting").canonicalPath val ktOutput = File(riderGeneratedSources, "AwsSettingsProtocol") - inputs.property("rdgen", rdGenVersion()) + inputs.property("rdgen", ideVersions.rdGenVersion()) inputs.dir(settingModelSource) outputs.dirs(ktOutput, csAwsSettingGeneratedOutput) @@ -205,7 +200,7 @@ val generateAwsProjectModel = tasks.register("generateAwsProjectModel val projectModelSource = File(modelDir, "project").canonicalPath val ktOutput = File(riderGeneratedSources, "AwsProjectProtocol") - inputs.property("rdgen", rdGenVersion()) + inputs.property("rdgen", ideVersions.rdGenVersion()) inputs.dir(projectModelSource) outputs.dirs(ktOutput, csAwsProjectGeneratedOutput) @@ -271,11 +266,11 @@ val prepareBuildProps = tasks.register("prepareBuildProps") { val riderSdkVersionPropsPath = File(resharperPluginPath, "RiderSdkPackageVersion.props") group = backendGroup - inputs.property("riderNugetSdkVersion", riderNugetSdkVersion()) + inputs.property("riderNugetSdkVersion", ideVersions.nugetSdkVersion()) outputs.file(riderSdkVersionPropsPath) doLast { - val riderSdkVersion = riderNugetSdkVersion() + val riderSdkVersion = ideVersions.nugetSdkVersion() val configText = """ [$riderSdkVersion] @@ -291,7 +286,7 @@ val prepareNuGetConfig = tasks.register("prepareNuGetConfig") { val nugetConfigPath = File(projectDir, "NuGet.Config") - inputs.property("rdVersion", ideSdkVersion("RD")) + inputs.property("rdVersion",ideVersions.sdkVersion(ProductCode.RD)) outputs.file(nugetConfigPath) doLast { @@ -326,7 +321,7 @@ val buildReSharperPlugin = tasks.register("buildReSharperPlugin") { val arguments = listOf( "build", "${resharperPluginPath.canonicalPath}/ReSharper.AWS.sln", - "/p:DefineConstants=\"PROFILE_${resolveIdeProfileName().replace(".", "_")}\"" + "/p:DefineConstants=\"PROFILE_${ideVersions.resolveIdeProfileName().replace(".", "_")}\"" ) exec { executable = "dotnet" @@ -362,7 +357,9 @@ dependencies { } sourceSets { - main.get().java.srcDirs("$buildDir/generated-src") + main{ + java.srcDirs("$buildDir/generated-src") + } } val resharperParts = listOf( diff --git a/jetbrains-ultimate/build.gradle.kts b/jetbrains-ultimate/build.gradle.kts index d670c8e544..176592cec5 100644 --- a/jetbrains-ultimate/build.gradle.kts +++ b/jetbrains-ultimate/build.gradle.kts @@ -1,16 +1,13 @@ // Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import groovy.lang.Closure import org.jetbrains.intellij.IntelliJPluginExtension +import software.aws.toolkits.gradle.IdeVersions +import software.aws.toolkits.gradle.ProductCode plugins { id("org.jetbrains.intellij") } -apply(from = "../intellijJVersions.gradle") - -val ideSdkVersion: Closure by ext -val idePlugins: Closure> by ext dependencies { api(project(":jetbrains-core")) @@ -19,10 +16,12 @@ dependencies { integrationTestImplementation(project(path = ":jetbrains-core", configuration = "testArtifacts")) } +val ideVersions = IdeVersions(project) + intellij { val parentIntellijTask = rootProject.intellij - version = ideSdkVersion("IU") - setPlugins(*(idePlugins("IU").toArray())) + version = ideVersions.sdkVersion(ProductCode.IU) + setPlugins(*ideVersions.plugins(ProductCode.IU).toTypedArray()) pluginName = parentIntellijTask.pluginName updateSinceUntilBuild = parentIntellijTask.updateSinceUntilBuild downloadSources = parentIntellijTask.downloadSources diff --git a/resources/build.gradle.kts b/resources/build.gradle.kts index efc200df59..d995965a99 100644 --- a/resources/build.gradle.kts +++ b/resources/build.gradle.kts @@ -8,7 +8,9 @@ plugins { } sourceSets { - main.get().resources.srcDir("$buildDir/downloaded-resources") + main { + resources.srcDir("$buildDir/downloaded-resources") + } } val download = tasks.register("downloadResources") { diff --git a/telemetry-client/build.gradle.kts b/telemetry-client/build.gradle.kts index 2e94862386..89a1dcf017 100644 --- a/telemetry-client/build.gradle.kts +++ b/telemetry-client/build.gradle.kts @@ -14,7 +14,9 @@ dependencies { val generatedSources = "$buildDir/generated-src" sourceSets { - main.get().java.srcDir(generatedSources) + main { + java.srcDir(generatedSources) + } } idea { diff --git a/ui-tests/build.gradle.kts b/ui-tests/build.gradle.kts index 15bbd2341e..edd59c207c 100644 --- a/ui-tests/build.gradle.kts +++ b/ui-tests/build.gradle.kts @@ -29,7 +29,9 @@ dependencies { } // don't run gui tests as part of check -tasks["test"].enabled = false +tasks.test { + enabled = false +} tasks.register("uiTestCore") { // we don't want to cache the results of this. From f09e8275e809c05f40508986a3f2c9ced76439f9 Mon Sep 17 00:00:00 2001 From: Austin Brooks Date: Tue, 18 Aug 2020 11:31:09 -0700 Subject: [PATCH 0023/1546] Add edit creds corrective action to the explorer (#1992) --- .../core/credentials/AwsConnectionManager.kt | 9 +++++-- .../ChangeConnectionSettingsMenu.kt | 24 +++++++++++-------- 2 files changed, 21 insertions(+), 12 deletions(-) diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/core/credentials/AwsConnectionManager.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/core/credentials/AwsConnectionManager.kt index 548d6bea14..ade8cf5a56 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/core/credentials/AwsConnectionManager.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/core/credentials/AwsConnectionManager.kt @@ -3,6 +3,7 @@ package software.aws.toolkits.jetbrains.core.credentials +import com.intellij.openapi.actionSystem.ActionManager import com.intellij.openapi.actionSystem.AnAction import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.components.ServiceManager @@ -242,6 +243,8 @@ abstract class AwsConnectionManager(private val project: Project) : SimpleModifi * state is temporary in the 'connection validation' workflow or if this is a terminal state. */ sealed class ConnectionState(val displayMessage: String, val isTerminal: Boolean) { + protected val editCredsAction: AnAction = ActionManager.getInstance().getAction("aws.settings.upsertCredentials") + /** * An optional short message to display in places where space is at a premium */ @@ -268,13 +271,15 @@ sealed class ConnectionState(val displayMessage: String, val isTerminal: Boolean else -> throw IllegalArgumentException("At least one of regionId ($region) or toolkitCredentialsIdentifier ($credentials) must be null") }, isTerminal = true - ) + ) { + override val actions: List = listOf(editCredsAction) + } class InvalidConnection(private val cause: Exception) : ConnectionState(message("settings.states.invalid", ExceptionUtil.getMessage(cause) ?: ExceptionUtil.getThrowableText(cause)), isTerminal = true) { override val shortMessage = message("settings.states.invalid.short") - override val actions = listOf(RefreshConnectionAction(message("settings.retry"))) + override val actions: List = listOf(RefreshConnectionAction(message("settings.retry")), editCredsAction) } class RequiresUserAction(interactiveCredentials: InteractiveCredential) : diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/core/credentials/ChangeConnectionSettingsMenu.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/core/credentials/ChangeConnectionSettingsMenu.kt index 4bc1d93786..e931fab036 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/core/credentials/ChangeConnectionSettingsMenu.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/core/credentials/ChangeConnectionSettingsMenu.kt @@ -34,7 +34,7 @@ class ChangeAccountSettingsActionGroup(project: Project, private val mode: Chang accountSettingsManager, ChangePartitionActionGroup(accountSettingsManager) ) - private val credentialSelector = ChangeCredentialsActionGroup(true) + private val credentialSelector = ChangeCredentialsActionGroup() override fun createChildrenProvider(actionManager: ActionManager?): CachedValueProvider> = CachedValueProvider { val actions = mutableListOf() @@ -59,8 +59,6 @@ class ChangeAccountSettingsActionGroup(project: Project, private val mode: Chang val usedCredentials = accountSettingsManager.recentlyUsedCredentials() if (usedCredentials.isEmpty()) { actions.add(Separator.create(message("settings.credentials"))) - - credentialSelector.isPopup = false actions.add(credentialSelector) } else { actions.add(Separator.create(message("settings.credentials.recent"))) @@ -68,13 +66,20 @@ class ChangeAccountSettingsActionGroup(project: Project, private val mode: Chang actions.add(ChangeCredentialsAction(it)) } - credentialSelector.isPopup = true - actions.add(credentialSelector) + val allCredentials = DefaultActionGroup(message("settings.credentials.profile_sub_menu"), true) + allCredentials.add(credentialSelector) + allCredentials.add(Separator.create()) + allCredentials.add(ActionManager.getInstance().getAction("aws.settings.upsertCredentials")) + + actions.add(allCredentials) } } - actions.add(Separator.create()) - actions.addAll(accountSettingsManager.connectionState.actions) + // Both mode == status bar version + if (mode == BOTH) { + actions.add(Separator.create()) + actions.addAll(accountSettingsManager.connectionState.actions) + } CachedValueProvider.Result.create(actions.toTypedArray(), accountSettingsManager) } @@ -89,7 +94,7 @@ enum class ChangeAccountSettingsMode( BOTH(true, true) } -private class ChangeCredentialsActionGroup(popup: Boolean) : ComputableActionGroup(message("settings.credentials.profile_sub_menu"), popup), DumbAware { +private class ChangeCredentialsActionGroup : ComputableActionGroup(), DumbAware { override fun createChildrenProvider(actionManager: ActionManager?): CachedValueProvider> = CachedValueProvider { val credentialManager = CredentialManager.getInstance() @@ -97,8 +102,6 @@ private class ChangeCredentialsActionGroup(popup: Boolean) : ComputableActionGro credentialManager.getCredentialIdentifiers().forEach { actions.add(ChangeCredentialsAction(it)) } - actions.add(Separator.create()) - actions.add(ActionManager.getInstance().getAction("aws.settings.upsertCredentials")) CachedValueProvider.Result.create(actions.toTypedArray(), credentialManager) } @@ -123,6 +126,7 @@ internal class ChangeRegionActionGroup( name: String = message("settings.regions.region_sub_menu") ) : ComputableActionGroup(name, true), DumbAware { private val regionProvider = AwsRegionProvider.getInstance() + override fun createChildrenProvider(actionManager: ActionManager?): CachedValueProvider> = CachedValueProvider { val (regionMap, partitionGroup) = partition?.let { // if a partition has been selected, only show regions in that partition From d6626f94a51a40626f7d910b3979f9eb98bb0b5c Mon Sep 17 00:00:00 2001 From: Austin Brooks Date: Wed, 19 Aug 2020 13:06:49 -0700 Subject: [PATCH 0024/1546] Improve rendering performance of wrapped text in CW logs table (#1996) --- ...-70aef423-f200-4774-912a-6af2af8080db.json | 4 ++ .../cloudformation/stack/DynamicTableView.kt | 10 ++-- .../cloudwatch/logs/LogStreamEntry.kt | 4 +- .../toolkits/jetbrains/utils/ui/UiUtils.kt | 46 +++++++-------- .../cloudwatch/logs/TableUtilsTest.kt | 56 ------------------- .../cloudwatch/logs/WrapLogsActionTest.kt | 16 ++++-- 6 files changed, 45 insertions(+), 91 deletions(-) create mode 100644 .changes/next-release/bugfix-70aef423-f200-4774-912a-6af2af8080db.json delete mode 100644 jetbrains-core/tst/software/aws/toolkits/jetbrains/services/cloudwatch/logs/TableUtilsTest.kt diff --git a/.changes/next-release/bugfix-70aef423-f200-4774-912a-6af2af8080db.json b/.changes/next-release/bugfix-70aef423-f200-4774-912a-6af2af8080db.json new file mode 100644 index 0000000000..03bed9f614 --- /dev/null +++ b/.changes/next-release/bugfix-70aef423-f200-4774-912a-6af2af8080db.json @@ -0,0 +1,4 @@ +{ + "type" : "bugfix", + "description" : "Improved rendering speed of wrapped text in CloudWatch logs and CloudFormation events tables" +} \ No newline at end of file diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudformation/stack/DynamicTableView.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudformation/stack/DynamicTableView.kt index d0f1415b02..4a48b260b1 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudformation/stack/DynamicTableView.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudformation/stack/DynamicTableView.kt @@ -6,8 +6,8 @@ import com.intellij.ui.components.JBScrollPane import com.intellij.ui.table.JBTable import javax.swing.JComponent import javax.swing.SwingUtilities -import javax.swing.table.DefaultTableCellRenderer import javax.swing.table.DefaultTableModel +import javax.swing.table.TableCellRenderer class DynamicTableView(private vararg val fields: Field) : View { private val model = object : DefaultTableModel(fields.map(Field::readableName).toTypedArray(), 0) { @@ -19,9 +19,9 @@ class DynamicTableView(private vararg val fields: Field) : View { autoscrolls = true setShowColumns(true) setPaintBusy(true) - fields.forEach { - when (val renderer = it.renderer) { - is DefaultTableCellRenderer -> getColumn(it.readableName).cellRenderer = renderer + fields.forEach { field -> + field.renderer?.let { + getColumn(field.readableName).cellRenderer = it } } } @@ -45,7 +45,7 @@ class DynamicTableView(private vararg val fields: Field) : View { data class Field( val readableName: String, - val renderer: DefaultTableCellRenderer? = null, + val renderer: TableCellRenderer? = null, val getData: (T) -> Any? ) } diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/LogStreamEntry.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/LogStreamEntry.kt index c6729192b8..f156b10428 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/LogStreamEntry.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/LogStreamEntry.kt @@ -8,5 +8,5 @@ import software.amazon.awssdk.services.cloudwatchlogs.model.OutputLogEvent data class LogStreamEntry(val message: String, val timestamp: Long) -fun OutputLogEvent.toLogStreamEntry() = LogStreamEntry(message() ?: "", timestamp() ?: 0) -fun FilteredLogEvent.toLogStreamEntry() = LogStreamEntry(message() ?: "", timestamp() ?: 0) +fun OutputLogEvent.toLogStreamEntry() = LogStreamEntry(message()?.trim() ?: "", timestamp() ?: 0) +fun FilteredLogEvent.toLogStreamEntry() = LogStreamEntry(message()?.trim() ?: "", timestamp() ?: 0) 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 497d33f2a9..fcaeb0b950 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 @@ -9,11 +9,11 @@ import com.intellij.openapi.application.runWriteAction import com.intellij.openapi.command.CommandProcessor import com.intellij.openapi.ui.GraphicsConfig import com.intellij.openapi.ui.ValidationInfo +import com.intellij.ui.CellRendererPanel 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.JBTextArea import com.intellij.ui.paint.LinePainter2D import com.intellij.ui.speedSearch.SpeedSearchSupply @@ -32,12 +32,11 @@ import java.awt.geom.RoundRectangle2D import javax.swing.AbstractButton import javax.swing.JComboBox import javax.swing.JComponent -import javax.swing.JLabel import javax.swing.JTable import javax.swing.JTextArea import javax.swing.JTextField import javax.swing.ListModel -import javax.swing.table.DefaultTableCellRenderer +import javax.swing.table.TableCellRenderer import javax.swing.text.Highlighter import javax.swing.text.JTextComponent @@ -169,31 +168,34 @@ private fun JTextArea.speedSearchHighlighter(speedSearchEnabledComponent: JCompo } } -class WrappingCellRenderer(private val wrapOnSelection: Boolean, private val toggleableWrap: Boolean) : DefaultTableCellRenderer() { +class WrappingCellRenderer(private val wrapOnSelection: Boolean, private val toggleableWrap: Boolean) : CellRendererPanel(), TableCellRenderer { var wrap: Boolean = false - // JBTextArea has a different font from JBLabel (the default in a table) so harvest the font off of it - private val jLabelFont = JBLabel().font + private val textArea = JBTextArea() + + init { + textArea.font = UIUtil.getLabelFont() + textArea.wrapStyleWord = true + + add(textArea) + } override fun getTableCellRendererComponent(table: JTable?, value: Any?, isSelected: Boolean, hasFocus: Boolean, row: Int, column: Int): Component { - val defaultComponent = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column) - table ?: return defaultComponent - val component = JBTextArea() - - component.border = (defaultComponent as? JLabel)?.border ?: JBUI.Borders.empty(2, 2) - component.wrapStyleWord = (wrapOnSelection && isSelected) || (toggleableWrap && wrap) - component.lineWrap = (wrapOnSelection && isSelected) || (toggleableWrap && wrap) - component.font = jLabelFont - component.text = (value as? String)?.trim() - component.setSelectionHighlighting(table, isSelected) - - component.setSize(table.columnModel.getColumn(column).width, component.preferredSize.height) - if (table.getRowHeight(row) != component.preferredSize.height) { - table.setRowHeight(row, component.preferredSize.height) + if (table == null) { + return this + } + + textArea.lineWrap = (wrapOnSelection && isSelected) || (toggleableWrap && wrap) + textArea.text = (value as? String) ?: "" + textArea.setSelectionHighlighting(table, isSelected) + + setSize(table.columnModel.getColumn(column).width, preferredSize.height) + if (table.getRowHeight(row) != preferredSize.height) { + table.setRowHeight(row, preferredSize.height) } - component.speedSearchHighlighter(table) + textArea.speedSearchHighlighter(table) - return component + return this } } diff --git a/jetbrains-core/tst/software/aws/toolkits/jetbrains/services/cloudwatch/logs/TableUtilsTest.kt b/jetbrains-core/tst/software/aws/toolkits/jetbrains/services/cloudwatch/logs/TableUtilsTest.kt deleted file mode 100644 index 3f0feefc84..0000000000 --- a/jetbrains-core/tst/software/aws/toolkits/jetbrains/services/cloudwatch/logs/TableUtilsTest.kt +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -package software.aws.toolkits.jetbrains.services.cloudwatch.logs - -import com.intellij.ui.components.JBTextArea -import com.intellij.ui.table.TableView -import com.intellij.util.ui.ListTableModel -import org.assertj.core.api.Assertions.assertThat -import org.junit.Test -import software.amazon.awssdk.services.cloudwatchlogs.model.OutputLogEvent -import software.aws.toolkits.jetbrains.services.cloudwatch.logs.editor.LogStreamDateColumn -import software.aws.toolkits.jetbrains.services.cloudwatch.logs.editor.LogStreamMessageColumn - -class TableUtilsTest { - private val event = OutputLogEvent.builder().message("aaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaa").build().toLogStreamEntry() - - @Test - fun wrappingColumnMakesTallComponent() { - val model = ListTableModel(LogStreamMessageColumn()) - model.addRow(event) - val table = TableView(model) - val renderer = LogStreamMessageColumn() - renderer.wrap() - val wrappingRenderer = renderer.getRenderer(event) - val wrappingComponent = wrappingRenderer?.getTableCellRendererComponent(table, event.message, true, true, 0, 0) - assertThat(wrappingComponent).isInstanceOf(JBTextArea::class.java) - assertThat(wrappingComponent?.height).isNotZero() - assertThat(wrappingComponent?.preferredSize?.height).isGreaterThan(wrappingComponent?.height) - } - - @Test - fun normalRendererDoesNotWrap() { - val model = ListTableModel(LogStreamMessageColumn()) - model.addRow(event) - val table = TableView(model) - val renderer = table.getDefaultRenderer(LogStreamMessageColumn::class.java) - val component = renderer?.getTableCellRendererComponent(table, event.message, true, true, 0, 0) - assertThat(component?.height).isZero() - assertThat(component?.preferredSize?.height).isGreaterThan(10) - } - - @Test - fun componentIsHighlighedOnSelection() { - val model = ListTableModel(LogStreamDateColumn(), LogStreamMessageColumn()) - val table = TableView(model) - val selectedComponent = model.columnInfos[1].getRenderer(null)!!.getTableCellRendererComponent(table, 0, true, false, 0, 0) - assertThat(selectedComponent).isInstanceOf(JBTextArea::class.java) - assertThat(selectedComponent.background).isEqualTo(table.selectionBackground) - assertThat(selectedComponent.foreground).isEqualTo(table.selectionForeground) - val component = model.columnInfos[1].getRenderer(null)!!.getTableCellRendererComponent(table, 0, false, false, 0, 0) - assertThat(component).isInstanceOf(JBTextArea::class.java) - assertThat(component.background).isEqualTo(table.background) - assertThat(component.foreground).isEqualTo(table.foreground) - } -} diff --git a/jetbrains-core/tst/software/aws/toolkits/jetbrains/services/cloudwatch/logs/WrapLogsActionTest.kt b/jetbrains-core/tst/software/aws/toolkits/jetbrains/services/cloudwatch/logs/WrapLogsActionTest.kt index 9ec178bc1d..221e24e5e1 100644 --- a/jetbrains-core/tst/software/aws/toolkits/jetbrains/services/cloudwatch/logs/WrapLogsActionTest.kt +++ b/jetbrains-core/tst/software/aws/toolkits/jetbrains/services/cloudwatch/logs/WrapLogsActionTest.kt @@ -14,6 +14,7 @@ import org.junit.Test import software.aws.toolkits.jetbrains.services.cloudwatch.logs.actions.WrapLogsAction import software.aws.toolkits.jetbrains.services.cloudwatch.logs.editor.LogStreamDateColumn import software.aws.toolkits.jetbrains.services.cloudwatch.logs.editor.LogStreamMessageColumn +import java.awt.Container class WrapLogsActionTest { @JvmField @@ -27,14 +28,17 @@ class WrapLogsActionTest { val wrapLogsAction = WrapLogsAction(projectRule.project) { table } wrapLogsAction.setSelected(TestActionEvent(), true) val wrappedComponent = model.columnInfos[1].getRenderer(null)!!.getTableCellRendererComponent(table, 0, false, false, 0, 0) - assertThat(wrappedComponent).isInstanceOf(JBTextArea::class.java) - assertThat((wrappedComponent as JBTextArea).wrapStyleWord).isTrue() - assertThat(wrappedComponent.lineWrap).isTrue() + + val textArea = (wrappedComponent as Container).getComponent(0) + + assertThat(textArea).isInstanceOf(JBTextArea::class.java) + assertThat((textArea as JBTextArea).lineWrap).isTrue() wrapLogsAction.setSelected(TestActionEvent(), false) val component = model.columnInfos[1].getRenderer(null)!!.getTableCellRendererComponent(table, 0, false, false, 0, 0) - assertThat(component).isInstanceOf(JBTextArea::class.java) - assertThat((component as JBTextArea).wrapStyleWord).isFalse() - assertThat(component.lineWrap).isFalse() + val textArea2 = (component as Container).getComponent(0) + + assertThat(textArea2).isInstanceOf(JBTextArea::class.java) + assertThat((textArea2 as JBTextArea).lineWrap).isFalse() } } From cbd881563a02b24418ec3bcb90d0d252281aef83 Mon Sep 17 00:00:00 2001 From: ranyoo2367 Date: Wed, 19 Aug 2020 16:28:06 -0400 Subject: [PATCH 0025/1546] Update Queue.kt to remove Kotlin asserts --- .../aws/toolkits/jetbrains/services/sqs/QueueTest.kt | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/jetbrains-core/tst/software/aws/toolkits/jetbrains/services/sqs/QueueTest.kt b/jetbrains-core/tst/software/aws/toolkits/jetbrains/services/sqs/QueueTest.kt index 15a958a7b4..31a3b62d7b 100644 --- a/jetbrains-core/tst/software/aws/toolkits/jetbrains/services/sqs/QueueTest.kt +++ b/jetbrains-core/tst/software/aws/toolkits/jetbrains/services/sqs/QueueTest.kt @@ -7,7 +7,6 @@ import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThatThrownBy import org.junit.Test import software.aws.toolkits.core.region.AwsRegion -import kotlin.test.assertFailsWith class QueueTest { private val defaultRegion = AwsRegion("us-east-1", "US East (N. Virginia)", "aws") @@ -44,16 +43,16 @@ class QueueTest { @Test fun `Throw exception with non-url`() { - assertThatThrownBy{ Queue("Not a URL", defaultRegion) }.isInstanceOf(IllegalArgumentException::class.java) + assertThatThrownBy { Queue("Not a URL", defaultRegion) }.isInstanceOf(IllegalArgumentException::class.java) } @Test fun `Throw exception with no name`() { - assertThatThrownBy{ Queue("https://sqs.us-east-1.amazonaws.com/123456789012/", defaultRegion) }.isInstanceOf(IllegalArgumentException::class.java) + assertThatThrownBy { Queue("https://sqs.us-east-1.amazonaws.com/123456789012/", defaultRegion) }.isInstanceOf(IllegalArgumentException::class.java) } @Test fun `Throw exception with invalid account ID`() { - assertThatThrownBy{ Queue("https://sqs.us-east-1.amazonaws.com/123/test-4", defaultRegion) }.isInstanceOf(IllegalArgumentException::class.java) + assertThatThrownBy { Queue("https://sqs.us-east-1.amazonaws.com/123/test-4", defaultRegion) }.isInstanceOf(IllegalArgumentException::class.java) } } From 11de4efcd2d549804a9a64cb9c1e1abe2b5b81d0 Mon Sep 17 00:00:00 2001 From: Ran Yoo <30319724+ranyoo2367@users.noreply.github.com> Date: Wed, 19 Aug 2020 17:42:31 -0400 Subject: [PATCH 0026/1546] SQS: Update UI (#1997) * Send Message Pane UI revisions * Create queue UI fix for .fifo * Updated send button to blue Co-authored-by: Hunter Werlla --- .../services/sqs/CreateQueueDialog.kt | 13 ++--- .../services/sqs/CreateQueuePanel.form | 50 +++++++++++-------- .../services/sqs/CreateQueuePanel.kt | 25 +++++++++- .../sqs/toolwindow/SendMessagePane.form | 40 +++++++++------ .../sqs/toolwindow/SendMessagePane.kt | 22 ++++++-- .../resources/localized_messages.properties | 2 +- 6 files changed, 100 insertions(+), 52 deletions(-) diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/CreateQueueDialog.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/CreateQueueDialog.kt index 20edf8b7da..c6377913fc 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/CreateQueueDialog.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/CreateQueueDialog.kt @@ -72,15 +72,8 @@ class CreateQueueDialog( if (queueName().length > MAX_LENGTH_OF_QUEUE_NAME) { return ValidationInfo(message("sqs.create.validation.long.queue.name", MAX_LENGTH_OF_QUEUE_NAME), view.queueName) } - - if (view.fifoType.isSelected) { - if (!validateCharacters(queueName().substringBefore(FIFO_SUFFIX))) { - return ValidationInfo(message("sqs.create.validation.queue.name.invalid"), view.queueName) - } - } else { - if (!validateCharacters(queueName())) { - return ValidationInfo(message("sqs.create.validation.queue.name.invalid"), view.queueName) - } + if (!validateCharacters(view.queueName.text)) { + return ValidationInfo(message("sqs.create.validation.queue.name.invalid"), view.queueName) } return null @@ -90,7 +83,7 @@ class CreateQueueDialog( private fun queueName(): String { val name = view.queueName.text.trim() - return if (view.fifoType.isSelected && !name.endsWith(FIFO_SUFFIX)) { + return if (view.fifoType.isSelected) { name + FIFO_SUFFIX } else { name diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/CreateQueuePanel.form b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/CreateQueuePanel.form index 952a63036c..7266424294 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/CreateQueuePanel.form +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/CreateQueuePanel.form @@ -1,24 +1,13 @@
- + - + - - - - - - - - - - - @@ -55,22 +44,41 @@ - + - + - + - + + + + - - - - + + + + + + + + + + + + + + + + + + + + diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/CreateQueuePanel.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/CreateQueuePanel.kt index 9545735b9b..0ced573e25 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/CreateQueuePanel.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/CreateQueuePanel.kt @@ -4,7 +4,10 @@ package software.aws.toolkits.jetbrains.services.sqs import com.intellij.icons.AllIcons import com.intellij.ide.HelpTooltip +import com.intellij.ui.IdeBorderFactory +import com.intellij.ui.JBColor import software.aws.toolkits.resources.message +import java.awt.BorderLayout import javax.swing.JLabel import javax.swing.JPanel import javax.swing.JRadioButton @@ -15,12 +18,14 @@ class CreateQueuePanel { lateinit var queueName: JTextField lateinit var standardType: JRadioButton lateinit var fifoType: JRadioButton - lateinit var fifoSuffixLabel: JLabel lateinit var queueNameContextHelp: JLabel + lateinit var fifoSuffixField: JTextField + lateinit var textPanel: JPanel init { setRadioButton() setTooltip() + setFields() } private fun setRadioButton() { @@ -40,4 +45,22 @@ class CreateQueuePanel { installOn(queueNameContextHelp) } } + + private fun setFields() { + queueName.apply { + border = IdeBorderFactory.createBorder(0) + } + fifoSuffixField.apply { + background = queueName.background + layout = BorderLayout() + border = IdeBorderFactory.createBorder(0) + add(fifoSuffixLabel, BorderLayout.WEST) + } + } + + companion object { + val fifoSuffixLabel = JLabel(".fifo").apply { + foreground = JBColor.GRAY + } + } } diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/toolwindow/SendMessagePane.form b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/toolwindow/SendMessagePane.form index 39b1a0ba64..dabf588e02 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/toolwindow/SendMessagePane.form +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/toolwindow/SendMessagePane.form @@ -1,6 +1,6 @@ - + @@ -30,7 +30,7 @@ - + @@ -58,31 +58,41 @@ - + - - - - - - - - - + - + + - + + + + + + + + + + + + + + + + + + - + diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/toolwindow/SendMessagePane.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/toolwindow/SendMessagePane.kt index 5071886553..c4adbba7f8 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/toolwindow/SendMessagePane.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/toolwindow/SendMessagePane.kt @@ -2,7 +2,9 @@ // SPDX-License-Identifier: Apache-2.0 package software.aws.toolkits.jetbrains.services.sqs.toolwindow +import com.intellij.ide.plugins.newui.UpdateButton import com.intellij.ide.util.PropertiesComponent +import com.intellij.ui.IdeBorderFactory import com.intellij.ui.components.JBTextArea import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers @@ -17,6 +19,7 @@ import java.awt.event.KeyListener import javax.swing.JButton import javax.swing.JLabel import javax.swing.JPanel +import javax.swing.JScrollPane class SendMessagePane( private val client: SqsClient, @@ -25,11 +28,12 @@ class SendMessagePane( ) : CoroutineScope by ApplicationThreadPoolScope("SendMessagePane") { lateinit var component: JPanel lateinit var inputText: JBTextArea - lateinit var sendButton: JButton + lateinit var sendButton: UpdateButton lateinit var clearButton: JButton lateinit var bodyErrorLabel: JLabel lateinit var confirmationLabel: JLabel lateinit var fifoFields: FifoPanel + lateinit var scrollPane: JScrollPane init { loadComponents() @@ -50,15 +54,16 @@ class SendMessagePane( launch { sendMessage() } } clearButton.addActionListener { - inputText.text = "" - fifoFields.deduplicationId.text = "" - fifoFields.groupId.text = "" + clearFields() messageCache.setValue(queue.queueUrl, "") confirmationLabel.isVisible = false } } private fun setFields() { + scrollPane.apply { + border = IdeBorderFactory.createBorder() + } inputText.apply { emptyText.text = message("sqs.send.message.body.empty.text") text = messageCache.getValue(queue.queueUrl) @@ -89,6 +94,7 @@ class SendMessagePane( confirmationLabel.text = message("sqs.send.message.success", messageId) } messageCache.setValue(queue.queueUrl, inputText.text) + clearFields() } catch (e: Exception) { confirmationLabel.text = message("sqs.failed_to_send_message") } @@ -106,4 +112,12 @@ class SendMessagePane( inputIsValid } } + + private fun clearFields() { + inputText.text = "" + if (queue.isFifo) { + fifoFields.deduplicationId.text = "" + fifoFields.groupId.text = "" + } + } } diff --git a/resources/resources/software/aws/toolkits/resources/localized_messages.properties b/resources/resources/software/aws/toolkits/resources/localized_messages.properties index 05c8ecbcd0..39899efa18 100644 --- a/resources/resources/software/aws/toolkits/resources/localized_messages.properties +++ b/resources/resources/software/aws/toolkits/resources/localized_messages.properties @@ -735,7 +735,7 @@ sqs.message.validation.long.id=ID exceeds the maximum length of 128 characters. sqs.messages.available.text=Messages Available: sqs.poll.message=Poll for Messages sqs.poll.warning.text=To view messages in the queue, poll for messages.

Once polled, the sample of messages remain in the queue and are not returned to subsequent receive requests for the duration of the visibility timeout.

-sqs.queue.name.tooltip=

A queue name is case-sensitive and can have up to 80 characters of alphanumeric characters, hyphens (-), and underscores ( _ ).

If FIFO is selected, '.fifo' will be appended to the specified name if it does not already exist.

+sqs.queue.name.tooltip=

A queue name is case-sensitive and can have up to 80 characters of alphanumeric characters, hyphens (-), and underscores ( _ ).

If FIFO is selected, '.fifo' will be appended to the specified name.

sqs.queue.polled.messages=Polled Messages sqs.required.empty.text=(Required) sqs.send.message=Send a Message From 26537a94ab16be70c6ed6a78246874a31b6249ea Mon Sep 17 00:00:00 2001 From: Austin Brooks Date: Wed, 19 Aug 2020 15:22:33 -0700 Subject: [PATCH 0027/1546] =?UTF-8?q?Fix=20removing=20a=20source=20profile?= =?UTF-8?q?=20leading=20to=20IDE=20error=20on=20profile=20file=20re?= =?UTF-8?q?=E2=80=A6=20(#1998)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...-cb9820c2-03b5-4d8e-b28e-b6622a697bdd.json | 4 + .../ProfileCredentialProviderFactory.kt | 2 +- .../ProfileCredentialProviderFactoryTest.kt | 91 +++++++++++++++++++ 3 files changed, 96 insertions(+), 1 deletion(-) create mode 100644 .changes/next-release/bugfix-cb9820c2-03b5-4d8e-b28e-b6622a697bdd.json diff --git a/.changes/next-release/bugfix-cb9820c2-03b5-4d8e-b28e-b6622a697bdd.json b/.changes/next-release/bugfix-cb9820c2-03b5-4d8e-b28e-b6622a697bdd.json new file mode 100644 index 0000000000..3f5d94c235 --- /dev/null +++ b/.changes/next-release/bugfix-cb9820c2-03b5-4d8e-b28e-b6622a697bdd.json @@ -0,0 +1,4 @@ +{ + "type" : "bugfix", + "description" : "Fix removing a source_profile leading to an IDE error on profile file refresh" +} \ No newline at end of file diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/core/credentials/profiles/ProfileCredentialProviderFactory.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/core/credentials/profiles/ProfileCredentialProviderFactory.kt index f092d583d2..9a119e3a14 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/core/credentials/profiles/ProfileCredentialProviderFactory.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/core/credentials/profiles/ProfileCredentialProviderFactory.kt @@ -113,7 +113,7 @@ class ProfileCredentialProviderFactory : CredentialProviderFactory { } // Any remaining profiles must have either become invalid or removed from the cred/config files - previousProfilesSnapshot.values.asSequence().map { it.asId(newProfiles.validProfiles) }.toCollection(profilesRemoved) + previousProfilesSnapshot.values.asSequence().map { it.asId(previousProfilesSnapshot) }.toCollection(profilesRemoved) profileHolder.update(newProfiles.validProfiles) credentialLoadCallback(CredentialsChangeEvent(profilesAdded, profilesModified, profilesRemoved)) diff --git a/jetbrains-core/tst/software/aws/toolkits/jetbrains/core/credentials/ProfileCredentialProviderFactoryTest.kt b/jetbrains-core/tst/software/aws/toolkits/jetbrains/core/credentials/ProfileCredentialProviderFactoryTest.kt index 6ffc6d1343..ac27727c02 100644 --- a/jetbrains-core/tst/software/aws/toolkits/jetbrains/core/credentials/ProfileCredentialProviderFactoryTest.kt +++ b/jetbrains-core/tst/software/aws/toolkits/jetbrains/core/credentials/ProfileCredentialProviderFactoryTest.kt @@ -350,6 +350,51 @@ class ProfileCredentialProviderFactoryTest { verify(mockSdkHttpClient, times(2)).prepareRequest(any()) } + @Test + fun testRemovingASourceProfile() { + profileFile.writeToFile( + """ + [profile role] + role_arn=arn1 + source_profile=source_profile + + [profile source_profile] + aws_access_key_id=BarAccessKey + aws_secret_access_key=BarSecretKey + """.trimIndent() + ) + + val providerFactory = createProviderFactory() + val roleProfile = findCredentialIdentifier("role") + providerFactory.createProvider(roleProfile) + + profileFile.writeToFile( + """ + [profile role] + role_arn=arn1 + source_profile=source_profile + """.trimIndent() + ) + + mockProfileWatcher.triggerListeners() + + argumentCaptor().apply { + verify(profileLoadCallback, times(2)).invoke(capture()) + + assertThat(firstValue.added).hasSize(2).has(profileName("role")).has(profileName("source_profile")) + assertThat(firstValue.modified).isEmpty() + assertThat(firstValue.removed).isEmpty() + + assertThat(secondValue.added).isEmpty() + assertThat(secondValue.modified).isEmpty() + assertThat(secondValue.removed).hasSize(2).has(profileName("role")).has(profileName("source_profile")) + } + + assertThatThrownBy { + providerFactory.createProvider(roleProfile) + } + } + @Test fun testRefreshExistingProfiles() { profileFile.writeToFile( @@ -395,6 +440,52 @@ class ProfileCredentialProviderFactoryTest { } } + @Test + fun testEditingUnrelatedProfilesAreNotNotified() { + profileFile.writeToFile( + """ + [profile foo] + aws_access_key_id=FooAccessKey + aws_secret_access_key=FooSecretKey + aws_session_token=FooSessionToken + + [profile bar] + aws_access_key_id=BarAccessKey + aws_secret_access_key=BarSecretKey + """.trimIndent() + ) + + createProviderFactory() + findCredentialIdentifier("foo") + + profileFile.writeToFile( + """ + [profile foo] + aws_access_key_id=FooAccessKey2 + aws_secret_access_key=FooSecretKey2 + aws_session_token=FooSessionToken2 + + [profile bar] + aws_access_key_id=BarAccessKey + aws_secret_access_key=BarSecretKey + """.trimIndent() + ) + + mockProfileWatcher.triggerListeners() + + argumentCaptor().apply { + verify(profileLoadCallback, times(2)).invoke(capture()) + + assertThat(firstValue.added).hasSize(2).has(profileName("foo")).has(profileName("bar")) + assertThat(firstValue.modified).isEmpty() + assertThat(firstValue.removed).isEmpty() + + assertThat(secondValue.added).isEmpty() + assertThat(secondValue.modified).hasSize(1).has(profileName("foo")) + assertThat(secondValue.removed).isEmpty() + } + } + @Test fun testRefreshDeletesProfiles() { profileFile.writeToFile( From ecc47a5d1f14d1ae3384412280670121d0b84f80 Mon Sep 17 00:00:00 2001 From: Austin Brooks Date: Thu, 20 Aug 2020 09:46:10 -0700 Subject: [PATCH 0028/1546] Obfuscate our build scan data (#1999) --- settings.gradle.kts | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/settings.gradle.kts b/settings.gradle.kts index 408c59a02f..fb9c457f22 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -10,3 +10,17 @@ include("jetbrains-core") include("jetbrains-ultimate") include("jetbrains-rider") include("ui-tests") + +plugins { + id("com.gradle.enterprise").version("3.4.1") +} + +gradleEnterprise { + buildScan { + obfuscation { + username { ""} + hostname { ""} + ipAddresses { it.map { "0.0.0.0"} } + } + } +} From 6b63fd66dd532464265e083b5387b1c543fd5c6e Mon Sep 17 00:00:00 2001 From: Hunter Werlla Date: Thu, 20 Aug 2020 16:10:55 -0700 Subject: [PATCH 0029/1546] Output each run of UI tests to a new file (#2001) Each run of UI tests is its own run, JaCoCo was overwriting the exec data each time. Since the actual test coverage data only really matters for CI, and these files are wiped out after every run, making a new file per run fixes the coverage issue without potentially breaking anything. --- build.gradle.kts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 8f4d2739ec..b136ba49d9 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -19,6 +19,7 @@ import software.aws.toolkits.gradle.getOrCreate import software.aws.toolkits.gradle.intellij import software.aws.toolkits.gradle.removeTask import software.aws.toolkits.gradle.resources.ValidateMessages +import java.time.Instant buildscript { repositories { @@ -220,6 +221,8 @@ subprojects { } project.plugins.withId("org.jetbrains.intellij") { + extensions.getByType().applyTo(tasks.getByName("runIdeForUiTests")) + tasks.withType(DownloadRobotServerPluginTask::class.java) { this.version = remoteRobotVersion } @@ -233,9 +236,11 @@ subprojects { if (System.getenv("CI") != null) { systemProperty("aws.sharedCredentialsFile", "/tmp/.aws/credentials") } - } - extensions.getByType().applyTo(tasks.getByName("runIdeForUiTests")) + configure { + destinationFile = file("$buildDir/jacoco/${Instant.now()}-jacocoUiTests.exec") + } + } } tasks.withType().all { From d438026612b35a37edf15e5c9ba24211cccded08 Mon Sep 17 00:00:00 2001 From: Austin Brooks Date: Thu, 20 Aug 2020 16:38:32 -0700 Subject: [PATCH 0030/1546] Fix CreateOrUpdateCredentialProfilesActionTest not able to run in isolation due to bad init (#2002) --- .../CreateOrUpdateCredentialProfilesActionTest.kt | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/jetbrains-core/tst/software/aws/toolkits/jetbrains/core/credentials/CreateOrUpdateCredentialProfilesActionTest.kt b/jetbrains-core/tst/software/aws/toolkits/jetbrains/core/credentials/CreateOrUpdateCredentialProfilesActionTest.kt index b6d1267197..d7780f4dc2 100644 --- a/jetbrains-core/tst/software/aws/toolkits/jetbrains/core/credentials/CreateOrUpdateCredentialProfilesActionTest.kt +++ b/jetbrains-core/tst/software/aws/toolkits/jetbrains/core/credentials/CreateOrUpdateCredentialProfilesActionTest.kt @@ -21,6 +21,7 @@ import com.nhaarman.mockitokotlin2.verify import com.nhaarman.mockitokotlin2.verifyZeroInteractions import org.assertj.core.api.Assertions.assertThat import org.junit.After +import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.rules.TemporaryFolder @@ -36,8 +37,14 @@ class CreateOrUpdateCredentialProfilesActionTest { @JvmField val projectRule = ProjectRule() - private val fileEditorManager = FileEditorManager.getInstance(projectRule.project) - private val localFileSystem = LocalFileSystem.getInstance() + private lateinit var fileEditorManager: FileEditorManager + private lateinit var localFileSystem: LocalFileSystem + + @Before + fun setUp() { + fileEditorManager = FileEditorManager.getInstance(projectRule.project) + localFileSystem = LocalFileSystem.getInstance() + } @After fun cleanUp() { From 781a7e75863867a33b15b3a73adf8621c2a7db4d Mon Sep 17 00:00:00 2001 From: Austin Brooks Date: Thu, 20 Aug 2020 17:41:34 -0700 Subject: [PATCH 0031/1546] Fix few issues in buildSrc (#2004) * Tests did not run * Enable kotlin in buildSrc the correct way * Few receivers changed from 'it' to 'this' because of that * Upgrade our markdown renderer --- buildSrc/build.gradle.kts | 17 ++++------------- .../aws/toolkits/gradle/BuildScriptUtils.kt | 2 +- .../gradle/changelog/ChangeLogPlugin.kt | 12 ++++++------ .../gradle/changelog/tasks/ChangeLogTask.kt | 2 +- .../aws/toolkits/gradle}/SourceUtilsTest.kt | 2 ++ .../gradle/changelog/ChangeLogGeneratorTest.kt | 12 ++++++------ 6 files changed, 20 insertions(+), 27 deletions(-) rename buildSrc/tst/{ => software/aws/toolkits/gradle}/SourceUtilsTest.kt (96%) diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index 48550add1c..5c01e9d2c4 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -1,4 +1,4 @@ -// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 @@ -17,11 +17,6 @@ buildscript { val props = java.util.Properties() file("${project.projectDir.parent}/gradle.properties").inputStream().use { props.load(it) } props.entries.forEach { it: Map.Entry -> project.extensions.add(it.key.toString(), it.value) } - - val kotlinVersion: String by project - dependencies { - classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion") - } } repositories { @@ -32,11 +27,8 @@ repositories { } plugins { - // TODO this really doesn't work. The plugin block requires a const string but the above - // hack we had in place to copy the properties also fixes this for now. - val kotlinVersion: String by project - kotlin("jvm") version kotlinVersion `java-gradle-plugin` + `kotlin-dsl` } sourceSets { @@ -44,16 +36,15 @@ sourceSets { java.srcDir("src") } test { - java.srcDir("src") + java.srcDir("tst") } } dependencies { api("com.fasterxml.jackson.datatype:jackson-datatype-jsr310:$jacksonVersion") - api("org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlinVersion") api("com.fasterxml.jackson.module:jackson-module-kotlin:$jacksonVersion") api("org.eclipse.jgit:org.eclipse.jgit:5.0.2.201807311906-r") - api("com.atlassian.commonmark:commonmark:0.11.0") + api("com.atlassian.commonmark:commonmark:0.15.2") api("software.amazon.awssdk:codegen:$awsSdkVersion") implementation("gradle.plugin.org.jetbrains.intellij.plugins:gradle-intellij-plugin:$ideaPluginVersion") diff --git a/buildSrc/src/software/aws/toolkits/gradle/BuildScriptUtils.kt b/buildSrc/src/software/aws/toolkits/gradle/BuildScriptUtils.kt index 8368a522c7..1f2a021573 100644 --- a/buildSrc/src/software/aws/toolkits/gradle/BuildScriptUtils.kt +++ b/buildSrc/src/software/aws/toolkits/gradle/BuildScriptUtils.kt @@ -14,7 +14,7 @@ import org.jetbrains.intellij.IntelliJPluginExtension inline fun Project.removeTask() { // For some reason in buildSrc, we can't use the <> version tasks.withType(T::class.java) { - it.enabled = false + enabled = false } } diff --git a/buildSrc/src/software/aws/toolkits/gradle/changelog/ChangeLogPlugin.kt b/buildSrc/src/software/aws/toolkits/gradle/changelog/ChangeLogPlugin.kt index 48470cb41f..e796a9da80 100644 --- a/buildSrc/src/software/aws/toolkits/gradle/changelog/ChangeLogPlugin.kt +++ b/buildSrc/src/software/aws/toolkits/gradle/changelog/ChangeLogPlugin.kt @@ -12,21 +12,21 @@ import software.aws.toolkits.gradle.changelog.tasks.NewChange class ChangeLogPlugin : Plugin { override fun apply(project: Project) { project.tasks.register("createRelease", CreateRelease::class.java) { - it.description = "Generates a release entry from unreleased changelog entries" + description = "Generates a release entry from unreleased changelog entries" } project.tasks.register("newChange", NewChange::class.java) { - it.description = "Creates a new change entry for inclusion in the Change Log" + description = "Creates a new change entry for inclusion in the Change Log" } project.tasks.register("newFeature", NewChange::class.java) { - it.description = "Creates a new feature change entry for inclusion in the Change Log" - it.defaultChangeType = ChangeType.FEATURE + description = "Creates a new feature change entry for inclusion in the Change Log" + defaultChangeType = ChangeType.FEATURE } project.tasks.register("newBugFix", NewChange::class.java) { - it.description = "Creates a new bug-fix change entry for inclusion in the Change Log" - it.defaultChangeType = ChangeType.BUGFIX + description = "Creates a new bug-fix change entry for inclusion in the Change Log" + defaultChangeType = ChangeType.BUGFIX } } } diff --git a/buildSrc/src/software/aws/toolkits/gradle/changelog/tasks/ChangeLogTask.kt b/buildSrc/src/software/aws/toolkits/gradle/changelog/tasks/ChangeLogTask.kt index cdbe0309e4..e72dc452fe 100644 --- a/buildSrc/src/software/aws/toolkits/gradle/changelog/tasks/ChangeLogTask.kt +++ b/buildSrc/src/software/aws/toolkits/gradle/changelog/tasks/ChangeLogTask.kt @@ -22,6 +22,6 @@ abstract class ChangeLogTask : DefaultTask() { val nextReleaseDirectory: DirectoryProperty = project.objects.directoryProperty().convention(changesDirectory.dir("next-release")) protected fun DirectoryProperty.jsonFiles(): FileTree = this.asFileTree.matching { - it.include("*.json") + include("*.json") } } diff --git a/buildSrc/tst/SourceUtilsTest.kt b/buildSrc/tst/software/aws/toolkits/gradle/SourceUtilsTest.kt similarity index 96% rename from buildSrc/tst/SourceUtilsTest.kt rename to buildSrc/tst/software/aws/toolkits/gradle/SourceUtilsTest.kt index ffff559f32..c471ebf72a 100644 --- a/buildSrc/tst/SourceUtilsTest.kt +++ b/buildSrc/tst/software/aws/toolkits/gradle/SourceUtilsTest.kt @@ -1,6 +1,8 @@ // Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 +package software.aws.toolkits.gradle + import org.assertj.core.api.Assertions.assertThat import org.junit.Test import org.junit.runner.RunWith diff --git a/buildSrc/tst/software/aws/toolkits/gradle/changelog/ChangeLogGeneratorTest.kt b/buildSrc/tst/software/aws/toolkits/gradle/changelog/ChangeLogGeneratorTest.kt index 78e3922a7f..b9d1921213 100644 --- a/buildSrc/tst/software/aws/toolkits/gradle/changelog/ChangeLogGeneratorTest.kt +++ b/buildSrc/tst/software/aws/toolkits/gradle/changelog/ChangeLogGeneratorTest.kt @@ -77,7 +77,7 @@ class ChangeLogGeneratorTest { ) val writer = mock() - val sut = ChangeLogGenerator(listOf(writer)) + val sut = ChangeLogGenerator(listOf(writer), mock()) sut.addReleasedChanges(listOf(first, third, second)) sut.close() @@ -148,7 +148,7 @@ class ChangeLogGeneratorTest { """ ) - val sut = ChangeLogGenerator(mock()) + val sut = ChangeLogGenerator(mock(), mock()) sut.addReleasedChanges(listOf(first, second)) } @@ -171,7 +171,7 @@ class ChangeLogGeneratorTest { val firstWriter = mock() val secondWriter = mock() - val sut = ChangeLogGenerator(listOf(firstWriter, secondWriter)) + val sut = ChangeLogGenerator(listOf(firstWriter, secondWriter), mock()) sut.addReleasedChanges(listOf(entry)) sut.close() @@ -184,7 +184,7 @@ class ChangeLogGeneratorTest { @Test fun basicWrite() { val writer = mock() - val sut = ChangeLogGenerator(listOf(writer)) + val sut = ChangeLogGenerator(listOf(writer), mock()) val first = createFile( """ @@ -246,7 +246,7 @@ class ChangeLogGeneratorTest { @Test fun canHandleMarkdown() { val writer = mock() - val sut = ChangeLogGenerator(listOf(writer)) + val sut = ChangeLogGenerator(listOf(writer), mock()) val first = createFile( """ @@ -289,7 +289,7 @@ class ChangeLogGeneratorTest { @Test fun canHandleMultiLine() { val writer = mock() - val sut = ChangeLogGenerator(listOf(writer)) + val sut = ChangeLogGenerator(listOf(writer), mock()) val first = createFile( """ From 8f9aa10d19509903be9349886fbcd4df4da4dc47 Mon Sep 17 00:00:00 2001 From: Austin Brooks Date: Fri, 21 Aug 2020 10:47:39 -0700 Subject: [PATCH 0032/1546] Stop using project / application as parent disposable (#2000) --- .../jetbrains/core/AwsClientManager.kt | 7 ++----- .../jetbrains/core/AwsResourceCache.kt | 2 +- .../toolkits/jetbrains/core/AwsSdkClient.kt | 5 ----- .../core/credentials/AwsConnectionManager.kt | 9 +++++++-- .../core/explorer/AwsExplorerFactory.kt | 1 - .../core/explorer/ExplorerToolWindow.kt | 12 +++++++---- .../core/toolwindow/ToolkitToolWindow.kt | 9 ++++++--- .../EcsCloudDebugSettingsEditorPanel.kt | 2 +- .../jetbrains/core/AwsClientManagerTest.kt | 20 +++++-------------- ...ateOrUpdateCredentialProfilesActionTest.kt | 19 +++++++++--------- .../clouddebug/DotNetDebuggerSupport.kt | 3 +-- .../lambda/dotnet/DotNetSamDebugSupport.kt | 2 +- 12 files changed, 41 insertions(+), 50 deletions(-) diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/core/AwsClientManager.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/core/AwsClientManager.kt index 155b542ab0..ba4f031c20 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/core/AwsClientManager.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/core/AwsClientManager.kt @@ -10,7 +10,6 @@ import com.intellij.openapi.application.ex.ApplicationInfoEx import com.intellij.openapi.components.ServiceManager import com.intellij.openapi.progress.ProcessCanceledException import com.intellij.openapi.project.Project -import com.intellij.openapi.util.Disposer import software.amazon.awssdk.core.SdkClient import software.amazon.awssdk.http.SdkHttpClient import software.aws.toolkits.core.ToolkitClientManager @@ -22,9 +21,9 @@ import software.aws.toolkits.core.region.AwsRegion import software.aws.toolkits.core.region.ToolkitRegionProvider import software.aws.toolkits.core.utils.tryOrNull import software.aws.toolkits.jetbrains.AwsToolkit +import software.aws.toolkits.jetbrains.core.credentials.AwsConnectionManager import software.aws.toolkits.jetbrains.core.credentials.ConnectionSettings import software.aws.toolkits.jetbrains.core.credentials.CredentialManager -import software.aws.toolkits.jetbrains.core.credentials.AwsConnectionManager import software.aws.toolkits.jetbrains.core.region.AwsRegionProvider open class AwsClientManager(project: Project) : ToolkitClientManager(), Disposable { @@ -33,9 +32,7 @@ open class AwsClientManager(project: Project) : ToolkitClientManager(), Disposab private val regionProvider = AwsRegionProvider.getInstance() init { - Disposer.register(project, Disposable { this.dispose() }) - - val busConnection = ApplicationManager.getApplication().messageBus.connect(project) + val busConnection = ApplicationManager.getApplication().messageBus.connect(this) busConnection.subscribe(CredentialManager.CREDENTIALS_CHANGED, object : ToolkitCredentialsChangeListener { override fun providerRemoved(identifier: CredentialIdentifier) { invalidateSdks(identifier.id) diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/core/AwsResourceCache.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/core/AwsResourceCache.kt index 83d461cf86..7d15d02910 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/core/AwsResourceCache.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/core/AwsResourceCache.kt @@ -228,7 +228,7 @@ class DefaultAwsResourceCache( private val cache = ConcurrentHashMap>() private val accountSettings by lazy { AwsConnectionManager.getInstance(project) } - private val alarm = AlarmFactory.getInstance().create(Alarm.ThreadToUse.POOLED_THREAD, project) + private val alarm = AlarmFactory.getInstance().create(Alarm.ThreadToUse.POOLED_THREAD, this) init { ApplicationManager.getApplication().messageBus.connect(this).subscribe(CredentialManager.CREDENTIALS_CHANGED, this) diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/core/AwsSdkClient.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/core/AwsSdkClient.kt index 8b37f17b01..b228662893 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/core/AwsSdkClient.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/core/AwsSdkClient.kt @@ -6,7 +6,6 @@ package software.aws.toolkits.jetbrains.core import com.intellij.openapi.Disposable import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.components.ServiceManager -import com.intellij.openapi.util.Disposer import com.intellij.util.net.ssl.CertificateManager import com.intellij.util.proxy.CommonProxy import org.apache.http.impl.client.SystemDefaultCredentialsProvider @@ -21,10 +20,6 @@ import software.aws.toolkits.core.utils.getLogger import software.aws.toolkits.core.utils.info class AwsSdkClient : Disposable { - init { - Disposer.register(ApplicationManager.getApplication(), this) - } - val sdkHttpClient: SdkHttpClient by lazy { LOG.info { "Create new Apache client" } val httpClientBuilder = ApacheHttpClient.builder() diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/core/credentials/AwsConnectionManager.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/core/credentials/AwsConnectionManager.kt index ade8cf5a56..bfac2242dc 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/core/credentials/AwsConnectionManager.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/core/credentials/AwsConnectionManager.kt @@ -3,6 +3,7 @@ package software.aws.toolkits.jetbrains.core.credentials +import com.intellij.openapi.Disposable import com.intellij.openapi.actionSystem.ActionManager import com.intellij.openapi.actionSystem.AnAction import com.intellij.openapi.application.ApplicationManager @@ -32,7 +33,7 @@ import software.aws.toolkits.jetbrains.utils.MRUList import software.aws.toolkits.resources.message import software.aws.toolkits.telemetry.AwsTelemetry -abstract class AwsConnectionManager(private val project: Project) : SimpleModificationTracker() { +abstract class AwsConnectionManager(private val project: Project) : SimpleModificationTracker(), Disposable { private val resourceCache = AwsResourceCache.getInstance(project) private val regionProvider = AwsRegionProvider.getInstance() private val credentialsRegionHandler = CredentialsRegionHandler.getInstance(project) @@ -59,7 +60,8 @@ abstract class AwsConnectionManager(private val project: Project) : SimpleModifi private var selectedCredentialsProvider: ToolkitCredentialsProvider? = null init { - ApplicationManager.getApplication().messageBus.connect(project) + @Suppress("LeakingThis") + ApplicationManager.getApplication().messageBus.connect(this) .subscribe(CredentialManager.CREDENTIALS_CHANGED, object : ToolkitCredentialsChangeListener { override fun providerRemoved(identifier: CredentialIdentifier) { if (selectedCredentialIdentifier == identifier) { @@ -219,6 +221,9 @@ abstract class AwsConnectionManager(private val project: Project) : SimpleModifi } } + override fun dispose() { + } + companion object { /*** * MessageBus topic for when the active credential profile or region is changed diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/core/explorer/AwsExplorerFactory.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/core/explorer/AwsExplorerFactory.kt index b5eae11630..90a9411659 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/core/explorer/AwsExplorerFactory.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/core/explorer/AwsExplorerFactory.kt @@ -17,7 +17,6 @@ import software.aws.toolkits.jetbrains.ui.feedback.FeedbackDialog import software.aws.toolkits.jetbrains.utils.actions.OpenBrowserAction import software.aws.toolkits.resources.message -@Suppress("unused") class AwsExplorerFactory : ToolWindowFactory, DumbAware { override fun createToolWindowContent(project: Project, toolWindow: ToolWindow) { val explorer = ExplorerToolWindow.getInstance(project) diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/core/explorer/ExplorerToolWindow.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/core/explorer/ExplorerToolWindow.kt index 0e59329f44..2a336be8d6 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/core/explorer/ExplorerToolWindow.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/core/explorer/ExplorerToolWindow.kt @@ -10,6 +10,7 @@ import com.intellij.ide.util.treeView.AbstractTreeNode import com.intellij.ide.util.treeView.NodeDescriptor import com.intellij.ide.util.treeView.NodeRenderer import com.intellij.ide.util.treeView.TreeState +import com.intellij.openapi.Disposable import com.intellij.openapi.actionSystem.ActionGroup import com.intellij.openapi.actionSystem.ActionManager import com.intellij.openapi.actionSystem.ActionPlaces @@ -66,12 +67,12 @@ import javax.swing.text.StyleConstants import javax.swing.tree.DefaultMutableTreeNode import javax.swing.tree.TreeModel -class ExplorerToolWindow(project: Project) : SimpleToolWindowPanel(true, true), ConnectionSettingsStateChangeNotifier { +class ExplorerToolWindow(project: Project) : SimpleToolWindowPanel(true, true), ConnectionSettingsStateChangeNotifier, Disposable { private val actionManager = ActionManagerEx.getInstanceEx() private val treePanelWrapper = NonOpaquePanel() private val awsTreeModel = AwsExplorerTreeStructure(project) - private val structureTreeModel = StructureTreeModel(awsTreeModel, project) - private val awsTree = createTree(AsyncTreeModel(structureTreeModel, true, project)) + private val structureTreeModel = StructureTreeModel(awsTreeModel, this) + private val awsTree = createTree(AsyncTreeModel(structureTreeModel, true, this)) private val awsTreePanel = ScrollPaneFactory.createScrollPane(awsTree) private val accountSettingsManager = AwsConnectionManager.getInstance(project) @@ -88,7 +89,7 @@ class ExplorerToolWindow(project: Project) : SimpleToolWindowPanel(true, true), background = UIUtil.getTreeBackground() setContent(treePanelWrapper) - project.messageBus.connect().subscribe(AwsConnectionManager.CONNECTION_SETTINGS_STATE_CHANGED, this) + project.messageBus.connect(this).subscribe(AwsConnectionManager.CONNECTION_SETTINGS_STATE_CHANGED, this) settingsStateChanged(accountSettingsManager.connectionState) } @@ -292,6 +293,9 @@ class ExplorerToolWindow(project: Project) : SimpleToolWindowPanel(true, true), } } + override fun dispose() { + } + companion object { fun getInstance(project: Project): ExplorerToolWindow = ServiceManager.getService(project, ExplorerToolWindow::class.java) } diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/core/toolwindow/ToolkitToolWindow.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/core/toolwindow/ToolkitToolWindow.kt index d9d7c68059..000b891f62 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/core/toolwindow/ToolkitToolWindow.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/core/toolwindow/ToolkitToolWindow.kt @@ -28,7 +28,7 @@ interface ToolkitToolWindowTab : Disposable { fun show() } -class ToolkitToolWindowManager(private val project: Project) { +class ToolkitToolWindowManager(private val project: Project) : Disposable { private val toolWindows = ConcurrentHashMap() internal fun getInstance(type: ToolkitToolWindowType) = toolWindows.computeIfAbsent(type) { ManagedToolkitToolWindow(type) } @@ -38,7 +38,7 @@ class ToolkitToolWindowManager(private val project: Project) { override fun addTab(title: String, component: JComponent, activate: Boolean, id: String, disposable: Disposable?): ToolkitToolWindowTab { val content = ContentImpl(component, title, true) val toolWindow = windowManager.getToolWindow(type.id) - ?: windowManager.registerToolWindow(type.id, true, type.anchor, project, true).also { + ?: windowManager.registerToolWindow(type.id, true, type.anchor, this@ToolkitToolWindowManager, true).also { it.setIcon(type.icon) it.stripeTitle = type.title } @@ -74,7 +74,7 @@ class ToolkitToolWindowManager(private val project: Project) { } } - inner class ManagedToolkitToolWindowTab(private val toolWindow: ToolWindow, internal val content: Content) : ToolkitToolWindowTab { + class ManagedToolkitToolWindowTab(private val toolWindow: ToolWindow, internal val content: Content) : ToolkitToolWindowTab { override fun show() { toolWindow.activate(null, true) toolWindow.contentManager.setSelectedContent(content) @@ -96,6 +96,9 @@ class ToolkitToolWindowManager(private val project: Project) { } } + override fun dispose() { + } + companion object { fun getInstance(project: Project, toolWindowType: ToolkitToolWindowType): ToolkitToolWindow = ServiceManager.getService( project, diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/ecs/execution/EcsCloudDebugSettingsEditorPanel.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/ecs/execution/EcsCloudDebugSettingsEditorPanel.kt index 5533fe8cce..40ed9f8c58 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/ecs/execution/EcsCloudDebugSettingsEditorPanel.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/ecs/execution/EcsCloudDebugSettingsEditorPanel.kt @@ -78,7 +78,7 @@ class EcsCloudDebugSettingsEditorPanel(private val project: Project) : Disposabl } private fun createUIComponents() { - containerLoadingIndicator = JBLoadingPanel(BorderLayout(), project) + containerLoadingIndicator = JBLoadingPanel(BorderLayout(), this) containerLoadingIndicator.setLoadingText(message("cloud_debug.ecs.run_config.container.loading")) containerLoadingIndicator.border = JBUI.Borders.empty() diff --git a/jetbrains-core/tst/software/aws/toolkits/jetbrains/core/AwsClientManagerTest.kt b/jetbrains-core/tst/software/aws/toolkits/jetbrains/core/AwsClientManagerTest.kt index b512c45f17..c88925b488 100644 --- a/jetbrains-core/tst/software/aws/toolkits/jetbrains/core/AwsClientManagerTest.kt +++ b/jetbrains-core/tst/software/aws/toolkits/jetbrains/core/AwsClientManagerTest.kt @@ -5,10 +5,8 @@ package software.aws.toolkits.jetbrains.core import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.project.Project -import com.intellij.openapi.project.ex.ProjectManagerEx -import com.intellij.testFramework.HeavyPlatformTestCase +import com.intellij.openapi.util.Disposer import com.intellij.testFramework.ProjectRule -import com.intellij.testFramework.runInEdtAndWait import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThatThrownBy import org.junit.After @@ -27,8 +25,8 @@ import software.aws.toolkits.core.region.AwsRegion import software.aws.toolkits.core.region.Endpoint import software.aws.toolkits.core.region.Service import software.aws.toolkits.jetbrains.core.credentials.CredentialManager -import software.aws.toolkits.jetbrains.core.credentials.MockCredentialsManager import software.aws.toolkits.jetbrains.core.credentials.MockAwsConnectionManager +import software.aws.toolkits.jetbrains.core.credentials.MockCredentialsManager import software.aws.toolkits.jetbrains.core.credentials.waitUntilConnectionStateIsStable import software.aws.toolkits.jetbrains.core.region.MockRegionProvider import kotlin.reflect.full.declaredMemberProperties @@ -99,19 +97,11 @@ class AwsClientManagerTest { @Test fun clientsAreClosedWhenProjectIsDisposed() { - val project = HeavyPlatformTestCase.createProject(temporaryDirectory.newFolder().toPath()) - val projectManager = ProjectManagerEx.getInstanceEx() - - runInEdtAndWait { - projectManager.openProject(project) - } - - val sut = getClientManager(project) + val sut = getClientManager(projectRule.project) val client = sut.getClient() - runInEdtAndWait { - projectManager.closeAndDispose(project) - } + // Frameworks handle this normally but we can't trigger it from tests + Disposer.dispose(sut) assertThat(client.closed).isTrue() } diff --git a/jetbrains-core/tst/software/aws/toolkits/jetbrains/core/credentials/CreateOrUpdateCredentialProfilesActionTest.kt b/jetbrains-core/tst/software/aws/toolkits/jetbrains/core/credentials/CreateOrUpdateCredentialProfilesActionTest.kt index d7780f4dc2..c11e4511a0 100644 --- a/jetbrains-core/tst/software/aws/toolkits/jetbrains/core/credentials/CreateOrUpdateCredentialProfilesActionTest.kt +++ b/jetbrains-core/tst/software/aws/toolkits/jetbrains/core/credentials/CreateOrUpdateCredentialProfilesActionTest.kt @@ -3,7 +3,6 @@ package software.aws.toolkits.jetbrains.core.credentials -import com.intellij.openapi.actionSystem.DataContext import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.fileEditor.FileEditorManager import com.intellij.openapi.fileTypes.FileTypes @@ -65,7 +64,7 @@ class CreateOrUpdateCredentialProfilesActionTest { val sut = CreateOrUpdateCredentialProfilesAction(writer, configFile, credFile) Messages.setTestDialog(TestDialog.OK) - sut.actionPerformed(TestActionEvent(DataContext { projectRule.project })) + sut.actionPerformed(TestActionEvent { projectRule.project }) verify(writer).createFile(configFile) @@ -85,7 +84,7 @@ class CreateOrUpdateCredentialProfilesActionTest { val sut = CreateOrUpdateCredentialProfilesAction(writer, configFile, credFile) Messages.setTestDialog(TestDialog.OK) - sut.actionPerformed(TestActionEvent(DataContext { projectRule.project })) + sut.actionPerformed(TestActionEvent { projectRule.project }) verifyZeroInteractions(writer) @@ -105,7 +104,7 @@ class CreateOrUpdateCredentialProfilesActionTest { val sut = CreateOrUpdateCredentialProfilesAction(writer, configFile, credFile) Messages.setTestDialog(TestDialog.OK) - sut.actionPerformed(TestActionEvent(DataContext { projectRule.project })) + sut.actionPerformed(TestActionEvent { projectRule.project }) verifyZeroInteractions(writer) @@ -123,7 +122,7 @@ class CreateOrUpdateCredentialProfilesActionTest { val sut = CreateOrUpdateCredentialProfilesAction(writer, configFile, credFile) Messages.setTestDialog(TestDialog.OK) - sut.actionPerformed(TestActionEvent(DataContext { projectRule.project })) + sut.actionPerformed(TestActionEvent { projectRule.project }) verifyZeroInteractions(writer) @@ -139,9 +138,9 @@ class CreateOrUpdateCredentialProfilesActionTest { // Mark the file as unknown for the purpose of the test. This is needed because some // other extensions can have weird file type association patterns (like Docker having - // *. (?)) which makes this test fail because it is not file type unkown - val file = listOf(localFileSystem.refreshAndFindFileByIoFile(credFile)) - localFileSystem.refreshFiles(file, false, false) { + // *. (?)) which makes this test fail because it is not file type unknown + localFileSystem.refreshAndFindFileByIoFile(credFile) + runInEdtAndWait { ApplicationManager.getApplication().runWriteAction { FileTypeManagerEx.getInstanceEx().associatePattern( FileTypes.UNKNOWN, @@ -153,7 +152,7 @@ class CreateOrUpdateCredentialProfilesActionTest { val sut = CreateOrUpdateCredentialProfilesAction(writer, configFile, credFile) Messages.setTestDialog(TestDialog.OK) - sut.actionPerformed(TestActionEvent(DataContext { projectRule.project })) + sut.actionPerformed(TestActionEvent { projectRule.project }) verifyZeroInteractions(writer) @@ -173,7 +172,7 @@ class CreateOrUpdateCredentialProfilesActionTest { val sut = CreateOrUpdateCredentialProfilesAction(writer, configFile, credFile) Messages.setTestDialog(TestDialog.NO) - sut.actionPerformed(TestActionEvent(DataContext { projectRule.project })) + sut.actionPerformed(TestActionEvent { projectRule.project }) verifyZeroInteractions(writer) } diff --git a/jetbrains-rider/src/software/aws/toolkits/jetbrains/services/clouddebug/DotNetDebuggerSupport.kt b/jetbrains-rider/src/software/aws/toolkits/jetbrains/services/clouddebug/DotNetDebuggerSupport.kt index 65b287a097..46b237f60d 100644 --- a/jetbrains-rider/src/software/aws/toolkits/jetbrains/services/clouddebug/DotNetDebuggerSupport.kt +++ b/jetbrains-rider/src/software/aws/toolkits/jetbrains/services/clouddebug/DotNetDebuggerSupport.kt @@ -208,10 +208,9 @@ class DotNetDebuggerSupport : DebuggerSupport() { exeRemotePath: String ): Promise { val promise = AsyncPromise() - val project = environment.project // Define a debugger lifetime to be able to dispose the debugger process and all nested component on termination - val debuggerLifetimeDefinition = project.defineNestedLifetime() + val debuggerLifetimeDefinition = environment.defineNestedLifetime() val debuggerLifetime = debuggerLifetimeDefinition.lifetime val scheduler = RdDispatcher(debuggerLifetime) diff --git a/jetbrains-rider/src/software/aws/toolkits/jetbrains/services/lambda/dotnet/DotNetSamDebugSupport.kt b/jetbrains-rider/src/software/aws/toolkits/jetbrains/services/lambda/dotnet/DotNetSamDebugSupport.kt index 2e1203d84f..8a7e04cdcc 100644 --- a/jetbrains-rider/src/software/aws/toolkits/jetbrains/services/lambda/dotnet/DotNetSamDebugSupport.kt +++ b/jetbrains-rider/src/software/aws/toolkits/jetbrains/services/lambda/dotnet/DotNetSamDebugSupport.kt @@ -116,7 +116,7 @@ class DotNetSamDebugSupport : SamDebugSupport { val promise = AsyncPromise() // Define a debugger lifetime to be able to dispose the debugger process and all nested component on termination - val debuggerLifetimeDefinition = environment.project.defineNestedLifetime() + val debuggerLifetimeDefinition = environment.defineNestedLifetime() val debuggerLifetime = debuggerLifetimeDefinition.lifetime val scheduler = RdDispatcher(debuggerLifetime) From f903ac55eb2695253ffafa4634a8e9eff7331e06 Mon Sep 17 00:00:00 2001 From: Hunter Werlla Date: Fri, 21 Aug 2020 12:02:10 -0700 Subject: [PATCH 0033/1546] Fix issue where templates > 51200 bytes would not deploy with "Deploy Serverless Application" (#2006) Add --s3-bucket to SAM deploy as well --- ...-e9d9fcce-86a0-4b24-87d6-39c38922f58c.json | 4 + build.gradle.kts | 2 + .../services/lambda/deploy/SamDeployTest.kt | 43 +- .../services/lambda/deploy/SamDeployDialog.kt | 2 + testdata/testFiles/LargeTemplate.yml | 6153 +++++++++++++++++ 5 files changed, 6199 insertions(+), 5 deletions(-) create mode 100644 .changes/next-release/bugfix-e9d9fcce-86a0-4b24-87d6-39c38922f58c.json create mode 100644 testdata/testFiles/LargeTemplate.yml diff --git a/.changes/next-release/bugfix-e9d9fcce-86a0-4b24-87d6-39c38922f58c.json b/.changes/next-release/bugfix-e9d9fcce-86a0-4b24-87d6-39c38922f58c.json new file mode 100644 index 0000000000..cae90dfdc1 --- /dev/null +++ b/.changes/next-release/bugfix-e9d9fcce-86a0-4b24-87d6-39c38922f58c.json @@ -0,0 +1,4 @@ +{ + "type" : "bugfix", + "description" : "Fix issue where templates > 51200 bytes would not deploy with \"Deploy Serverless Application\" (#1973)" +} \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts index b136ba49d9..63ec314331 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -217,6 +217,8 @@ subprojects { systemProperty("log.dir", "${(project.extensions["intellij"] as org.jetbrains.intellij.IntelliJPluginExtension).sandboxDirectory}-test/logs") } + systemProperty("testDataPath", project.rootDir.toPath().resolve("testdata").toString()) + mustRunAfter(tasks.test) } diff --git a/jetbrains-core/it/software/aws/toolkits/jetbrains/services/lambda/deploy/SamDeployTest.kt b/jetbrains-core/it/software/aws/toolkits/jetbrains/services/lambda/deploy/SamDeployTest.kt index c660ac7410..3007bf7a8d 100644 --- a/jetbrains-core/it/software/aws/toolkits/jetbrains/services/lambda/deploy/SamDeployTest.kt +++ b/jetbrains-core/it/software/aws/toolkits/jetbrains/services/lambda/deploy/SamDeployTest.kt @@ -22,6 +22,8 @@ import software.aws.toolkits.jetbrains.core.credentials.MockAwsConnectionManager import software.aws.toolkits.jetbrains.core.credentials.runUnderRealCredentials import software.aws.toolkits.jetbrains.utils.rules.HeavyJavaCodeInsightTestFixtureRule import software.aws.toolkits.jetbrains.utils.setSamExecutableFromEnvironment +import java.io.File +import java.nio.file.Paths import java.util.UUID import java.util.concurrent.TimeUnit @@ -37,6 +39,8 @@ class SamDeployTest { .region(Region.US_WEST_2) .build() + private val largeTemplateLocation = Paths.get(System.getProperty("testDataPath"), "testFiles", "LargeTemplate.yml").toString() + @Rule @JvmField val projectRule = HeavyJavaCodeInsightTestFixtureRule() @@ -76,6 +80,31 @@ class SamDeployTest { } } + @Test + // Tests using a stack > the CFN limit of 51200 bytes + fun deployLargeAppUsingSam() { + val stackName = "SamDeployTest-${UUID.randomUUID()}" + val templateFile = setUpProject(largeTemplateLocation) + runAssertsAndClean(stackName) { + val changeSetArn = createChangeSet(templateFile, stackName, mapOf("InstanceType" to "t2.small")) + + assertThat(changeSetArn).isNotNull() + + val describeChangeSetResponse = cfnClient.describeChangeSet { + it.stackName(stackName) + it.changeSetName(changeSetArn) + } + + assertThat(describeChangeSetResponse).isNotNull + assertThat(describeChangeSetResponse.parameters()).contains( + Parameter.builder() + .parameterKey("InstanceType") + .parameterValue("t2.small") + .build() + ) + } + } + @Test fun deployAppUsingSamWithParameters() { val stackName = "SamDeployTest-${UUID.randomUUID()}" @@ -100,7 +129,7 @@ class SamDeployTest { } } - private fun setUpProject(): VirtualFile { + private fun setUpProject(templateFilePath: String? = null): VirtualFile { projectRule.fixture.addFileToProject( "hello_world/app.py", """ @@ -114,9 +143,10 @@ class SamDeployTest { "" ) - return projectRule.fixture.addFileToProject( - "template.yaml", - """ + return if (templateFilePath == null) { + projectRule.fixture.addFileToProject( + "template.yaml", + """ AWSTemplateFormatVersion: '2010-09-09' Transform: AWS::Serverless-2016-10-31 Parameters: @@ -135,7 +165,10 @@ class SamDeployTest { Runtime: python2.7 Timeout: 900 """.trimIndent() - ).virtualFile + ).virtualFile + } else { + projectRule.fixture.addFileToProject("template.yaml", File(templateFilePath).readText()).virtualFile + } } private fun createChangeSet(templateFile: VirtualFile, stackName: String, parameters: Map = emptyMap()): String? { diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/deploy/SamDeployDialog.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/deploy/SamDeployDialog.kt index 19bacac926..4ee3720108 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/deploy/SamDeployDialog.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/deploy/SamDeployDialog.kt @@ -142,6 +142,8 @@ class SamDeployDialog( .withParameters(packagedTemplateFile.toString()) .withParameters("--stack-name") .withParameters(stackName) + .withParameters("--s3-bucket") + .withParameters(s3Bucket) if (capabilities.isNotEmpty()) { it.withParameters("--capabilities") diff --git a/testdata/testFiles/LargeTemplate.yml b/testdata/testFiles/LargeTemplate.yml new file mode 100644 index 0000000000..c52bb40bb5 --- /dev/null +++ b/testdata/testFiles/LargeTemplate.yml @@ -0,0 +1,6153 @@ +AWSTemplateFormatVersion: '2010-09-09' +Transform: AWS::Serverless-2016-10-31 + +Description: This template has a size over 51,200 bytes, so requires an S3 bucket to upload. Useful for testing Deploy Serverless Application + +Parameters: + InstanceType: + Type: String + Default: t2.small + AllowedValues: + - "t1.micro" + - "t2.nano" + - "t2.micro" + - "t2.small" + - "t2.medium" + - "t2.large" + - "m1.small" + - "m1.medium" + - "m1.large" + - "m1.xlarge" + - "m2.xlarge" + - "m2.2xlarge" + - "m2.4xlarge" + - "m3.medium" + - "m3.large" + - "m3.xlarge" + - "m3.2xlarge" + - "m4.large" + - "m4.xlarge" + - "m4.2xlarge" + - "m4.4xlarge" + - "m4.10xlarge" + - "c1.medium" + - "c1.xlarge" + - "c3.large" + - "c3.xlarge" + - "c3.2xlarge" + - "c3.4xlarge" + - "c3.8xlarge" + - "c4.large" + - "c4.xlarge" + - "c4.2xlarge" + - "c4.4xlarge" + - "c4.8xlarge" + - "g2.2xlarge" + - "g2.8xlarge" + - "r3.large" + - "r3.xlarge" + - "r3.2xlarge" + - "r3.4xlarge" + - "r3.8xlarge" + - "i2.xlarge" + - "i2.2xlarge" + - "i2.4xlarge" + - "i2.8xlarge" + - "d2.xlarge" + - "d2.2xlarge" + - "d2.4xlarge" + - "d2.8xlarge" + - "hi1.4xlarge" + - "hs1.8xlarge" + - "cr1.8xlarge" + - "cc2.8xlarge" + - "cg1.4xlarge" + - "26779" + - "26780" + - "26781" + - "26782" + - "26783" + - "26784" + - "26785" + - "26786" + - "26787" + - "26788" + - "26789" + - "26790" + - "26791" + - "26792" + - "26793" + - "26794" + - "26795" + - "26796" + - "26797" + - "26798" + - "26799" + - "26800" + - "26801" + - "26802" + - "26803" + - "26804" + - "26805" + - "26806" + - "26807" + - "26808" + - "26809" + - "26810" + - "26811" + - "26812" + - "26813" + - "26814" + - "26815" + - "26816" + - "26817" + - "26818" + - "26819" + - "26820" + - "26821" + - "26822" + - "26823" + - "26824" + - "26825" + - "26826" + - "26827" + - "26828" + - "26829" + - "26830" + - "26831" + - "26832" + - "26833" + - "26834" + - "26835" + - "26836" + - "26837" + - "26838" + - "26839" + - "26840" + - "26841" + - "26842" + - "26843" + - "26844" + - "26845" + - "26846" + - "26847" + - "26848" + - "26849" + - "26850" + - "26851" + - "26852" + - "26853" + - "26854" + - "26855" + - "26856" + - "26857" + - "26858" + - "26859" + - "26860" + - "26861" + - "26862" + - "26863" + - "26864" + - "26865" + - "26866" + - "26867" + - "26868" + - "26869" + - "26870" + - "26871" + - "26872" + - "26873" + - "26874" + - "26875" + - "26876" + - "26877" + - "26878" + - "26879" + - "26880" + - "26881" + - "26882" + - "26883" + - "26884" + - "26885" + - "26886" + - "26887" + - "26888" + - "26889" + - "26890" + - "26891" + - "26892" + - "26893" + - "26894" + - "26895" + - "26896" + - "26897" + - "26898" + - "26899" + - "26900" + - "26901" + - "26902" + - "26903" + - "26904" + - "26905" + - "26906" + - "26907" + - "26908" + - "26909" + - "26910" + - "26911" + - "26912" + - "26913" + - "26914" + - "26915" + - "26916" + - "26917" + - "26918" + - "26919" + - "26920" + - "26921" + - "26922" + - "26923" + - "26924" + - "26925" + - "26926" + - "26927" + - "26928" + - "26929" + - "26930" + - "26931" + - "26932" + - "26933" + - "26934" + - "26935" + - "26936" + - "26937" + - "26938" + - "26939" + - "26940" + - "26941" + - "26942" + - "26943" + - "26944" + - "26945" + - "26946" + - "26947" + - "26948" + - "26949" + - "26950" + - "26951" + - "26952" + - "26953" + - "26954" + - "26955" + - "26956" + - "26957" + - "26958" + - "26959" + - "26960" + - "26961" + - "26962" + - "26963" + - "26964" + - "26965" + - "26966" + - "26967" + - "26968" + - "26969" + - "26970" + - "26971" + - "26972" + - "26973" + - "26974" + - "26975" + - "26976" + - "26977" + - "26978" + - "26979" + - "26980" + - "26981" + - "26982" + - "26983" + - "26984" + - "26985" + - "26986" + - "26987" + - "26988" + - "26989" + - "26990" + - "26991" + - "26992" + - "26993" + - "26994" + - "26995" + - "26996" + - "26997" + - "26998" + - "26999" + - "27000" + - "27001" + - "27002" + - "27003" + - "27004" + - "27005" + - "27006" + - "27007" + - "27008" + - "27009" + - "27010" + - "27011" + - "27012" + - "27013" + - "27014" + - "27015" + - "27016" + - "27017" + - "27018" + - "27019" + - "27020" + - "27021" + - "27022" + - "27023" + - "27024" + - "27025" + - "27026" + - "27027" + - "27028" + - "27029" + - "27030" + - "27031" + - "27032" + - "27033" + - "27034" + - "27035" + - "27036" + - "27037" + - "27038" + - "27039" + - "27040" + - "27041" + - "27042" + - "27043" + - "27044" + - "27045" + - "27046" + - "27047" + - "27048" + - "27049" + - "27050" + - "27051" + - "27052" + - "27053" + - "27054" + - "27055" + - "27056" + - "27057" + - "27058" + - "27059" + - "27060" + - "27061" + - "27062" + - "27063" + - "27064" + - "27065" + - "27066" + - "27067" + - "27068" + - "27069" + - "27070" + - "27071" + - "27072" + - "27073" + - "27074" + - "27075" + - "27076" + - "27077" + - "27078" + - "27079" + - "27080" + - "27081" + - "27082" + - "27083" + - "27084" + - "27085" + - "27086" + - "27087" + - "27088" + - "27089" + - "27090" + - "27091" + - "27092" + - "27093" + - "27094" + - "27095" + - "27096" + - "27097" + - "27098" + - "27099" + - "27100" + - "27101" + - "27102" + - "27103" + - "27104" + - "27105" + - "27106" + - "27107" + - "27108" + - "27109" + - "27110" + - "27111" + - "27112" + - "27113" + - "27114" + - "27115" + - "27116" + - "27117" + - "27118" + - "27119" + - "27120" + - "27121" + - "27122" + - "27123" + - "27124" + - "27125" + - "27126" + - "27127" + - "27128" + - "27129" + - "27130" + - "27131" + - "27132" + - "27133" + - "27134" + - "27135" + - "27136" + - "27137" + - "27138" + - "27139" + - "27140" + - "27141" + - "27142" + - "27143" + - "27144" + - "27145" + - "27146" + - "27147" + - "27148" + - "27149" + - "27150" + - "27151" + - "27152" + - "27153" + - "27154" + - "27155" + - "27156" + - "27157" + - "27158" + - "27159" + - "27160" + - "27161" + - "27162" + - "27163" + - "27164" + - "27165" + - "27166" + - "27167" + - "27168" + - "27169" + - "27170" + - "27171" + - "27172" + - "27173" + - "27174" + - "27175" + - "27176" + - "27177" + - "27178" + - "27179" + - "27180" + - "27181" + - "27182" + - "27183" + - "27184" + - "27185" + - "27186" + - "27187" + - "27188" + - "27189" + - "27190" + - "27191" + - "27192" + - "27193" + - "27194" + - "27195" + - "27196" + - "27197" + - "27198" + - "27199" + - "27200" + - "27201" + - "27202" + - "27203" + - "27204" + - "27205" + - "27206" + - "27207" + - "27208" + - "27209" + - "27210" + - "27211" + - "27212" + - "27213" + - "27214" + - "27215" + - "27216" + - "27217" + - "27218" + - "27219" + - "27220" + - "27221" + - "27222" + - "27223" + - "27224" + - "27225" + - "27226" + - "27227" + - "27228" + - "27229" + - "27230" + - "27231" + - "27232" + - "27233" + - "27234" + - "27235" + - "27236" + - "27237" + - "27238" + - "27239" + - "27240" + - "27241" + - "27242" + - "27243" + - "27244" + - "27245" + - "27246" + - "27247" + - "27248" + - "27249" + - "27250" + - "27251" + - "27252" + - "27253" + - "27254" + - "27255" + - "27256" + - "27257" + - "27258" + - "27259" + - "27260" + - "27261" + - "27262" + - "27263" + - "27264" + - "27265" + - "27266" + - "27267" + - "27268" + - "27269" + - "27270" + - "27271" + - "27272" + - "27273" + - "27274" + - "27275" + - "27276" + - "27277" + - "27278" + - "27279" + - "27280" + - "27281" + - "27282" + - "27283" + - "27284" + - "27285" + - "27286" + - "27287" + - "27288" + - "27289" + - "27290" + - "27291" + - "27292" + - "27293" + - "27294" + - "27295" + - "27296" + - "27297" + - "27298" + - "27299" + - "27300" + - "27301" + - "27302" + - "27303" + - "27304" + - "27305" + - "27306" + - "27307" + - "27308" + - "27309" + - "27310" + - "27311" + - "27312" + - "27313" + - "27314" + - "27315" + - "27316" + - "27317" + - "27318" + - "27319" + - "27320" + - "27321" + - "27322" + - "27323" + - "27324" + - "27325" + - "27326" + - "27327" + - "27328" + - "27329" + - "27330" + - "27331" + - "27332" + - "27333" + - "27334" + - "27335" + - "27336" + - "27337" + - "27338" + - "27339" + - "27340" + - "27341" + - "27342" + - "27343" + - "27344" + - "27345" + - "27346" + - "27347" + - "27348" + - "27349" + - "27350" + - "27351" + - "27352" + - "27353" + - "27354" + - "27355" + - "27356" + - "27357" + - "27358" + - "27359" + - "27360" + - "27361" + - "27362" + - "27363" + - "27364" + - "27365" + - "27366" + - "27367" + - "27368" + - "27369" + - "27370" + - "27371" + - "27372" + - "27373" + - "27374" + - "27375" + - "27376" + - "27377" + - "27378" + - "27379" + - "27380" + - "27381" + - "27382" + - "27383" + - "27384" + - "27385" + - "27386" + - "27387" + - "27388" + - "27389" + - "27390" + - "27391" + - "27392" + - "27393" + - "27394" + - "27395" + - "27396" + - "27397" + - "27398" + - "27399" + - "27400" + - "27401" + - "27402" + - "27403" + - "27404" + - "27405" + - "27406" + - "27407" + - "27408" + - "27409" + - "27410" + - "27411" + - "27412" + - "27413" + - "27414" + - "27415" + - "27416" + - "27417" + - "27418" + - "27419" + - "27420" + - "27421" + - "27422" + - "27423" + - "27424" + - "27425" + - "27426" + - "27427" + - "27428" + - "27429" + - "27430" + - "27431" + - "27432" + - "27433" + - "27434" + - "27435" + - "27436" + - "27437" + - "27438" + - "27439" + - "27440" + - "27441" + - "27442" + - "27443" + - "27444" + - "27445" + - "27446" + - "27447" + - "27448" + - "27449" + - "27450" + - "27451" + - "27452" + - "27453" + - "27454" + - "27455" + - "27456" + - "27457" + - "27458" + - "27459" + - "27460" + - "27461" + - "27462" + - "27463" + - "27464" + - "27465" + - "27466" + - "27467" + - "27468" + - "27469" + - "27470" + - "27471" + - "27472" + - "27473" + - "27474" + - "27475" + - "27476" + - "27477" + - "27478" + - "27479" + - "27480" + - "27481" + - "27482" + - "27483" + - "27484" + - "27485" + - "27486" + - "27487" + - "27488" + - "27489" + - "27490" + - "27491" + - "27492" + - "27493" + - "27494" + - "27495" + - "27496" + - "27497" + - "27498" + - "27499" + - "27500" + - "27501" + - "27502" + - "27503" + - "27504" + - "27505" + - "27506" + - "27507" + - "27508" + - "27509" + - "27510" + - "27511" + - "27512" + - "27513" + - "27514" + - "27515" + - "27516" + - "27517" + - "27518" + - "27519" + - "27520" + - "27521" + - "27522" + - "27523" + - "27524" + - "27525" + - "27526" + - "27527" + - "27528" + - "27529" + - "27530" + - "27531" + - "27532" + - "27533" + - "27534" + - "27535" + - "27536" + - "27537" + - "27538" + - "27539" + - "27540" + - "27541" + - "27542" + - "27543" + - "27544" + - "27545" + - "27546" + - "27547" + - "27548" + - "27549" + - "27550" + - "27551" + - "27552" + - "27553" + - "27554" + - "27555" + - "27556" + - "27557" + - "27558" + - "27559" + - "27560" + - "27561" + - "27562" + - "27563" + - "27564" + - "27565" + - "27566" + - "27567" + - "27568" + - "27569" + - "22550" + - "22551" + - "22552" + - "22553" + - "22554" + - "22555" + - "22556" + - "22557" + - "22558" + - "22559" + - "22560" + - "22561" + - "22562" + - "22563" + - "22564" + - "22565" + - "22566" + - "22567" + - "22568" + - "22569" + - "22570" + - "22571" + - "22572" + - "22573" + - "22574" + - "22575" + - "22576" + - "22577" + - "22578" + - "22579" + - "22580" + - "22581" + - "22582" + - "22583" + - "22584" + - "22585" + - "22586" + - "22587" + - "22588" + - "22589" + - "22590" + - "22591" + - "22592" + - "22593" + - "22594" + - "22595" + - "22596" + - "22597" + - "22598" + - "22599" + - "22600" + - "22601" + - "22602" + - "22603" + - "22604" + - "22605" + - "22606" + - "22607" + - "22608" + - "22609" + - "22610" + - "22611" + - "22612" + - "22613" + - "22614" + - "22615" + - "22616" + - "22617" + - "22618" + - "22619" + - "22620" + - "22621" + - "22622" + - "22623" + - "22624" + - "22625" + - "22626" + - "22627" + - "22628" + - "22629" + - "22630" + - "22631" + - "22632" + - "22633" + - "22634" + - "22635" + - "22636" + - "22637" + - "22638" + - "22639" + - "22640" + - "22641" + - "22642" + - "22643" + - "22644" + - "22645" + - "22646" + - "22647" + - "22648" + - "22649" + - "22650" + - "22651" + - "22652" + - "22653" + - "22654" + - "22655" + - "22656" + - "22657" + - "22658" + - "22659" + - "22660" + - "22661" + - "22662" + - "22663" + - "22664" + - "22665" + - "22666" + - "22667" + - "22668" + - "22669" + - "22670" + - "22671" + - "22672" + - "22673" + - "22674" + - "22675" + - "22676" + - "22677" + - "22678" + - "22679" + - "22680" + - "22681" + - "22682" + - "22683" + - "22684" + - "22685" + - "22686" + - "22687" + - "22688" + - "22689" + - "22690" + - "22691" + - "22692" + - "22693" + - "22694" + - "22695" + - "22696" + - "22697" + - "22698" + - "22699" + - "22700" + - "22701" + - "22702" + - "22703" + - "22704" + - "22705" + - "22706" + - "22707" + - "22708" + - "22709" + - "22710" + - "22711" + - "22712" + - "22713" + - "22714" + - "22715" + - "22716" + - "22717" + - "22718" + - "22719" + - "22720" + - "22721" + - "22722" + - "22723" + - "22724" + - "22725" + - "22726" + - "22727" + - "22728" + - "22729" + - "22730" + - "22731" + - "22732" + - "22733" + - "22734" + - "22735" + - "22736" + - "22737" + - "22738" + - "22739" + - "22740" + - "22741" + - "22742" + - "22743" + - "22744" + - "22745" + - "22746" + - "22747" + - "22748" + - "22749" + - "22750" + - "22751" + - "22752" + - "22753" + - "22754" + - "22755" + - "22756" + - "22757" + - "22758" + - "22759" + - "22760" + - "22761" + - "22762" + - "22763" + - "22764" + - "22765" + - "22766" + - "22767" + - "22768" + - "22769" + - "22770" + - "22771" + - "22772" + - "22773" + - "22774" + - "22775" + - "22776" + - "22777" + - "22778" + - "22779" + - "22780" + - "22781" + - "22782" + - "22783" + - "22784" + - "22785" + - "22786" + - "22787" + - "22788" + - "22789" + - "22790" + - "22791" + - "22792" + - "22793" + - "22794" + - "22795" + - "22796" + - "22797" + - "22798" + - "22799" + - "22800" + - "22801" + - "22802" + - "22803" + - "22804" + - "22805" + - "22806" + - "22807" + - "22808" + - "22809" + - "22810" + - "22811" + - "22812" + - "22813" + - "22814" + - "22815" + - "22816" + - "22817" + - "22818" + - "22819" + - "22820" + - "22821" + - "22822" + - "22823" + - "22824" + - "22825" + - "22826" + - "22827" + - "22828" + - "22829" + - "22830" + - "22831" + - "22832" + - "22833" + - "22834" + - "22835" + - "22836" + - "22837" + - "22838" + - "22839" + - "22840" + - "22841" + - "22842" + - "22843" + - "22844" + - "22845" + - "22846" + - "22847" + - "22848" + - "22849" + - "22850" + - "22851" + - "22852" + - "22853" + - "22854" + - "22855" + - "22856" + - "22857" + - "22858" + - "22859" + - "22860" + - "22861" + - "22862" + - "22863" + - "22864" + - "22865" + - "22866" + - "22867" + - "22868" + - "22869" + - "22870" + - "22871" + - "22872" + - "22873" + - "22874" + - "22875" + - "22876" + - "22877" + - "22878" + - "22879" + - "22880" + - "22881" + - "22882" + - "22883" + - "22884" + - "22885" + - "22886" + - "22887" + - "22888" + - "22889" + - "22890" + - "22891" + - "22892" + - "22893" + - "22894" + - "22895" + - "22896" + - "22897" + - "22898" + - "22899" + - "22900" + - "22901" + - "22902" + - "22903" + - "22904" + - "22905" + - "22906" + - "22907" + - "22908" + - "22909" + - "22910" + - "22911" + - "22912" + - "22913" + - "22914" + - "22915" + - "22916" + - "22917" + - "22918" + - "22919" + - "22920" + - "22921" + - "22922" + - "22923" + - "22924" + - "22925" + - "22926" + - "22927" + - "22928" + - "22929" + - "22930" + - "22931" + - "22932" + - "22933" + - "22934" + - "22935" + - "22936" + - "22937" + - "22938" + - "22939" + - "22940" + - "22941" + - "22942" + - "22943" + - "22944" + - "22945" + - "22946" + - "22947" + - "22948" + - "22949" + - "22950" + - "22951" + - "22952" + - "22953" + - "22954" + - "22955" + - "22956" + - "22957" + - "22958" + - "22959" + - "22960" + - "22961" + - "22962" + - "22963" + - "22964" + - "22965" + - "22966" + - "22967" + - "22968" + - "22969" + - "22970" + - "22971" + - "22972" + - "22973" + - "22974" + - "22975" + - "22976" + - "22977" + - "22978" + - "22979" + - "22980" + - "22981" + - "22982" + - "22983" + - "22984" + - "22985" + - "22986" + - "22987" + - "22988" + - "22989" + - "22990" + - "22991" + - "22992" + - "22993" + - "22994" + - "22995" + - "22996" + - "22997" + - "22998" + - "22999" + - "23000" + - "23001" + - "23002" + - "23003" + - "23004" + - "23005" + - "23006" + - "23007" + - "23008" + - "23009" + - "23010" + - "23011" + - "23012" + - "23013" + - "23014" + - "23015" + - "23016" + - "23017" + - "23018" + - "23019" + - "23020" + - "23021" + - "23022" + - "23023" + - "23024" + - "23025" + - "23026" + - "23027" + - "23028" + - "23029" + - "23030" + - "23031" + - "23032" + - "23033" + - "23034" + - "23035" + - "23036" + - "23037" + - "23038" + - "23039" + - "23040" + - "23041" + - "23042" + - "23043" + - "23044" + - "23045" + - "23046" + - "23047" + - "23048" + - "23049" + - "23050" + - "23051" + - "23052" + - "23053" + - "23054" + - "23055" + - "23056" + - "23057" + - "23058" + - "23059" + - "23060" + - "23061" + - "23062" + - "23063" + - "23064" + - "23065" + - "23066" + - "23067" + - "23068" + - "23069" + - "23070" + - "23071" + - "23072" + - "23073" + - "23074" + - "23075" + - "23076" + - "23077" + - "23078" + - "23079" + - "23080" + - "23081" + - "23082" + - "23083" + - "23084" + - "23085" + - "23086" + - "23087" + - "23088" + - "23089" + - "23090" + - "23091" + - "23092" + - "23093" + - "23094" + - "23095" + - "23096" + - "23097" + - "23098" + - "23099" + - "23100" + - "23101" + - "23102" + - "23103" + - "23104" + - "23105" + - "23106" + - "23107" + - "23108" + - "23109" + - "23110" + - "23111" + - "23112" + - "23113" + - "23114" + - "23115" + - "23116" + - "23117" + - "23118" + - "23119" + - "23120" + - "23121" + - "23122" + - "23123" + - "23124" + - "23125" + - "23126" + - "23127" + - "23128" + - "23129" + - "23130" + - "23131" + - "23132" + - "23133" + - "23134" + - "23135" + - "23136" + - "23137" + - "23138" + - "23139" + - "23140" + - "23141" + - "23142" + - "23143" + - "23144" + - "23145" + - "23146" + - "23147" + - "23148" + - "23149" + - "23150" + - "23151" + - "23152" + - "23153" + - "23154" + - "23155" + - "23156" + - "23157" + - "23158" + - "23159" + - "23160" + - "23161" + - "23162" + - "23163" + - "23164" + - "23165" + - "23166" + - "23167" + - "23168" + - "23169" + - "23170" + - "23171" + - "23172" + - "23173" + - "23174" + - "23175" + - "23176" + - "23177" + - "23178" + - "23179" + - "23180" + - "23181" + - "23182" + - "23183" + - "23184" + - "23185" + - "23186" + - "23187" + - "23188" + - "23189" + - "23190" + - "23191" + - "23192" + - "23193" + - "23194" + - "23195" + - "23196" + - "23197" + - "23198" + - "23199" + - "23200" + - "23201" + - "23202" + - "23203" + - "23204" + - "23205" + - "23206" + - "23207" + - "23208" + - "23209" + - "23210" + - "23211" + - "23212" + - "23213" + - "23214" + - "23215" + - "23216" + - "23217" + - "23218" + - "23219" + - "23220" + - "23221" + - "23222" + - "23223" + - "23224" + - "23225" + - "23226" + - "23227" + - "23228" + - "23229" + - "23230" + - "23231" + - "23232" + - "23233" + - "23234" + - "23235" + - "23236" + - "23237" + - "23238" + - "23239" + - "23240" + - "23241" + - "23242" + - "23243" + - "23244" + - "23245" + - "23246" + - "23247" + - "23248" + - "23249" + - "23250" + - "23251" + - "23252" + - "23253" + - "23254" + - "23255" + - "23256" + - "23257" + - "23258" + - "23259" + - "23260" + - "23261" + - "23262" + - "23263" + - "23264" + - "23265" + - "23266" + - "23267" + - "23268" + - "23269" + - "23270" + - "23271" + - "23272" + - "23273" + - "23274" + - "23275" + - "23276" + - "23277" + - "23278" + - "23279" + - "23280" + - "23281" + - "23282" + - "23283" + - "23284" + - "23285" + - "23286" + - "23287" + - "23288" + - "23289" + - "23290" + - "23291" + - "23292" + - "23293" + - "23294" + - "23295" + - "23296" + - "23297" + - "23298" + - "23299" + - "23300" + - "23301" + - "23302" + - "23303" + - "23304" + - "23305" + - "23306" + - "23307" + - "23308" + - "23309" + - "23310" + - "23311" + - "23312" + - "23313" + - "23314" + - "23315" + - "23316" + - "23317" + - "23318" + - "23319" + - "23320" + - "23321" + - "23322" + - "23323" + - "23324" + - "23325" + - "23326" + - "23327" + - "23328" + - "23329" + - "23330" + - "23331" + - "23332" + - "23333" + - "23334" + - "23335" + - "23336" + - "23337" + - "23338" + - "23339" + - "23340" + - "23341" + - "23342" + - "23343" + - "23344" + - "23345" + - "23346" + - "23347" + - "23348" + - "23349" + - "23350" + - "23351" + - "23352" + - "23353" + - "23354" + - "23355" + - "23356" + - "23357" + - "23358" + - "23359" + - "23360" + - "23361" + - "23362" + - "23363" + - "23364" + - "23365" + - "23366" + - "23367" + - "23368" + - "23369" + - "23370" + - "23371" + - "23372" + - "23373" + - "23374" + - "23375" + - "23376" + - "23377" + - "23378" + - "23379" + - "23380" + - "23381" + - "23382" + - "23383" + - "23384" + - "23385" + - "23386" + - "23387" + - "23388" + - "23389" + - "23390" + - "23391" + - "23392" + - "23393" + - "23394" + - "23395" + - "23396" + - "23397" + - "23398" + - "23399" + - "23400" + - "23401" + - "23402" + - "23403" + - "23404" + - "23405" + - "23406" + - "23407" + - "23408" + - "23409" + - "23410" + - "23411" + - "23412" + - "23413" + - "23414" + - "23415" + - "23416" + - "23417" + - "23418" + - "23419" + - "23420" + - "23421" + - "23422" + - "23423" + - "23424" + - "23425" + - "23426" + - "23427" + - "23428" + - "23429" + - "23430" + - "23431" + - "23432" + - "23433" + - "23434" + - "23435" + - "23436" + - "23437" + - "23438" + - "23439" + - "23440" + - "23441" + - "23442" + - "23443" + - "23444" + - "23445" + - "23446" + - "23447" + - "23448" + - "23449" + - "23450" + - "23451" + - "23452" + - "23453" + - "23454" + - "23455" + - "23456" + - "23457" + - "23458" + - "23459" + - "23460" + - "23461" + - "23462" + - "23463" + - "23464" + - "23465" + - "23466" + - "23467" + - "23468" + - "23469" + - "23470" + - "23471" + - "23472" + - "23473" + - "23474" + - "23475" + - "23476" + - "23477" + - "23478" + - "23479" + - "23480" + - "23481" + - "23482" + - "23483" + - "23484" + - "23485" + - "23486" + - "23487" + - "23488" + - "23489" + - "23490" + - "23491" + - "23492" + - "23493" + - "23494" + - "23495" + - "23496" + - "23497" + - "23498" + - "23499" + - "23500" + - "23501" + - "23502" + - "23503" + - "23504" + - "23505" + - "23506" + - "23507" + - "23508" + - "23509" + - "23510" + - "23511" + - "23512" + - "23513" + - "23514" + - "23515" + - "23516" + - "23517" + - "23518" + - "23519" + - "23520" + - "23521" + - "23522" + - "23523" + - "23524" + - "23525" + - "23526" + - "23527" + - "23528" + - "23529" + - "23530" + - "23531" + - "23532" + - "23533" + - "23534" + - "23535" + - "23536" + - "23537" + - "23538" + - "23539" + - "23540" + - "23541" + - "23542" + - "23543" + - "23544" + - "23545" + - "23546" + - "23547" + - "23548" + - "23549" + - "23550" + - "23551" + - "23552" + - "23553" + - "23554" + - "23555" + - "23556" + - "23557" + - "23558" + - "23559" + - "23560" + - "23561" + - "23562" + - "23563" + - "23564" + - "23565" + - "23566" + - "23567" + - "23568" + - "23569" + - "23570" + - "23571" + - "23572" + - "23573" + - "23574" + - "23575" + - "23576" + - "23577" + - "23578" + - "23579" + - "23580" + - "23581" + - "23582" + - "23583" + - "23584" + - "23585" + - "23586" + - "23587" + - "23588" + - "23589" + - "23590" + - "23591" + - "23592" + - "23593" + - "23594" + - "23595" + - "23596" + - "23597" + - "23598" + - "23599" + - "23600" + - "23601" + - "23602" + - "23603" + - "23604" + - "23605" + - "23606" + - "23607" + - "23608" + - "23609" + - "23610" + - "23611" + - "23612" + - "23613" + - "23614" + - "23615" + - "23616" + - "23617" + - "23618" + - "23619" + - "23620" + - "23621" + - "23622" + - "23623" + - "23624" + - "23625" + - "23626" + - "23627" + - "23628" + - "23629" + - "23630" + - "23631" + - "23632" + - "23633" + - "23634" + - "23635" + - "23636" + - "23637" + - "23638" + - "23639" + - "23640" + - "23641" + - "23642" + - "23643" + - "23644" + - "23645" + - "23646" + - "23647" + - "23648" + - "23649" + - "23650" + - "23651" + - "23652" + - "23653" + - "23654" + - "23655" + - "23656" + - "23657" + - "23658" + - "23659" + - "23660" + - "23661" + - "23662" + - "23663" + - "23664" + - "23665" + - "23666" + - "23667" + - "23668" + - "23669" + - "23670" + - "23671" + - "23672" + - "23673" + - "23674" + - "23675" + - "23676" + - "23677" + - "23678" + - "23679" + - "23680" + - "23681" + - "23682" + - "23683" + - "23684" + - "23685" + - "23686" + - "23687" + - "23688" + - "23689" + - "23690" + - "23691" + - "23692" + - "23693" + - "23694" + - "23695" + - "23696" + - "23697" + - "23698" + - "23699" + - "23700" + - "23701" + - "23702" + - "23703" + - "23704" + - "23705" + - "23706" + - "23707" + - "23708" + - "23709" + - "23710" + - "23711" + - "23712" + - "23713" + - "23714" + - "23715" + - "23716" + - "23717" + - "23718" + - "23719" + - "23720" + - "23721" + - "23722" + - "23723" + - "23724" + - "23725" + - "23726" + - "23727" + - "23728" + - "23729" + - "23730" + - "23731" + - "23732" + - "23733" + - "23734" + - "23735" + - "23736" + - "23737" + - "23738" + - "23739" + - "23740" + - "23741" + - "23742" + - "23743" + - "23744" + - "23745" + - "23746" + - "23747" + - "23748" + - "23749" + - "23750" + - "23751" + - "23752" + - "23753" + - "23754" + - "23755" + - "23756" + - "23757" + - "23758" + - "23759" + - "23760" + - "23761" + - "23762" + - "23763" + - "23764" + - "23765" + - "23766" + - "23767" + - "23768" + - "23769" + - "23770" + - "23771" + - "23772" + - "23773" + - "23774" + - "23775" + - "23776" + - "23777" + - "23778" + - "23779" + - "23780" + - "23781" + - "23782" + - "23783" + - "23784" + - "23785" + - "23786" + - "23787" + - "23788" + - "23789" + - "23790" + - "23791" + - "23792" + - "23793" + - "23794" + - "23795" + - "23796" + - "23797" + - "23798" + - "23799" + - "23800" + - "23801" + - "23802" + - "23803" + - "23804" + - "23805" + - "23806" + - "23807" + - "23808" + - "23809" + - "23810" + - "23811" + - "23812" + - "23813" + - "23814" + - "23815" + - "23816" + - "23817" + - "23818" + - "23819" + - "23820" + - "23821" + - "23822" + - "23823" + - "23824" + - "23825" + - "23826" + - "23827" + - "23828" + - "23829" + - "23830" + - "23831" + - "23832" + - "23833" + - "23834" + - "23835" + - "23836" + - "23837" + - "23838" + - "23839" + - "23840" + - "23841" + - "23842" + - "23843" + - "23844" + - "23845" + - "23846" + - "23847" + - "23848" + - "23849" + - "23850" + - "23851" + - "23852" + - "23853" + - "23854" + - "23855" + - "23856" + - "23857" + - "23858" + - "23859" + - "23860" + - "23861" + - "23862" + - "23863" + - "23864" + - "23865" + - "23866" + - "23867" + - "23868" + - "23869" + - "23870" + - "23871" + - "23872" + - "23873" + - "23874" + - "23875" + - "23876" + - "23877" + - "23878" + - "23879" + - "23880" + - "23881" + - "23882" + - "23883" + - "23884" + - "23885" + - "23886" + - "23887" + - "23888" + - "23889" + - "23890" + - "23891" + - "23892" + - "23893" + - "23894" + - "23895" + - "23896" + - "23897" + - "23898" + - "23899" + - "23900" + - "23901" + - "23902" + - "23903" + - "23904" + - "23905" + - "23906" + - "23907" + - "23908" + - "23909" + - "23910" + - "23911" + - "23912" + - "23913" + - "23914" + - "23915" + - "23916" + - "23917" + - "23918" + - "23919" + - "23920" + - "23921" + - "23922" + - "23923" + - "23924" + - "23925" + - "23926" + - "23927" + - "23928" + - "23929" + - "23930" + - "23931" + - "23932" + - "23933" + - "23934" + - "23935" + - "23936" + - "23937" + - "23938" + - "23939" + - "23940" + - "23941" + - "23942" + - "23943" + - "23944" + - "23945" + - "23946" + - "23947" + - "23948" + - "23949" + - "23950" + - "23951" + - "23952" + - "23953" + - "23954" + - "23955" + - "23956" + - "23957" + - "23958" + - "23959" + - "23960" + - "23961" + - "23962" + - "23963" + - "23964" + - "23965" + - "23966" + - "23967" + - "23968" + - "23969" + - "23970" + - "23971" + - "23972" + - "23973" + - "23974" + - "23975" + - "23976" + - "23977" + - "23978" + - "23979" + - "23980" + - "23981" + - "23982" + - "23983" + - "23984" + - "23985" + - "23986" + - "23987" + - "23988" + - "23989" + - "23990" + - "23991" + - "23992" + - "23993" + - "23994" + - "23995" + - "23996" + - "23997" + - "23998" + - "23999" + - "24000" + - "24001" + - "24002" + - "24003" + - "24004" + - "24005" + - "24006" + - "24007" + - "24008" + - "24009" + - "24010" + - "24011" + - "24012" + - "24013" + - "24014" + - "24015" + - "24016" + - "24017" + - "24018" + - "24019" + - "24020" + - "24021" + - "24022" + - "24023" + - "24024" + - "24025" + - "24026" + - "24027" + - "24028" + - "24029" + - "24030" + - "24031" + - "24032" + - "24033" + - "24034" + - "24035" + - "24036" + - "24037" + - "24038" + - "24039" + - "24040" + - "24041" + - "24042" + - "24043" + - "24044" + - "24045" + - "46029" + - "46030" + - "46031" + - "46032" + - "46033" + - "46034" + - "46035" + - "46036" + - "46037" + - "46038" + - "46039" + - "46040" + - "46041" + - "46042" + - "46043" + - "46044" + - "46045" + - "46046" + - "46047" + - "46048" + - "46049" + - "46050" + - "46051" + - "46052" + - "46053" + - "46054" + - "46055" + - "46056" + - "46057" + - "46058" + - "46059" + - "46060" + - "46061" + - "46062" + - "46063" + - "46064" + - "46065" + - "46066" + - "46067" + - "46068" + - "46069" + - "46070" + - "46071" + - "46072" + - "46073" + - "46074" + - "46075" + - "46076" + - "46077" + - "46078" + - "46079" + - "46080" + - "46081" + - "46082" + - "46083" + - "46084" + - "46085" + - "46086" + - "46087" + - "46088" + - "46089" + - "46090" + - "46091" + - "46092" + - "46093" + - "46094" + - "46095" + - "46096" + - "46097" + - "46098" + - "46099" + - "46100" + - "46101" + - "46102" + - "46103" + - "46104" + - "46105" + - "46106" + - "46107" + - "46108" + - "46109" + - "46110" + - "46111" + - "46112" + - "46113" + - "46114" + - "46115" + - "46116" + - "46117" + - "46118" + - "46119" + - "46120" + - "46121" + - "46122" + - "46123" + - "46124" + - "46125" + - "46126" + - "46127" + - "46128" + - "46129" + - "46130" + - "46131" + - "46132" + - "46133" + - "46134" + - "46135" + - "46136" + - "46137" + - "46138" + - "46139" + - "46140" + - "46141" + - "46142" + - "46143" + - "46144" + - "46145" + - "46146" + - "46147" + - "46148" + - "46149" + - "46150" + - "46151" + - "46152" + - "46153" + - "46154" + - "46155" + - "46156" + - "46157" + - "46158" + - "46159" + - "46160" + - "46161" + - "46162" + - "46163" + - "46164" + - "46165" + - "46166" + - "46167" + - "46168" + - "46169" + - "46170" + - "46171" + - "46172" + - "46173" + - "46174" + - "46175" + - "46176" + - "46177" + - "46178" + - "46179" + - "46180" + - "46181" + - "46182" + - "46183" + - "46184" + - "46185" + - "46186" + - "46187" + - "46188" + - "46189" + - "46190" + - "46191" + - "46192" + - "46193" + - "46194" + - "46195" + - "46196" + - "46197" + - "46198" + - "46199" + - "46200" + - "46201" + - "46202" + - "46203" + - "46204" + - "46205" + - "46206" + - "46207" + - "46208" + - "46209" + - "46210" + - "46211" + - "46212" + - "46213" + - "46214" + - "46215" + - "46216" + - "46217" + - "46218" + - "46219" + - "46220" + - "46221" + - "46222" + - "46223" + - "46224" + - "46225" + - "46226" + - "46227" + - "46228" + - "46229" + - "46230" + - "46231" + - "46232" + - "46233" + - "46234" + - "46235" + - "46236" + - "46237" + - "46238" + - "46239" + - "46240" + - "46241" + - "46242" + - "46243" + - "46244" + - "46245" + - "46246" + - "46247" + - "46248" + - "46249" + - "46250" + - "46251" + - "46252" + - "46253" + - "46254" + - "46255" + - "46256" + - "46257" + - "46258" + - "46259" + - "46260" + - "46261" + - "46262" + - "46263" + - "46264" + - "46265" + - "46266" + - "46267" + - "46268" + - "46269" + - "46270" + - "46271" + - "46272" + - "46273" + - "46274" + - "46275" + - "46276" + - "46277" + - "46278" + - "46279" + - "46280" + - "46281" + - "46282" + - "46283" + - "46284" + - "46285" + - "46286" + - "46287" + - "46288" + - "46289" + - "46290" + - "46291" + - "46292" + - "46293" + - "46294" + - "46295" + - "46296" + - "46297" + - "46298" + - "46299" + - "46300" + - "46301" + - "46302" + - "46303" + - "46304" + - "46305" + - "46306" + - "46307" + - "46308" + - "46309" + - "46310" + - "46311" + - "46312" + - "46313" + - "46314" + - "46315" + - "46316" + - "46317" + - "46318" + - "46319" + - "46320" + - "46321" + - "46322" + - "46323" + - "46324" + - "46325" + - "46326" + - "46327" + - "46328" + - "46329" + - "46330" + - "46331" + - "46332" + - "46333" + - "46334" + - "46335" + - "46336" + - "46337" + - "46338" + - "46339" + - "46340" + - "46341" + - "46342" + - "46343" + - "46344" + - "46345" + - "46346" + - "46347" + - "46348" + - "46349" + - "46350" + - "46351" + - "46352" + - "46353" + - "46354" + - "46355" + - "46356" + - "46357" + - "46358" + - "46359" + - "46360" + - "46361" + - "46362" + - "46363" + - "46364" + - "46365" + - "46366" + - "46367" + - "46368" + - "46369" + - "46370" + - "46371" + - "46372" + - "46373" + - "46374" + - "46375" + - "46376" + - "46377" + - "46378" + - "46379" + - "46380" + - "46381" + - "46382" + - "46383" + - "46384" + - "46385" + - "46386" + - "46387" + - "46388" + - "46389" + - "46390" + - "46391" + - "46392" + - "46393" + - "46394" + - "46395" + - "46396" + - "46397" + - "46398" + - "46399" + - "46400" + - "46401" + - "46402" + - "46403" + - "46404" + - "46405" + - "46406" + - "46407" + - "46408" + - "46409" + - "46410" + - "46411" + - "46412" + - "46413" + - "46414" + - "46415" + - "46416" + - "46417" + - "46418" + - "46419" + - "46420" + - "46421" + - "46422" + - "46423" + - "46424" + - "46425" + - "46426" + - "46427" + - "46428" + - "46429" + - "46430" + - "46431" + - "46432" + - "46433" + - "46434" + - "46435" + - "46436" + - "46437" + - "46438" + - "46439" + - "46440" + - "46441" + - "46442" + - "46443" + - "46444" + - "46445" + - "46446" + - "46447" + - "46448" + - "46449" + - "46450" + - "46451" + - "46452" + - "46453" + - "46454" + - "46455" + - "46456" + - "46457" + - "46458" + - "46459" + - "46460" + - "46461" + - "46462" + - "46463" + - "46464" + - "46465" + - "46466" + - "46467" + - "46468" + - "46469" + - "46470" + - "46471" + - "46472" + - "46473" + - "46474" + - "46475" + - "46476" + - "46477" + - "46478" + - "46479" + - "46480" + - "46481" + - "46482" + - "46483" + - "46484" + - "46485" + - "46486" + - "46487" + - "46488" + - "46489" + - "46490" + - "46491" + - "46492" + - "46493" + - "46494" + - "46495" + - "46496" + - "46497" + - "46498" + - "46499" + - "46500" + - "46501" + - "46502" + - "46503" + - "46504" + - "46505" + - "46506" + - "46507" + - "46508" + - "46509" + - "46510" + - "46511" + - "46512" + - "46513" + - "46514" + - "46515" + - "46516" + - "46517" + - "46518" + - "46519" + - "46520" + - "46521" + - "46522" + - "46523" + - "46524" + - "46525" + - "46526" + - "46527" + - "46528" + - "46529" + - "46530" + - "46531" + - "46532" + - "46533" + - "46534" + - "46535" + - "46536" + - "46537" + - "46538" + - "46539" + - "46540" + - "46541" + - "46542" + - "46543" + - "46544" + - "46545" + - "46546" + - "46547" + - "46548" + - "46549" + - "46550" + - "46551" + - "46552" + - "46553" + - "46554" + - "46555" + - "46556" + - "46557" + - "46558" + - "46559" + - "46560" + - "46561" + - "46562" + - "46563" + - "46564" + - "46565" + - "46566" + - "46567" + - "46568" + - "46569" + - "46570" + - "46571" + - "46572" + - "46573" + - "46574" + - "46575" + - "46576" + - "46577" + - "46578" + - "46579" + - "46580" + - "46581" + - "46582" + - "46583" + - "46584" + - "46585" + - "46586" + - "46587" + - "46588" + - "46589" + - "46590" + - "46591" + - "46592" + - "46593" + - "46594" + - "46595" + - "46596" + - "46597" + - "46598" + - "46599" + - "46600" + - "46601" + - "46602" + - "46603" + - "46604" + - "46605" + - "46606" + - "46607" + - "46608" + - "46609" + - "46610" + - "46611" + - "46612" + - "46613" + - "46614" + - "46615" + - "46616" + - "46617" + - "46618" + - "46619" + - "46620" + - "46621" + - "46622" + - "46623" + - "46624" + - "46625" + - "46626" + - "46627" + - "46628" + - "46629" + - "46630" + - "46631" + - "46632" + - "46633" + - "46634" + - "46635" + - "46636" + - "46637" + - "46638" + - "46639" + - "46640" + - "46641" + - "46642" + - "46643" + - "46644" + - "46645" + - "46646" + - "46647" + - "46648" + - "46649" + - "46650" + - "46651" + - "46652" + - "46653" + - "46654" + - "46655" + - "46656" + - "46657" + - "46658" + - "46659" + - "46660" + - "46661" + - "46662" + - "46663" + - "46664" + - "46665" + - "46666" + - "46667" + - "46668" + - "46669" + - "46670" + - "46671" + - "46672" + - "46673" + - "46674" + - "46675" + - "46676" + - "46677" + - "46678" + - "46679" + - "46680" + - "46681" + - "46682" + - "46683" + - "46684" + - "46685" + - "46686" + - "46687" + - "46688" + - "46689" + - "46690" + - "46691" + - "46692" + - "46693" + - "46694" + - "46695" + - "46696" + - "46697" + - "46698" + - "46699" + - "46700" + - "46701" + - "46702" + - "46703" + - "46704" + - "46705" + - "46706" + - "46707" + - "46708" + - "46709" + - "46710" + - "46711" + - "46712" + - "46713" + - "46714" + - "46715" + - "46716" + - "46717" + - "46718" + - "46719" + - "46720" + - "46721" + - "46722" + - "46723" + - "46724" + - "46725" + - "46726" + - "46727" + - "46728" + - "46729" + - "46730" + - "46731" + - "46732" + - "46733" + - "46734" + - "46735" + - "46736" + - "46737" + - "46738" + - "46739" + - "46740" + - "46741" + - "46742" + - "46743" + - "46744" + - "46745" + - "46746" + - "46747" + - "46748" + - "46749" + - "46750" + - "46751" + - "46752" + - "46753" + - "46754" + - "46755" + - "46756" + - "46757" + - "46758" + - "46759" + - "46760" + - "46761" + - "46762" + - "46763" + - "46764" + - "46765" + - "46766" + - "46767" + - "46768" + - "46769" + - "46770" + - "46771" + - "46772" + - "46773" + - "46774" + - "46775" + - "46776" + - "46777" + - "46778" + - "46779" + - "46780" + - "46781" + - "46782" + - "46783" + - "46784" + - "46785" + - "46786" + - "46787" + - "46788" + - "46789" + - "46790" + - "46791" + - "46792" + - "46793" + - "46794" + - "46795" + - "46796" + - "46797" + - "46798" + - "46799" + - "46800" + - "46801" + - "46802" + - "46803" + - "46804" + - "46805" + - "46806" + - "46807" + - "46808" + - "46809" + - "46810" + - "46811" + - "46812" + - "46813" + - "46814" + - "46815" + - "46816" + - "46817" + - "46818" + - "46819" + - "46820" + - "46821" + - "46822" + - "46823" + - "46824" + - "46825" + - "46826" + - "46827" + - "46828" + - "46829" + - "46830" + - "46831" + - "46832" + - "46833" + - "46834" + - "46835" + - "46836" + - "46837" + - "46838" + - "46839" + - "46840" + - "46841" + - "46842" + - "46843" + - "46844" + - "46845" + - "46846" + - "46847" + - "46848" + - "46849" + - "46850" + - "46851" + - "46852" + - "46853" + - "46854" + - "46855" + - "46856" + - "46857" + - "46858" + - "46859" + - "46860" + - "46861" + - "46862" + - "46863" + - "46864" + - "46865" + - "46866" + - "46867" + - "46868" + - "46869" + - "46870" + - "46871" + - "46872" + - "46873" + - "46874" + - "46875" + - "46876" + - "46877" + - "46878" + - "46879" + - "46880" + - "46881" + - "46882" + - "46883" + - "46884" + - "46885" + - "46886" + - "46887" + - "46888" + - "46889" + - "46890" + - "46891" + - "46892" + - "46893" + - "46894" + - "46895" + - "46896" + - "46897" + - "46898" + - "46899" + - "46900" + - "46901" + - "46902" + - "46903" + - "46904" + - "46905" + - "46906" + - "46907" + - "46908" + - "46909" + - "46910" + - "46911" + - "46912" + - "46913" + - "46914" + - "46915" + - "46916" + - "46917" + - "46918" + - "46919" + - "46920" + - "46921" + - "46922" + - "46923" + - "46924" + - "46925" + - "46926" + - "46927" + - "46928" + - "46929" + - "46930" + - "46931" + - "46932" + - "46933" + - "46934" + - "46935" + - "46936" + - "46937" + - "46938" + - "46939" + - "46940" + - "46941" + - "46942" + - "46943" + - "46944" + - "46945" + - "46946" + - "46947" + - "46948" + - "46949" + - "46950" + - "46951" + - "46952" + - "46953" + - "46954" + - "46955" + - "46956" + - "46957" + - "46958" + - "46959" + - "46960" + - "46961" + - "46962" + - "46963" + - "46964" + - "46965" + - "46966" + - "46967" + - "46968" + - "46969" + - "46970" + - "46971" + - "46972" + - "46973" + - "46974" + - "46975" + - "46976" + - "46977" + - "46978" + - "46979" + - "46980" + - "46981" + - "46982" + - "46983" + - "46984" + - "46985" + - "46986" + - "46987" + - "46988" + - "46989" + - "46990" + - "46991" + - "46992" + - "46993" + - "46994" + - "46995" + - "46996" + - "46997" + - "46998" + - "46999" + - "47000" + - "47001" + - "47002" + - "47003" + - "47004" + - "47005" + - "47006" + - "47007" + - "47008" + - "47009" + - "47010" + - "47011" + - "47012" + - "47013" + - "47014" + - "47015" + - "47016" + - "47017" + - "47018" + - "47019" + - "47020" + - "47021" + - "47022" + - "47023" + - "47024" + - "47025" + - "47026" + - "47027" + - "47028" + - "47029" + - "47030" + - "47031" + - "47032" + - "47033" + - "47034" + - "47035" + - "47036" + - "47037" + - "47038" + - "47039" + - "47040" + - "47041" + - "47042" + - "47043" + - "47044" + - "47045" + - "47046" + - "47047" + - "47048" + - "47049" + - "47050" + - "47051" + - "47052" + - "47053" + - "47054" + - "47055" + - "47056" + - "47057" + - "47058" + - "47059" + - "47060" + - "47061" + - "47062" + - "47063" + - "47064" + - "47065" + - "47066" + - "47067" + - "47068" + - "47069" + - "47070" + - "47071" + - "47072" + - "47073" + - "47074" + - "47075" + - "47076" + - "47077" + - "47078" + - "47079" + - "47080" + - "47081" + - "47082" + - "47083" + - "47084" + - "47085" + - "47086" + - "47087" + - "47088" + - "47089" + - "47090" + - "47091" + - "47092" + - "47093" + - "47094" + - "47095" + - "47096" + - "47097" + - "47098" + - "47099" + - "47100" + - "47101" + - "47102" + - "47103" + - "47104" + - "47105" + - "47106" + - "47107" + - "47108" + - "47109" + - "47110" + - "47111" + - "47112" + - "47113" + - "47114" + - "47115" + - "47116" + - "47117" + - "47118" + - "47119" + - "47120" + - "47121" + - "47122" + - "47123" + - "47124" + - "47125" + - "47126" + - "47127" + - "47128" + - "47129" + - "47130" + - "47131" + - "47132" + - "47133" + - "47134" + - "47135" + - "47136" + - "47137" + - "47138" + - "47139" + - "47140" + - "47141" + - "47142" + - "47143" + - "47144" + - "47145" + - "47146" + - "47147" + - "47148" + - "47149" + - "47150" + - "47151" + - "47152" + - "47153" + - "47154" + - "47155" + - "47156" + - "47157" + - "47158" + - "47159" + - "47160" + - "47161" + - "47162" + - "47163" + - "47164" + - "47165" + - "47166" + - "47167" + - "47168" + - "47169" + - "47170" + - "47171" + - "47172" + - "47173" + - "47174" + - "47175" + - "47176" + - "47177" + - "47178" + - "47179" + - "47180" + - "47181" + - "47182" + - "47183" + - "47184" + - "47185" + - "47186" + - "47187" + - "47188" + - "47189" + - "47190" + - "47191" + - "47192" + - "47193" + - "47194" + - "47195" + - "47196" + - "47197" + - "47198" + - "47199" + - "47200" + - "47201" + - "47202" + - "47203" + - "47204" + - "47205" + - "47206" + - "47207" + - "47208" + - "47209" + - "47210" + - "47211" + - "47212" + - "47213" + - "47214" + - "47215" + - "47216" + - "47217" + - "47218" + - "47219" + - "47220" + - "47221" + - "47222" + - "47223" + - "47224" + - "47225" + - "47226" + - "47227" + - "47228" + - "47229" + - "47230" + - "47231" + - "47232" + - "47233" + - "47234" + - "47235" + - "47236" + - "47237" + - "47238" + - "47239" + - "47240" + - "47241" + - "47242" + - "47243" + - "47244" + - "47245" + - "47246" + - "47247" + - "47248" + - "47249" + - "47250" + - "47251" + - "47252" + - "47253" + - "47254" + - "47255" + - "47256" + - "47257" + - "47258" + - "47259" + - "47260" + - "47261" + - "47262" + - "47263" + - "47264" + - "47265" + - "47266" + - "47267" + - "47268" + - "47269" + - "47270" + - "47271" + - "47272" + - "47273" + - "47274" + - "47275" + - "47276" + - "47277" + - "47278" + - "47279" + - "47280" + - "47281" + - "47282" + - "47283" + - "47284" + - "47285" + - "47286" + - "47287" + - "47288" + - "47289" + - "47290" + - "47291" + - "47292" + - "47293" + - "47294" + - "47295" + - "47296" + - "47297" + - "47298" + - "47299" + - "47300" + - "47301" + - "47302" + - "47303" + - "47304" + - "47305" + - "47306" + - "47307" + - "47308" + - "47309" + - "47310" + - "47311" + - "47312" + - "47313" + - "47314" + - "47315" + - "47316" + - "47317" + - "47318" + - "47319" + - "47320" + - "47321" + - "47322" + - "47323" + - "47324" + - "47325" + - "47326" + - "47327" + - "47328" + - "47329" + - "47330" + - "47331" + - "47332" + - "47333" + - "47334" + - "47335" + - "47336" + - "47337" + - "47338" + - "47339" + - "47340" + - "47341" + - "47342" + - "47343" + - "47344" + - "47345" + - "47346" + - "47347" + - "47348" + - "47349" + - "47350" + - "47351" + - "47352" + - "47353" + - "47354" + - "47355" + - "47356" + - "47357" + - "47358" + - "47359" + - "47360" + - "47361" + - "47362" + - "47363" + - "47364" + - "47365" + - "47366" + - "47367" + - "47368" + - "47369" + - "47370" + - "47371" + - "47372" + - "47373" + - "47374" + - "47375" + - "47376" + - "47377" + - "47378" + - "47379" + - "47380" + - "47381" + - "47382" + - "47383" + - "47384" + - "47385" + - "47386" + - "47387" + - "47388" + - "47389" + - "47390" + - "47391" + - "47392" + - "47393" + - "47394" + - "47395" + - "47396" + - "47397" + - "47398" + - "47399" + - "47400" + - "47401" + - "47402" + - "47403" + - "47404" + - "47405" + - "47406" + - "47407" + - "47408" + - "47409" + - "47410" + - "47411" + - "47412" + - "47413" + - "47414" + - "47415" + - "47416" + - "47417" + - "47418" + - "47419" + - "47420" + - "47421" + - "47422" + - "47423" + - "47424" + - "47425" + - "47426" + - "47427" + - "47428" + - "47429" + - "47430" + - "47431" + - "47432" + - "47433" + - "47434" + - "47435" + - "47436" + - "47437" + - "47438" + - "47439" + - "47440" + - "47441" + - "47442" + - "47443" + - "47444" + - "47445" + - "47446" + - "47447" + - "47448" + - "47449" + - "47450" + - "47451" + - "47452" + - "47453" + - "47454" + - "47455" + - "47456" + - "47457" + - "47458" + - "47459" + - "47460" + - "47461" + - "47462" + - "47463" + - "47464" + - "47465" + - "47466" + - "47467" + - "47468" + - "47469" + - "47470" + - "47471" + - "47472" + - "47473" + - "47474" + - "47475" + - "47476" + - "47477" + - "47478" + - "47479" + - "47480" + - "47481" + - "47482" + - "47483" + - "47484" + - "47485" + - "47486" + - "47487" + - "47488" + - "47489" + - "47490" + - "47491" + - "47492" + - "47493" + - "47494" + - "47495" + - "47496" + - "47497" + - "47498" + - "47499" + - "47500" + - "47501" + - "47502" + - "47503" + - "47504" + - "47505" + - "47506" + - "47507" + - "47508" + - "47509" + - "47510" + - "47511" + - "47512" + - "47513" + - "47514" + - "47515" + - "47516" + - "47517" + - "47518" + - "47519" + - "47520" + - "47521" + - "47522" + - "47523" + - "47524" + - "47525" + - "47526" + - "47527" + - "47528" + - "47529" + - "47530" + - "47531" + - "47532" + - "47533" + - "47534" + - "47535" + - "47536" + - "47537" + - "47538" + - "47539" + - "47540" + - "47541" + - "47542" + - "47543" + - "47544" + - "47545" + - "47546" + - "47547" + - "47548" + - "47549" + - "47550" + - "47551" + - "47552" + - "47553" + - "47554" + - "47555" + - "47556" + - "47557" + - "47558" + - "47559" + - "47560" + - "47561" + - "47562" + - "47563" + - "47564" + - "47565" + - "47566" + - "47567" + - "47568" + - "47569" + - "47570" + - "47571" + - "47572" + - "47573" + - "47574" + - "47575" + - "47576" + - "47577" + - "47578" + - "47579" + - "47580" + - "47581" + - "47582" + - "47583" + - "47584" + - "47585" + - "47586" + - "47587" + - "47588" + - "47589" + - "47590" + - "47591" + - "47592" + - "47593" + - "47594" + - "47595" + - "47596" + - "47597" + - "47598" + - "47599" + - "47600" + - "47601" + - "47602" + - "47603" + - "47604" + - "47605" + - "47606" + - "47607" + - "47608" + - "47609" + - "47610" + - "47611" + - "47612" + - "47613" + - "47614" + - "47615" + - "47616" + - "47617" + - "47618" + - "47619" + - "47620" + - "47621" + - "47622" + - "47623" + - "47624" + - "47625" + - "47626" + - "47627" + - "47628" + - "47629" + - "47630" + - "47631" + - "47632" + - "47633" + - "47634" + - "47635" + - "47636" + - "47637" + - "47638" + - "47639" + - "47640" + - "47641" + - "47642" + - "47643" + - "47644" + - "47645" + - "47646" + - "47647" + - "47648" + - "47649" + - "47650" + - "47651" + - "47652" + - "47653" + - "47654" + - "47655" + - "47656" + - "47657" + - "47658" + - "47659" + - "47660" + - "47661" + - "47662" + - "47663" + - "47664" + - "47665" + - "47666" + - "47667" + - "47668" + - "47669" + - "47670" + - "47671" + - "47672" + - "47673" + - "47674" + - "47675" + - "47676" + - "47677" + - "47678" + - "47679" + - "47680" + - "47681" + - "47682" + - "47683" + - "47684" + - "47685" + - "47686" + - "47687" + - "47688" + - "47689" + - "47690" + - "47691" + - "47692" + - "47693" + - "47694" + - "47695" + - "47696" + - "47697" + - "47698" + - "47699" + - "47700" + - "47701" + - "47702" + - "47703" + - "47704" + - "47705" + - "47706" + - "47707" + - "47708" + - "47709" + - "47710" + - "47711" + - "47712" + - "47713" + - "47714" + - "47715" + - "47716" + - "47717" + - "47718" + - "47719" + - "47720" + - "47721" + - "47722" + - "47723" + - "47724" + - "47725" + - "47726" + - "47727" + - "47728" + - "47729" + - "47730" + - "47731" + - "47732" + - "47733" + - "47734" + - "47735" + - "47736" + - "47737" + - "47738" + - "47739" + - "47740" + - "47741" + - "47742" + - "47743" + - "47744" + - "47745" + - "47746" + - "47747" + - "47748" + - "47749" + - "47750" + - "47751" + - "47752" + - "47753" + - "47754" + - "47755" + - "47756" + - "47757" + - "47758" + - "47759" + - "47760" + - "47761" + - "47762" + - "47763" + - "47764" + - "47765" + - "47766" + - "47767" + - "47768" + - "47769" + - "47770" + - "47771" + - "47772" + - "47773" + - "47774" + - "47775" + - "47776" + - "47777" + - "47778" + - "47779" + - "47780" + - "47781" + - "47782" + - "47783" + - "47784" + - "47785" + - "47786" + - "47787" + - "47788" + - "47789" + - "47790" + - "47791" + - "47792" + - "47793" + - "47794" + - "47795" + - "47796" + - "47797" + - "47798" + - "47799" + - "47800" + - "47801" + - "47802" + - "47803" + - "47804" + - "47805" + - "47806" + - "47807" + - "47808" + - "47809" + - "47810" + - "47811" + - "47812" + - "47813" + - "47814" + - "47815" + - "47816" + - "47817" + - "47818" + - "47819" + - "47820" + - "47821" + - "47822" + - "47823" + - "47824" + - "47825" + - "47826" + - "47827" + - "47828" + - "47829" + - "47830" + - "47831" + - "47832" + - "47833" + - "47834" + - "47835" + - "47836" + - "47837" + - "47838" + - "47839" + - "47840" + - "47841" + - "47842" + - "47843" + - "47844" + - "47845" + - "47846" + - "47847" + - "47848" + - "47849" + - "47850" + - "47851" + - "47852" + - "47853" + - "47854" + - "47855" + - "47856" + - "47857" + - "47858" + - "47859" + - "47860" + - "47861" + - "47862" + - "47863" + - "47864" + - "47865" + - "47866" + - "47867" + - "47868" + - "47869" + - "47870" + - "47871" + - "47872" + - "47873" + - "47874" + - "47875" + - "47876" + - "47877" + - "47878" + - "47879" + - "47880" + - "47881" + - "47882" + - "47883" + - "47884" + - "47885" + - "47886" + - "47887" + - "47888" + - "47889" + - "47890" + - "47891" + - "47892" + - "47893" + - "47894" + - "47895" + - "47896" + - "47897" + - "47898" + - "47899" + - "47900" + - "47901" + - "47902" + - "47903" + - "47904" + - "47905" + - "47906" + - "47907" + - "47908" + - "47909" + - "47910" + - "47911" + - "47912" + - "47913" + - "47914" + - "47915" + - "47916" + - "47917" + - "47918" + - "47919" + - "47920" + - "47921" + - "47922" + - "47923" + - "47924" + - "47925" + - "47926" + - "47927" + - "47928" + - "47929" + - "47930" + - "47931" + - "47932" + - "47933" + - "47934" + - "47935" + - "47936" + - "47937" + - "47938" + - "47939" + - "47940" + - "47941" + - "47942" + - "47943" + - "47944" + - "47945" + - "47946" + - "47947" + - "47948" + - "47949" + - "47950" + - "47951" + - "47952" + - "47953" + - "47954" + - "47955" + - "47956" + - "47957" + - "47958" + - "47959" + - "47960" + - "47961" + - "47962" + - "47963" + - "47964" + - "47965" + - "47966" + - "47967" + - "47968" + - "47969" + - "47970" + - "47971" + - "47972" + - "47973" + - "47974" + - "47975" + - "47976" + - "47977" + - "47978" + - "47979" + - "47980" + - "47981" + - "47982" + - "47983" + - "47984" + - "47985" + - "47986" + - "47987" + - "47988" + - "47989" + - "47990" + - "47991" + - "47992" + - "47993" + - "47994" + - "47995" + - "47996" + - "47997" + - "47998" + - "47999" + - "48000" + - "48001" + - "48002" + - "48003" + - "48004" + - "48005" + - "48006" + - "48007" + - "48008" + - "48009" + - "48010" + - "48011" + - "48012" + - "48013" + - "48014" + - "48015" + - "48016" + - "48017" + - "48018" + - "48019" + - "48020" + - "48021" + - "48022" + - "48023" + - "48024" + - "48025" + - "48026" + - "48027" + - "48028" + - "48029" + - "48030" + - "48031" + - "48032" + - "48033" + - "48034" + - "48035" + - "48036" + - "48037" + - "48038" + - "48039" + - "48040" + - "48041" + - "48042" + - "48043" + - "48044" + - "48045" + - "48046" + - "48047" + - "48048" + - "48049" + - "48050" + - "48051" + - "48052" + - "48053" + - "48054" + - "48055" + - "48056" + - "48057" + - "48058" + - "48059" + - "48060" + - "48061" + - "48062" + - "48063" + - "48064" + - "48065" + - "48066" + - "48067" + - "48068" + - "48069" + - "48070" + - "48071" + - "48072" + - "48073" + - "48074" + - "48075" + - "48076" + - "48077" + - "48078" + - "48079" + - "48080" + - "48081" + - "48082" + - "48083" + - "48084" + - "48085" + - "48086" + - "48087" + - "48088" + - "48089" + - "48090" + - "48091" + - "48092" + - "48093" + - "48094" + - "48095" + - "48096" + - "48097" + - "48098" + - "48099" + - "48100" + - "48101" + - "48102" + - "48103" + - "48104" + - "48105" + - "48106" + - "48107" + - "48108" + - "48109" + - "48110" + - "48111" + - "48112" + - "48113" + - "48114" + - "48115" + - "48116" + - "48117" + - "48118" + - "48119" + - "48120" + - "48121" + - "48122" + - "48123" + - "48124" + - "48125" + - "48126" + - "48127" + - "48128" + - "48129" + - "48130" + - "48131" + - "48132" + - "48133" + - "48134" + - "48135" + - "48136" + - "48137" + - "48138" + - "48139" + - "48140" + - "48141" + - "48142" + - "48143" + - "48144" + - "48145" + - "48146" + - "48147" + - "48148" + - "48149" + - "48150" + - "48151" + - "48152" + - "48153" + - "48154" + - "48155" + - "48156" + - "48157" + - "48158" + - "48159" + - "48160" + - "48161" + - "48162" + - "48163" + - "48164" + - "48165" + - "48166" + - "48167" + - "48168" + - "48169" + - "48170" + - "48171" + - "48172" + - "48173" + - "48174" + - "48175" + - "48176" + - "48177" + - "48178" + - "48179" + - "48180" + - "48181" + - "48182" + - "48183" + - "48184" + - "48185" + - "48186" + - "48187" + - "48188" + - "48189" + - "48190" + - "48191" + - "48192" + - "48193" + - "48194" + - "48195" + - "48196" + - "48197" + - "48198" + - "48199" + - "48200" + - "48201" + - "48202" + - "48203" + - "48204" + - "48205" + - "48206" + - "48207" + - "48208" + - "48209" + - "48210" + - "48211" + - "48212" + - "48213" + - "48214" + - "48215" + - "48216" + - "48217" + - "48218" + - "48219" + - "48220" + - "48221" + - "48222" + - "48223" + - "48224" + - "48225" + - "48226" + - "48227" + - "48228" + - "48229" + - "48230" + - "48231" + - "48232" + - "48233" + - "48234" + - "48235" + - "48236" + - "48237" + - "48238" + - "48239" + - "48240" + - "48241" + - "48242" + - "48243" + - "48244" + - "48245" + - "48246" + - "48247" + - "48248" + - "48249" + - "48250" + - "48251" + - "48252" + - "48253" + - "48254" + - "48255" + - "48256" + - "48257" + - "48258" + - "48259" + - "48260" + - "48261" + - "48262" + - "48263" + - "48264" + - "48265" + - "48266" + - "48267" + - "48268" + - "48269" + - "48270" + - "48271" + - "48272" + - "48273" + - "48274" + - "48275" + - "48276" + - "48277" + - "48278" + - "48279" + - "48280" + - "48281" + - "48282" + - "48283" + - "48284" + - "48285" + - "48286" + - "48287" + - "48288" + - "48289" + - "48290" + - "48291" + - "48292" + - "48293" + - "48294" + - "48295" + - "48296" + - "48297" + - "48298" + - "48299" + - "48300" + - "48301" + - "48302" + - "48303" + - "48304" + - "48305" + - "48306" + - "48307" + - "48308" + - "48309" + - "48310" + - "48311" + - "48312" + - "48313" + - "48314" + - "48315" + - "48316" + - "48317" + - "48318" + - "48319" + - "48320" + - "48321" + - "48322" + - "48323" + - "48324" + - "48325" + - "48326" + - "48327" + - "48328" + - "48329" + - "48330" + - "48331" + - "48332" + - "48333" + - "48334" + - "48335" + - "48336" + - "48337" + - "48338" + - "48339" + - "48340" + - "48341" + - "48342" + - "48343" + - "48344" + - "48345" + - "48346" + - "48347" + - "48348" + - "48349" + - "48350" + - "48351" + - "48352" + - "48353" + - "48354" + - "48355" + - "48356" + - "48357" + - "48358" + - "48359" + - "48360" + - "48361" + - "48362" + - "48363" + - "48364" + - "48365" + - "48366" + - "48367" + - "48368" + - "48369" + - "48370" + - "48371" + - "48372" + - "48373" + - "48374" + - "48375" + - "48376" + - "48377" + - "48378" + - "48379" + - "48380" + - "48381" + - "48382" + - "48383" + - "48384" + - "48385" + - "48386" + - "48387" + - "48388" + - "48389" + - "48390" + - "48391" + - "48392" + - "48393" + - "48394" + - "48395" + - "48396" + - "48397" + - "48398" + - "48399" + - "48400" + - "48401" + - "48402" + - "48403" + - "48404" + - "48405" + - "48406" + - "48407" + - "48408" + - "48409" + - "48410" + - "48411" + - "48412" + - "48413" + - "48414" + - "48415" + - "48416" + - "48417" + - "48418" + - "48419" + - "48420" + - "48421" + - "48422" + - "48423" + - "48424" + - "48425" + - "48426" + - "48427" + - "48428" + - "48429" + - "48430" + - "48431" + - "48432" + - "48433" + - "48434" + - "48435" + - "48436" + - "48437" + - "48438" + - "48439" + - "48440" + - "48441" + - "48442" + - "48443" + - "48444" + - "48445" + - "48446" + - "48447" + - "48448" + - "48449" + - "48450" + - "48451" + - "48452" + - "48453" + - "48454" + - "48455" + - "48456" + - "48457" + - "48458" + - "48459" + - "48460" + - "48461" + - "48462" + - "48463" + - "48464" + - "48465" + - "48466" + - "48467" + - "48468" + - "48469" + - "48470" + - "48471" + - "48472" + - "48473" + - "48474" + - "48475" + - "48476" + - "48477" + - "48478" + - "48479" + - "48480" + - "48481" + - "48482" + - "48483" + - "48484" + - "48485" + - "48486" + - "48487" + - "48488" + - "48489" + - "48490" + - "48491" + - "48492" + - "48493" + - "48494" + - "48495" + - "48496" + - "48497" + - "48498" + - "48499" + - "48500" + - "48501" + - "48502" + - "48503" + - "48504" + - "48505" + - "48506" + - "48507" + - "48508" + - "48509" + - "48510" + - "48511" + - "48512" + - "48513" + - "48514" + - "48515" + - "48516" + - "48517" + - "48518" + - "48519" + - "48520" + - "48521" + - "48522" + - "48523" + - "48524" + - "48525" + - "48526" + - "48527" + - "48528" + - "48529" + - "48530" + - "48531" + - "48532" + - "48533" + - "48534" + - "48535" + - "48536" + - "48537" + - "48538" + - "48539" + - "48540" + - "48541" + - "48542" + - "48543" + - "48544" + - "48545" + - "48546" + - "48547" + - "48548" + - "48549" + - "48550" + - "48551" + - "48552" + - "48553" + - "48554" + - "48555" + - "48556" + - "48557" + - "48558" + - "48559" + - "48560" + - "48561" + - "48562" + - "48563" + - "48564" + - "48565" + - "48566" + - "48567" + - "48568" + - "48569" + - "48570" + - "48571" + - "48572" + - "48573" + - "48574" + - "48575" + - "48576" + - "48577" + - "48578" + - "48579" + - "48580" + - "48581" + - "48582" + - "48583" + - "48584" + - "48585" + - "48586" + - "48587" + - "48588" + - "48589" + - "48590" + - "48591" + - "48592" + - "48593" + - "48594" + - "48595" + - "48596" + - "48597" + - "48598" + - "48599" + - "48600" + - "48601" + - "48602" + - "48603" + - "48604" + - "48605" + - "48606" + - "48607" + - "48608" + - "48609" + - "48610" + - "48611" + - "48612" + - "48613" + - "48614" + - "48615" + - "48616" + - "48617" + - "48618" + - "48619" + - "48620" + - "48621" + - "48622" + - "48623" + - "48624" + - "48625" + - "48626" + - "48627" + - "48628" + - "48629" + - "48630" + - "48631" + - "48632" + - "48633" + - "48634" + - "48635" + - "48636" + - "48637" + - "48638" + - "48639" + - "48640" + - "48641" + - "48642" + - "48643" + - "48644" + - "48645" + - "48646" + - "48647" + - "48648" + - "48649" + - "48650" + - "48651" + - "48652" + - "48653" + - "48654" + - "48655" + - "48656" + - "48657" + - "48658" + - "48659" + - "48660" + - "48661" + - "48662" + - "48663" + - "48664" + - "48665" + - "48666" + - "48667" + - "48668" + - "48669" + - "48670" + - "48671" + - "48672" + - "48673" + - "48674" + - "48675" + - "48676" + - "48677" + - "48678" + - "48679" + - "48680" + - "48681" + - "48682" + - "48683" + - "48684" + - "48685" + - "48686" + - "48687" + - "48688" + - "48689" + - "48690" + - "48691" + - "48692" + - "48693" + - "48694" + - "48695" + - "48696" + - "48697" + - "48698" + - "48699" + - "48700" + - "48701" + - "48702" + - "48703" + - "48704" + - "48705" + - "48706" + - "48707" + - "48708" + - "48709" + - "48710" + - "48711" + - "48712" + - "48713" + - "48714" + - "48715" + - "48716" + - "48717" + - "48718" + - "48719" + - "48720" + - "48721" + - "48722" + - "48723" + - "48724" + - "48725" + - "48726" + - "48727" + - "48728" + - "48729" + - "48730" + - "48731" + - "48732" + - "48733" + - "48734" + - "48735" + - "48736" + - "48737" + - "48738" + - "48739" + - "48740" + - "48741" + - "48742" + - "48743" + - "48744" + - "48745" + - "48746" + - "48747" + - "48748" + - "48749" + - "48750" + - "48751" + - "48752" + - "48753" + - "48754" + - "48755" + - "48756" + - "48757" + - "48758" + - "48759" + - "48760" + - "48761" + - "48762" + - "48763" + - "48764" + - "48765" + - "48766" + - "48767" + - "48768" + - "48769" + - "48770" + - "48771" + - "48772" + - "48773" + - "48774" + - "48775" + - "48776" + - "48777" + - "48778" + - "48779" + - "48780" + - "48781" + - "48782" + - "48783" + - "48784" + - "48785" + - "48786" + - "48787" + - "48788" + - "48789" + - "48790" + - "48791" + - "48792" + - "48793" + - "48794" + - "48795" + - "48796" + - "48797" + - "48798" + - "48799" + - "48800" + - "48801" + - "48802" + - "48803" + - "48804" + - "48805" + - "48806" + - "48807" + - "48808" + - "48809" + - "48810" + - "48811" + - "48812" + - "48813" + - "48814" + - "48815" + - "48816" + - "48817" + - "48818" + - "48819" + - "48820" + - "48821" + - "48822" + - "48823" + - "48824" + - "48825" + - "48826" + - "48827" + - "48828" + - "48829" + - "48830" + - "48831" + - "48832" + - "48833" + - "48834" + - "48835" + - "48836" + - "48837" + - "48838" + - "48839" + - "48840" + - "48841" + - "48842" + - "48843" + - "48844" + - "48845" + - "48846" + - "48847" + - "48848" + - "48849" + - "48850" + - "48851" + - "48852" + - "48853" + - "48854" + - "48855" + - "48856" + - "48857" + - "48858" + - "48859" + - "48860" + - "48861" + - "48862" + - "48863" + - "48864" + - "48865" + - "48866" + - "48867" + - "48868" + - "48869" + - "48870" + - "48871" + - "48872" + - "48873" + - "48874" + - "48875" + - "48876" + - "48877" + - "48878" + - "48879" + - "48880" + - "48881" + - "48882" + - "48883" + - "48884" + - "48885" + - "48886" + - "48887" + - "48888" + - "48889" + - "48890" + - "48891" + - "48892" + - "48893" + - "48894" + - "48895" + - "48896" + - "48897" + - "48898" + - "48899" + - "48900" + - "48901" + - "48902" + - "48903" + - "48904" + - "48905" + - "48906" + - "48907" + - "48908" + - "48909" + - "48910" + - "48911" + - "48912" + - "48913" + - "48914" + - "48915" + - "48916" + - "48917" + - "48918" + - "48919" + - "48920" + - "48921" + - "48922" + - "48923" + - "48924" + - "48925" + - "48926" + - "48927" + - "48928" + - "48929" + - "48930" + - "48931" + - "48932" + - "48933" + - "48934" + - "48935" + - "48936" + - "48937" + - "48938" + - "48939" + - "48940" + - "48941" + - "48942" + - "48943" + - "48944" + - "48945" + - "48946" + - "48947" + - "48948" + - "48949" + - "48950" + - "48951" + - "48952" + - "48953" + - "48954" + - "48955" + - "48956" + - "48957" + - "48958" + - "48959" + - "48960" + - "48961" + - "48962" + - "48963" + - "48964" + - "48965" + - "48966" + - "48967" + - "48968" + - "48969" + - "48970" + - "48971" + - "48972" + - "48973" + - "48974" + - "48975" + - "48976" + - "48977" + - "48978" + - "48979" + - "48980" + - "48981" + - "48982" + - "48983" + - "48984" + - "48985" + - "48986" + - "48987" + - "48988" + - "48989" + - "48990" + - "48991" + - "48992" + - "48993" + - "48994" + - "48995" + - "48996" + - "48997" + - "48998" + - "48999" + - "49000" + - "49001" + - "49002" + - "49003" + - "49004" + - "49005" + - "49006" + - "49007" + - "49008" + - "49009" + - "49010" + - "49011" + - "49012" + - "49013" + - "49014" + - "49015" + - "49016" + - "49017" + - "49018" + - "49019" + - "49020" + - "49021" + - "49022" + - "49023" + - "49024" + - "49025" + - "49026" + - "49027" + - "49028" + - "49029" + - "49030" + - "49031" + - "49032" + - "49033" + - "49034" + - "49035" + - "49036" + - "49037" + - "49038" + - "49039" + - "49040" + - "49041" + - "49042" + - "49043" + - "49044" + - "49045" + - "49046" + - "49047" + - "49048" + - "49049" + - "49050" + - "49051" + - "49052" + - "49053" + - "49054" + - "49055" + - "49056" + - "49057" + - "49058" + - "49059" + - "49060" + - "49061" + - "49062" + - "49063" + - "49064" + - "49065" + - "49066" + - "49067" + - "49068" + - "49069" + - "49070" + - "49071" + - "49072" + - "49073" + - "49074" + - "49075" + - "49076" + - "49077" + - "49078" + - "49079" + - "49080" + - "49081" + - "49082" + - "49083" + - "49084" + - "49085" + - "49086" + - "49087" + - "49088" + - "49089" + - "49090" + - "49091" + - "49092" + - "49093" + - "49094" + - "49095" + - "49096" + - "49097" + - "49098" + - "49099" + - "49100" + - "49101" + - "49102" + - "49103" + - "49104" + - "49105" + - "49106" + - "49107" + - "49108" + - "49109" + - "49110" + - "49111" + - "49112" + - "49113" + - "49114" + - "49115" + - "49116" + - "49117" + - "49118" + - "49119" + - "49120" + - "49121" + - "49122" + - "49123" + - "49124" + - "49125" + - "49126" + - "49127" + - "49128" + - "49129" + - "49130" + - "49131" + - "49132" + - "49133" + - "49134" + - "49135" + - "49136" + - "49137" + - "49138" + - "49139" + - "49140" + - "49141" + - "49142" + - "49143" + - "49144" + - "49145" + - "49146" + - "49147" + - "49148" + - "49149" + - "49150" + - "49151" + - "49152" + - "49153" + - "49154" + - "49155" + - "49156" + - "49157" + - "49158" + - "49159" + - "49160" + - "49161" + - "49162" + - "49163" + - "49164" + - "49165" + - "49166" + - "49167" + - "49168" + - "49169" + - "49170" + - "49171" + - "49172" + - "49173" + - "49174" + - "49175" + - "49176" + - "49177" + - "49178" + - "49179" + - "49180" + - "49181" + - "49182" + - "49183" + - "49184" + - "49185" + - "49186" + - "49187" + - "49188" + - "49189" + - "49190" + - "49191" + - "49192" + - "49193" + - "49194" + - "49195" + - "49196" + - "49197" + - "49198" + - "49199" + - "49200" + - "49201" + - "49202" + - "49203" + - "49204" + - "49205" + - "49206" + - "49207" + - "49208" + - "49209" + - "49210" + - "49211" + - "49212" + - "49213" + - "49214" + - "49215" + - "49216" + - "49217" + - "49218" + - "49219" + - "49220" + - "49221" + - "49222" + - "49223" + - "49224" + - "49225" + - "49226" + - "49227" + - "49228" + - "49229" + - "49230" + - "49231" + - "49232" + - "49233" + - "49234" + - "49235" + - "49236" + - "49237" + - "49238" + - "49239" + - "49240" + - "49241" + - "49242" + - "49243" + - "49244" + - "49245" + - "49246" + - "49247" + - "49248" + - "49249" + - "49250" + - "49251" + - "49252" + - "49253" + - "49254" + - "49255" + - "49256" + - "49257" + - "49258" + - "49259" + - "49260" + - "49261" + - "49262" + - "49263" + - "49264" + - "49265" + - "49266" + - "49267" + - "49268" + - "49269" + - "49270" + - "49271" + - "49272" + - "49273" + - "49274" + - "49275" + - "49276" + - "49277" + - "49278" + - "49279" + - "49280" + - "49281" + - "49282" + - "49283" + - "49284" + - "49285" + - "49286" + - "49287" + - "49288" + - "49289" + - "49290" + - "49291" + - "49292" + - "49293" + - "49294" + - "49295" + - "49296" + - "49297" + - "49298" + - "49299" + - "49300" + - "49301" + - "49302" + - "49303" + - "49304" + - "49305" + - "49306" + - "49307" + - "49308" + - "49309" + - "49310" + - "49311" + - "49312" + - "49313" + - "49314" + - "49315" + - "49316" + - "49317" + - "49318" + - "49319" + - "49320" + - "49321" + - "49322" + - "49323" + - "49324" + - "49325" + - "49326" + - "49327" + - "49328" + - "49329" + - "49330" + - "49331" + - "49332" + - "49333" + - "49334" + - "49335" + - "49336" + - "49337" + - "49338" + - "49339" + - "49340" + - "49341" + - "49342" + - "49343" + - "49344" + - "49345" + - "49346" + - "49347" + - "49348" + - "49349" + - "49350" + - "49351" + - "49352" + - "49353" + - "49354" + - "49355" + - "49356" + - "49357" + - "49358" + - "49359" + - "49360" + - "49361" + - "49362" + - "49363" + - "49364" + - "49365" + - "49366" + - "49367" + - "49368" + - "49369" + - "49370" + - "49371" + - "49372" + - "49373" + - "49374" + - "49375" + - "49376" + - "49377" + - "49378" + - "49379" + - "49380" + - "49381" + - "49382" + - "49383" + - "49384" + - "49385" + - "49386" + - "49387" + - "49388" + - "49389" + - "49390" + - "49391" + - "49392" + - "49393" + - "49394" + - "49395" + - "49396" + - "49397" + - "49398" + - "49399" + - "49400" + - "49401" + - "49402" + - "49403" + - "49404" + - "49405" + - "49406" + - "49407" + - "49408" + - "49409" + - "49410" + - "49411" + - "49412" + - "49413" + - "49414" + - "49415" + - "49416" + - "49417" + - "49418" + - "49419" + - "49420" + - "49421" + - "49422" + - "49423" + - "49424" + - "49425" + - "49426" + - "49427" + - "49428" + - "49429" + - "49430" + - "49431" + - "49432" + - "49433" + - "49434" + - "49435" + - "49436" + - "49437" + - "49438" + - "49439" + - "49440" + - "49441" + - "49442" + - "49443" + - "49444" + - "49445" + - "49446" + - "49447" + - "49448" + - "49449" + - "49450" + - "49451" + - "49452" + - "49453" + - "49454" + - "49455" + - "49456" + - "49457" + - "49458" + - "49459" + - "49460" + - "49461" + - "49462" + - "49463" + - "49464" + - "49465" + - "49466" + - "49467" + - "49468" + - "49469" + - "49470" + - "49471" + - "49472" + - "49473" + - "49474" + - "49475" + - "49476" + - "49477" + - "49478" + - "49479" + - "49480" + - "49481" + - "49482" + - "49483" + - "49484" + - "49485" + - "49486" + - "49487" + - "49488" + - "49489" + - "49490" + - "49491" + - "49492" + - "49493" + - "49494" + - "49495" + - "49496" + - "49497" + - "49498" + - "49499" + - "49500" + - "49501" + - "49502" + - "49503" + - "49504" + - "49505" + - "49506" + - "49507" + - "49508" + - "49509" + - "49510" + - "49511" + - "49512" + - "49513" + - "49514" + - "49515" + - "49516" + - "49517" + - "49518" + - "49519" + - "49520" + - "49521" + - "49522" + - "49523" + - "49524" + - "49525" + - "49526" + - "49527" + - "49528" + - "49529" + - "49530" + - "49531" + - "49532" + - "49533" + - "49534" + - "49535" + - "49536" + - "49537" + - "49538" + - "49539" + - "49540" + - "49541" + - "49542" + - "49543" + - "49544" + - "49545" + - "49546" + - "49547" + - "49548" + - "49549" + - "49550" + - "49551" + - "49552" + - "49553" + - "49554" + - "49555" + - "49556" + - "49557" + - "49558" + - "49559" + - "49560" + - "49561" + - "49562" + - "49563" + - "49564" + - "49565" + - "49566" + - "49567" + - "49568" + - "49569" + - "49570" + - "49571" + - "49572" + - "49573" + - "49574" + - "49575" + - "49576" + - "49577" + - "49578" + - "49579" + - "49580" + - "49581" + - "49582" + - "49583" + - "49584" + - "49585" + - "49586" + - "49587" + - "49588" + - "49589" + - "49590" + - "49591" + - "49592" + - "49593" + - "49594" + - "49595" + - "49596" + - "49597" + - "49598" + - "49599" + - "49600" + - "49601" + - "49602" + - "49603" + - "49604" + - "49605" + - "49606" + - "49607" + - "49608" + - "49609" + - "49610" + - "49611" + - "49612" + - "49613" + - "49614" + - "49615" + - "49616" + - "49617" + - "49618" + - "49619" + - "49620" + - "49621" + - "49622" + - "49623" + - "49624" + - "49625" + - "49626" + - "49627" + - "49628" + - "49629" + - "49630" + - "49631" + - "49632" + - "49633" + - "49634" + - "49635" + - "49636" + - "49637" + - "49638" + - "49639" + - "49640" + - "49641" + - "49642" + - "49643" + - "49644" + - "49645" + - "49646" + - "49647" + - "49648" + - "49649" + - "49650" + - "49651" + - "49652" + - "49653" + - "49654" + - "49655" + - "49656" + - "49657" + - "49658" + - "49659" + - "49660" + - "49661" + - "49662" + - "49663" + - "49664" + - "49665" + - "49666" + - "49667" + - "49668" + - "49669" + - "49670" + - "49671" + - "49672" + - "49673" + - "49674" + - "49675" + - "49676" + - "49677" + - "49678" + - "49679" + - "49680" + - "49681" + - "49682" + - "49683" + - "49684" + - "49685" + - "49686" + - "49687" + - "49688" + - "49689" + - "49690" + - "49691" + - "49692" + - "49693" + - "49694" + - "49695" + - "49696" + - "49697" + - "49698" + - "49699" + - "49700" + - "49701" + - "49702" + - "49703" + - "49704" + - "49705" + - "49706" + - "49707" + - "49708" + - "49709" + - "49710" + - "49711" + - "49712" + - "49713" + - "49714" + - "49715" + - "49716" + - "49717" + - "49718" + - "49719" + - "49720" + - "49721" + - "49722" + - "49723" + - "49724" + - "49725" + - "49726" + - "49727" + - "49728" + - "49729" + - "49730" + - "49731" + - "49732" + - "49733" + - "49734" + - "49735" + - "49736" + - "49737" + - "49738" + - "49739" + - "49740" + - "49741" + - "49742" + - "49743" + - "49744" + - "49745" + - "49746" + - "49747" + - "49748" + - "49749" + - "49750" + - "49751" + - "49752" + - "49753" + - "49754" + - "49755" + - "49756" + - "49757" + - "49758" + - "49759" + - "49760" + - "49761" + - "49762" + - "49763" + - "49764" + - "49765" + - "49766" + - "49767" + - "49768" + - "49769" + - "49770" + - "49771" + - "49772" + - "49773" + - "49774" + - "49775" + - "49776" + - "49777" + - "49778" + - "49779" + - "49780" + - "49781" + - "49782" + - "49783" + - "49784" + - "49785" + - "49786" + - "49787" + - "49788" + - "49789" + - "49790" + - "49791" + - "49792" + - "49793" + - "49794" + - "49795" + - "49796" + - "49797" + - "49798" + - "49799" + - "49800" + - "49801" + - "49802" + - "49803" + - "49804" + - "49805" + - "49806" + - "49807" + - "49808" + - "49809" + - "49810" + - "49811" + - "49812" + - "49813" + - "49814" + - "49815" + - "49816" + +# More info about Globals: https://github.com/awslabs/serverless-application-model/blob/master/docs/globals.rst +Globals: + Function: + Timeout: 3 + +Resources: + SomeFunction: + Type: AWS::Serverless::Function + Properties: + Handler: hello_world/app.lambda_handler + CodeUri: . + Runtime: python2.7 + Timeout: 900 From 108c346cb153aa352b00eb539f16fa9607ebd785 Mon Sep 17 00:00:00 2001 From: Kyle Thomson Date: Fri, 21 Aug 2020 13:39:31 -0700 Subject: [PATCH 0034/1546] Removed duplication from ide profile (#2008) * Removed duplication from ide profile * move versions to the top as that is probably the most common thing to look at * Adding missed database plugin * Syntactic sugar * Breaking out plugin lists again Co-authored-by: Hunter Werlla --- build.gradle.kts | 28 +- .../aws/toolkits/gradle/IdeVersions.kt | 257 +++++++----------- .../aws/toolkits/gradle/SourcesUtils.kt | 6 +- jetbrains-core/build.gradle.kts | 17 +- jetbrains-rider/build.gradle.kts | 25 +- jetbrains-ultimate/build.gradle.kts | 7 +- 6 files changed, 135 insertions(+), 205 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 63ec314331..19fb8d042e 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -12,7 +12,6 @@ import org.jetbrains.intellij.tasks.RunIdeTask import org.jetbrains.intellij.tasks.VerifyPluginTask import org.jetbrains.kotlin.gradle.tasks.KotlinCompile import software.aws.toolkits.gradle.IdeVersions -import software.aws.toolkits.gradle.ProductCode import software.aws.toolkits.gradle.changelog.tasks.GenerateGithubChangeLog import software.aws.toolkits.gradle.findFolders import software.aws.toolkits.gradle.getOrCreate @@ -36,8 +35,7 @@ buildscript { } } -val ideVersions = IdeVersions(project) -val ideVersion = ideVersions.resolveShortenedIdeProfileName() +val ideProfile = IdeVersions.ideProfile(project) val toolkitVersion: String by project val kotlinVersion: String by project val mockitoVersion: String by project @@ -59,7 +57,7 @@ plugins { group = "software.aws.toolkits" // please check changelog generation logic if this format is changed -version = "$toolkitVersion-$ideVersion".toString() +version = "$toolkitVersion-${ideProfile.shortName}" repositories { maven("https://www.jetbrains.com/intellij-repository/snapshots/") @@ -129,18 +127,18 @@ subprojects { sourceSets { main { - java.srcDirs(findFolders(project, "src", ideVersion)) - resources.srcDirs(findFolders(project, "resources", ideVersion)) + java.srcDirs(findFolders(project, "src", ideProfile)) + resources.srcDirs(findFolders(project, "resources", ideProfile)) } test { - java.srcDirs(findFolders(project, "tst", ideVersion)) - resources.srcDirs(findFolders(project, "tst-resources", ideVersion)) + java.srcDirs(findFolders(project, "tst", ideProfile)) + resources.srcDirs(findFolders(project, "tst-resources", ideProfile)) } getOrCreate("integrationTest") { compileClasspath += main.get().output + test.get().output runtimeClasspath += main.get().output + test.get().output - java.srcDirs(findFolders(project, "it", ideVersion)) - resources.srcDirs(findFolders(project, "it-resources", ideVersion)) + java.srcDirs(findFolders(project, "it", ideProfile)) + resources.srcDirs(findFolders(project, "it-resources", ideProfile)) } } @@ -188,16 +186,16 @@ subprojects { model.module.apply { sourceDirs.plusAssign(sourceSets.main.get().java.srcDirs) resourceDirs.plusAssign(sourceSets.main.get().resources.srcDirs) - testSourceDirs.plusAssign(File("tst-$ideVersion")) - testResourceDirs.plusAssign(File("tst-resources-$ideVersion")) + testSourceDirs.plusAssign(File("tst-${ideProfile.shortName}")) + testResourceDirs.plusAssign(File("tst-resources-${ideProfile.shortName}")) sourceDirs.minusAssign(File("it")) testSourceDirs.plusAssign(File("it")) - testSourceDirs.plusAssign(File("it-$ideVersion")) + testSourceDirs.plusAssign(File("it-${ideProfile.shortName}")) resourceDirs.minusAssign(File("it-resources")) testResourceDirs.plusAssign(File("it-resources")) - testResourceDirs.plusAssign(File("it-resources-$ideVersion")) + testResourceDirs.plusAssign(File("it-resources-${ideProfile.shortName}")) } } @@ -278,7 +276,7 @@ apply(plugin = "org.jetbrains.intellij") apply(plugin = "toolkit-change-log") intellij { - version = ideVersions.ideSdkVersion(ProductCode.IC) + version = ideProfile.community.sdkVersion pluginName = "aws-jetbrains-toolkit" updateSinceUntilBuild = false downloadSources = System.getenv("CI") == null diff --git a/buildSrc/src/software/aws/toolkits/gradle/IdeVersions.kt b/buildSrc/src/software/aws/toolkits/gradle/IdeVersions.kt index d3949edaeb..854da7c26b 100644 --- a/buildSrc/src/software/aws/toolkits/gradle/IdeVersions.kt +++ b/buildSrc/src/software/aws/toolkits/gradle/IdeVersions.kt @@ -5,179 +5,116 @@ package software.aws.toolkits.gradle import org.gradle.api.Project -enum class ProductCode { - IC, - IU, - RD +object IdeVersions { + fun ideProfile(project: Project): Profile { + val profileName = resolveIdeProfileName(project) + return ideProfiles[profileName] ?: throw IllegalStateException("Can't find profile for $profileName") + } + + private val ideProfiles = listOf( + Profile( + name = "2019.3", + communityPlugins = listOf( + "java", + "com.intellij.gradle", + "org.jetbrains.idea.maven", + "PythonCore:193.5233.139", + "Docker:193.5233.140" + ), + ultimatePlugins = listOf( + "JavaScript", + "JavaScriptDebugger", + "Pythonid:193.5233.109" + ), + riderSdkOverride = "RD-2019.3.4", + rdGenVersion = "0.193.146", + nugetVersion = "2019.3.4" + ), + Profile( + name = "2020.1", + communityPlugins = listOf( + "java", + "com.intellij.gradle", + "org.jetbrains.idea.maven", + "PythonCore:201.6668.31", + "Docker:201.6668.30" + ), + ultimatePlugins = listOf( + "JavaScript", + "JavaScriptDebugger", + "com.intellij.database", + "Pythonid:201.6668.31" + ), + riderSdkOverride = "RD-2020.1.0", + rdGenVersion = "0.201.69", + nugetVersion = "2020.1.0" + ), + Profile( + name = "2020.2", + communityPlugins = listOf( + "java", + "com.intellij.gradle", + "org.jetbrains.idea.maven", + "PythonCore:202.6397.124", + "Docker:202.6397.93" + ), + ultimatePlugins = listOf( + "JavaScript", + "JavaScriptDebugger", + "com.intellij.database", + "Pythonid:202.6397.98" + ), + rdGenVersion = "0.202.113", + nugetVersion = "2020.2.0" + ) + ).associateBy { it.name } + + private fun resolveIdeProfileName(project: Project): String = if (System.getenv()["ALTERNATIVE_IDE_PROFILE_NAME"] != null) { + System.getenv("ALTERNATIVE_IDE_PROFILE_NAME") + } else { + project.properties["ideProfileName"]?.toString() ?: throw IllegalStateException("No ideProfileName property set") + } } open class ProductProfile( val sdkVersion: String, - val plugins: List + val plugins: Array ) class RiderProfile( sdkVersion: String, - plugins: List, + plugins: Array, val rdGenVersion: String, val nugetVersion: String ) : ProductProfile(sdkVersion, plugins) -data class Profile( - val sinceVersion: String, - val untilVersion: String, - val products: Map -) - -class IdeVersions(private val project: Project) { - private val ideProfiles = mapOf( - "2019.3" to Profile( - sinceVersion = "193", - untilVersion = "193.*", - products = mapOf( - ProductCode.IC to ProductProfile( - sdkVersion = "IC-2019.3", - plugins = listOf( - "org.jetbrains.plugins.terminal", - "org.jetbrains.plugins.yaml", - "PythonCore:193.5233.139", - "java", - "com.intellij.gradle", - "org.jetbrains.idea.maven", - "Docker:193.5233.140" - ) - ), - ProductCode.IU to ProductProfile( - sdkVersion = "IU-2019.3", - plugins = listOf( - "org.jetbrains.plugins.terminal", - "Pythonid:193.5233.109", - "org.jetbrains.plugins.yaml", - "JavaScript", - "JavaScriptDebugger" - ) - ), - ProductCode.RD to RiderProfile( - sdkVersion = "RD-2019.3.4", - rdGenVersion = "0.193.146", - nugetVersion = "2019.3.4", - plugins = listOf( - "org.jetbrains.plugins.yaml" - ) - ) - ) - ), - "2020.1" to Profile( - sinceVersion = "201", - untilVersion = "201.*", - products = mapOf( - ProductCode.IC to ProductProfile( - sdkVersion = "IC-2020.1", - plugins = listOf( - "org.jetbrains.plugins.terminal", - "org.jetbrains.plugins.yaml", - "PythonCore:201.6668.31", - "java", - "com.intellij.gradle", - "org.jetbrains.idea.maven", - "Docker:201.6668.30" - ) - ), - ProductCode.IU to ProductProfile( - sdkVersion = "IU-2020.1", - plugins = listOf( - "org.jetbrains.plugins.terminal", - "Pythonid:201.6668.31", - "org.jetbrains.plugins.yaml", - "JavaScript", - "JavaScriptDebugger", - "com.intellij.database" - ) - ), - ProductCode.RD to RiderProfile( - sdkVersion = "RD-2020.1.0", - rdGenVersion = "0.201.69", - nugetVersion = "2020.1.0", - plugins = listOf( - "org.jetbrains.plugins.yaml" - ) - ) - ) - ), - "2020.2" to Profile( - sinceVersion = "202", - untilVersion = "202.*", - products = mapOf( - ProductCode.IC to ProductProfile( - sdkVersion = "IC-2020.2", - plugins = listOf( - "org.jetbrains.plugins.terminal", - "org.jetbrains.plugins.yaml", - "PythonCore:202.6397.124", - "java", - "com.intellij.gradle", - "org.jetbrains.idea.maven", - "Docker:202.6397.93" - ) - ), - ProductCode.IU to ProductProfile( - sdkVersion = "IU-2020.2", - plugins = listOf( - "org.jetbrains.plugins.terminal", - "Pythonid:202.6397.98", - "org.jetbrains.plugins.yaml", - "JavaScript", - "JavaScriptDebugger", - "com.intellij.database" - ) - ), - ProductCode.RD to RiderProfile( - sdkVersion = "RD-2020.2", - rdGenVersion = "0.202.113", - nugetVersion = "2020.2.0", - plugins = listOf( - "org.jetbrains.plugins.yaml" - ) - ) - ) - ) +class Profile( + val name: String, + val shortName: String = shortenedIdeProfileName(name), + val sinceVersion: String = shortName, + val untilVersion: String = "$sinceVersion.*", + communityPlugins: List, + ultimatePlugins: List, + riderSdkOverride: String? = null, + rdGenVersion: String, + nugetVersion: String +) { + private val commonPlugins = arrayOf( + "org.jetbrains.plugins.terminal", + "org.jetbrains.plugins.yaml" ) - fun sinceVersion(): String = getProfile().sinceVersion - fun untilVersion() = getProfile().untilVersion - - fun sdkVersion(code: ProductCode): String = getProductProfile(code).sdkVersion - fun plugins(code: ProductCode): List = getProductProfile(code).plugins - - fun rdGenVersion(): String = getRiderProfile().rdGenVersion - fun nugetSdkVersion(): String = getRiderProfile().nugetVersion - - // Convert (as an example) 2020.2 -> 202 - fun resolveShortenedIdeProfileName(): String { - val profileName = resolveIdeProfileName().trim() - val parts = profileName.split(".") - return parts[0].substring(2) + parts[1] - } - - fun ideSdkVersion(code: ProductCode): String = ideProfiles[resolveIdeProfileName()] - ?.products - ?.get(code) - ?.sdkVersion - ?: throw IllegalArgumentException("Product not in map of IDE versions: ${resolveIdeProfileName()}, $code") - - fun resolveIdeProfileName(): String = if (System.getenv()["ALTERNATIVE_IDE_PROFILE_NAME"] != null) { - System.getenv("ALTERNATIVE_IDE_PROFILE_NAME") - } else { - project.properties["ideProfileName"]?.toString() ?: throw IllegalStateException("No ideProfileName property set") - } - - private fun getProfile(): Profile = - ideProfiles[resolveIdeProfileName()] ?: throw IllegalStateException("Unable to resolve profile ${resolveIdeProfileName()}") - - private fun getProductProfile(code: ProductCode): ProductProfile = - ideProfiles[resolveIdeProfileName()]?.products?.get(code) - ?: throw IllegalStateException("Unable to get profile ${resolveIdeProfileName()} code $code") + val community: ProductProfile = ProductProfile(sdkVersion = "IC-$name", plugins = commonPlugins + communityPlugins) + val ultimate: ProductProfile = ProductProfile(sdkVersion = "IU-$name", plugins = commonPlugins + ultimatePlugins) + val rider: RiderProfile = RiderProfile( + sdkVersion = riderSdkOverride ?: "RD-$name", + plugins = arrayOf("org.jetbrains.plugins.yaml"), + rdGenVersion = rdGenVersion, + nugetVersion = nugetVersion + ) +} - private fun getRiderProfile(): RiderProfile = ideProfiles[resolveIdeProfileName()]?.products?.get(ProductCode.RD) as? RiderProfile - ?: throw IllegalStateException("Failed to get Rider profile for ${resolveIdeProfileName()}!") +private fun shortenedIdeProfileName(sdkName: String): String { + val parts = sdkName.trim().split(".") + return parts[0].substring(2) + parts[1] } diff --git a/buildSrc/src/software/aws/toolkits/gradle/SourcesUtils.kt b/buildSrc/src/software/aws/toolkits/gradle/SourcesUtils.kt index a0acc41769..203edaa8b4 100644 --- a/buildSrc/src/software/aws/toolkits/gradle/SourcesUtils.kt +++ b/buildSrc/src/software/aws/toolkits/gradle/SourcesUtils.kt @@ -12,10 +12,10 @@ import java.io.FileFilter * * [project] the project to use as a directory base * [type] is the type of the source folder (e.g. 'src', 'tst', 'resources') - * [ideVersion] is the 3 digit numerical version of the JetBrains SDK (e.g. 192, 201 etc) + * [ideProfile] is the IDE [Profile] currently configured in the project */ -fun findFolders(project: Project, type: String, ideVersion: String): Set = project.projectDir.listFiles(FileFilter { - it.isDirectory && includeFolder(type, ideVersion, it.name) +fun findFolders(project: Project, type: String, ideProfile: Profile): Set = project.projectDir.listFiles(FileFilter { + it.isDirectory && includeFolder(type, ideProfile.shortName, it.name) })?.map { File(it.name) }?.toSet() ?: setOf() /** diff --git a/jetbrains-core/build.gradle.kts b/jetbrains-core/build.gradle.kts index fa481a6a19..ecdfa12996 100644 --- a/jetbrains-core/build.gradle.kts +++ b/jetbrains-core/build.gradle.kts @@ -1,12 +1,10 @@ // Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import org.jetbrains.intellij.tasks.PatchPluginXmlTask import org.jetbrains.kotlin.gradle.tasks.KotlinCompile -import software.aws.toolkits.telemetry.generator.gradle.GenerateTelemetry -import software.aws.toolkits.gradle.changelog.tasks.GeneratePluginChangeLog import software.aws.toolkits.gradle.IdeVersions -import software.aws.toolkits.gradle.ProductCode +import software.aws.toolkits.gradle.changelog.tasks.GeneratePluginChangeLog +import software.aws.toolkits.telemetry.generator.gradle.GenerateTelemetry plugins { id("org.jetbrains.intellij") @@ -23,7 +21,7 @@ buildscript { } } -val ideVersions = IdeVersions(project) +val ideProfile = IdeVersions.ideProfile(project) val telemetryVersion: String by project val awsSdkVersion: String by project val coroutinesVersion: String by project @@ -32,17 +30,16 @@ val compileKotlin: KotlinCompile by tasks intellij { val rootIntelliJTask = rootProject.intellij - version = ideVersions.sdkVersion(ProductCode.IC) - setPlugins(*ideVersions.plugins(ProductCode.IC).toTypedArray()) + version = ideProfile.community.sdkVersion + setPlugins(*ideProfile.community.plugins) pluginName = rootIntelliJTask.pluginName updateSinceUntilBuild = rootIntelliJTask.updateSinceUntilBuild downloadSources = rootIntelliJTask.downloadSources - } tasks.patchPluginXml { - setSinceBuild(ideVersions.sinceVersion()) - setUntilBuild(ideVersions.untilVersion()) + setSinceBuild(ideProfile.sinceVersion) + setUntilBuild(ideProfile.untilVersion) } configurations { diff --git a/jetbrains-rider/build.gradle.kts b/jetbrains-rider/build.gradle.kts index d02fb81ffd..6d0671c842 100644 --- a/jetbrains-rider/build.gradle.kts +++ b/jetbrains-rider/build.gradle.kts @@ -5,10 +5,9 @@ import com.jetbrains.rd.generator.gradle.RdgenParams import com.jetbrains.rd.generator.gradle.RdgenTask import org.jetbrains.intellij.tasks.PrepareSandboxTask import software.aws.toolkits.gradle.IdeVersions -import software.aws.toolkits.gradle.ProductCode buildscript { - val rdversion = software.aws.toolkits.gradle.IdeVersions(project).rdGenVersion() + val rdversion = software.aws.toolkits.gradle.IdeVersions.ideProfile(project).rider.rdGenVersion logger.info("Using rd-gen: $rdversion") @@ -28,7 +27,7 @@ plugins { apply(plugin = "com.jetbrains.rdgen") -val ideVersions = IdeVersions(project) +val ideProfile = IdeVersions.ideProfile(project) val resharperPluginPath = File(projectDir, "ReSharper.AWS") val resharperBuildPath = File(project.buildDir, "dotnetBuild") @@ -51,13 +50,13 @@ rdgenDir.mkdirs() intellij { val parentIntellijTask = rootProject.intellij - version = ideVersions.sdkVersion(ProductCode.RD) + version = ideProfile.rider.sdkVersion pluginName = parentIntellijTask.pluginName updateSinceUntilBuild = parentIntellijTask.updateSinceUntilBuild // Workaround for https://youtrack.jetbrains.com/issue/IDEA-179607 val extraPlugins = arrayOf("rider-plugins-appender") - setPlugins(*(ideVersions.plugins(ProductCode.RD) + extraPlugins).toTypedArray()) + setPlugins(*(ideProfile.rider.plugins + extraPlugins)) // Disable downloading source to avoid issues related to Rider SDK naming that is missed in Idea // snapshots repository. The task is failed because if is unable to find related IC sources. @@ -69,7 +68,7 @@ val generateDaemonModel = tasks.register("generateDaemonModel") { val daemonModelSource = File(modelDir, "daemon").canonicalPath val ktOutput = File(riderGeneratedSources, "DaemonProtocol") - inputs.property("rdgen", ideVersions.rdGenVersion()) + inputs.property("rdgen", ideProfile.rider.rdGenVersion) inputs.dir(daemonModelSource) outputs.dirs(ktOutput, csDaemonGeneratedOutput) @@ -113,7 +112,7 @@ val generatePsiModel = tasks.register("generatePsiModel") { val psiModelSource = File(modelDir, "psi").canonicalPath val ktOutput = File(riderGeneratedSources, "PsiProtocol") - inputs.property("rdgen", ideVersions.rdGenVersion()) + inputs.property("rdgen", ideProfile.rider.rdGenVersion) inputs.dir(psiModelSource) outputs.dirs(ktOutput, csPsiGeneratedOutput) @@ -157,7 +156,7 @@ val generateAwsSettingModel = tasks.register("generateAwsSettingModel val settingModelSource = File(modelDir, "setting").canonicalPath val ktOutput = File(riderGeneratedSources, "AwsSettingsProtocol") - inputs.property("rdgen", ideVersions.rdGenVersion()) + inputs.property("rdgen", ideProfile.rider.rdGenVersion) inputs.dir(settingModelSource) outputs.dirs(ktOutput, csAwsSettingGeneratedOutput) @@ -200,7 +199,7 @@ val generateAwsProjectModel = tasks.register("generateAwsProjectModel val projectModelSource = File(modelDir, "project").canonicalPath val ktOutput = File(riderGeneratedSources, "AwsProjectProtocol") - inputs.property("rdgen", ideVersions.rdGenVersion()) + inputs.property("rdgen", ideProfile.rider.rdGenVersion) inputs.dir(projectModelSource) outputs.dirs(ktOutput, csAwsProjectGeneratedOutput) @@ -266,11 +265,11 @@ val prepareBuildProps = tasks.register("prepareBuildProps") { val riderSdkVersionPropsPath = File(resharperPluginPath, "RiderSdkPackageVersion.props") group = backendGroup - inputs.property("riderNugetSdkVersion", ideVersions.nugetSdkVersion()) + inputs.property("riderNugetSdkVersion", ideProfile.rider.nugetVersion) outputs.file(riderSdkVersionPropsPath) doLast { - val riderSdkVersion = ideVersions.nugetSdkVersion() + val riderSdkVersion = ideProfile.rider.nugetVersion val configText = """ [$riderSdkVersion] @@ -286,7 +285,7 @@ val prepareNuGetConfig = tasks.register("prepareNuGetConfig") { val nugetConfigPath = File(projectDir, "NuGet.Config") - inputs.property("rdVersion",ideVersions.sdkVersion(ProductCode.RD)) + inputs.property("rdVersion", ideProfile.rider.sdkVersion) outputs.file(nugetConfigPath) doLast { @@ -321,7 +320,7 @@ val buildReSharperPlugin = tasks.register("buildReSharperPlugin") { val arguments = listOf( "build", "${resharperPluginPath.canonicalPath}/ReSharper.AWS.sln", - "/p:DefineConstants=\"PROFILE_${ideVersions.resolveIdeProfileName().replace(".", "_")}\"" + "/p:DefineConstants=\"PROFILE_${ideProfile.name.replace(".", "_")}\"" ) exec { executable = "dotnet" diff --git a/jetbrains-ultimate/build.gradle.kts b/jetbrains-ultimate/build.gradle.kts index 176592cec5..13fb2fe34f 100644 --- a/jetbrains-ultimate/build.gradle.kts +++ b/jetbrains-ultimate/build.gradle.kts @@ -3,7 +3,6 @@ import org.jetbrains.intellij.IntelliJPluginExtension import software.aws.toolkits.gradle.IdeVersions -import software.aws.toolkits.gradle.ProductCode plugins { id("org.jetbrains.intellij") @@ -16,12 +15,12 @@ dependencies { integrationTestImplementation(project(path = ":jetbrains-core", configuration = "testArtifacts")) } -val ideVersions = IdeVersions(project) +val ideProfile = IdeVersions.ideProfile(project) intellij { val parentIntellijTask = rootProject.intellij - version = ideVersions.sdkVersion(ProductCode.IU) - setPlugins(*ideVersions.plugins(ProductCode.IU).toTypedArray()) + version = ideProfile.ultimate.sdkVersion + setPlugins(*ideProfile.ultimate.plugins) pluginName = parentIntellijTask.pluginName updateSinceUntilBuild = parentIntellijTask.updateSinceUntilBuild downloadSources = parentIntellijTask.downloadSources From 6e6aead55fa2da1f7dc8c9a446638bce4c660612 Mon Sep 17 00:00:00 2001 From: Austin Brooks Date: Fri, 21 Aug 2020 14:12:09 -0700 Subject: [PATCH 0035/1546] Gate all of Java8.al2 on SAM CLI 1.2.0 (#2009) --- ...vaLocalLambdaRunConfigurationIntegrationTest.kt | 14 +++++++++++++- .../services/lambda/java/JavaRuntimeGroup.kt | 2 +- .../services/lambda/java/JavaSamDebugSupport.kt | 3 --- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/jetbrains-core/it/software/aws/toolkits/jetbrains/services/lambda/java/JavaLocalLambdaRunConfigurationIntegrationTest.kt b/jetbrains-core/it/software/aws/toolkits/jetbrains/services/lambda/java/JavaLocalLambdaRunConfigurationIntegrationTest.kt index 7f7caaf51c..33adc1aeaf 100644 --- a/jetbrains-core/it/software/aws/toolkits/jetbrains/services/lambda/java/JavaLocalLambdaRunConfigurationIntegrationTest.kt +++ b/jetbrains-core/it/software/aws/toolkits/jetbrains/services/lambda/java/JavaLocalLambdaRunConfigurationIntegrationTest.kt @@ -6,6 +6,7 @@ package software.aws.toolkits.jetbrains.services.lambda.java import com.intellij.compiler.CompilerTestUtil import com.intellij.execution.executors.DefaultDebugExecutor import com.intellij.testFramework.runInEdtAndWait +import com.intellij.util.text.SemVer import org.assertj.core.api.Assertions.assertThat import org.junit.After import org.junit.Assume.assumeTrue @@ -17,8 +18,11 @@ import org.junit.runners.Parameterized import software.amazon.awssdk.auth.credentials.AwsBasicCredentials import software.amazon.awssdk.services.lambda.model.Runtime import software.aws.toolkits.jetbrains.core.credentials.MockCredentialsManager +import software.aws.toolkits.jetbrains.core.executables.ExecutableManager +import software.aws.toolkits.jetbrains.core.executables.getExecutableIfPresent import software.aws.toolkits.jetbrains.services.lambda.execution.local.createHandlerBasedRunConfiguration import software.aws.toolkits.jetbrains.services.lambda.execution.local.createTemplateRunConfiguration +import software.aws.toolkits.jetbrains.services.lambda.sam.SamExecutable import software.aws.toolkits.jetbrains.utils.addBreakpoint import software.aws.toolkits.jetbrains.utils.checkBreakPointHit import software.aws.toolkits.jetbrains.utils.executeRunConfiguration @@ -170,7 +174,15 @@ class JavaLocalLambdaRunConfigurationIntegrationTest(private val runtime: Runtim @Test fun samIsExecutedWithDebugger() { - assumeTrue(runtime != Runtime.JAVA8_AL2) + // TODO Remove when SAM 1.2.0 is out + if (runtime == Runtime.JAVA8_AL2) { + val samVersion = ExecutableManager.getInstance().getExecutableIfPresent().version?.let { + SemVer.parseFromText(it) + } + println(samVersion) + assumeTrue(samVersion?.isGreaterOrEqualThan(1, 2, 0) == true) + } + projectRule.addBreakpoint() val runConfiguration = createHandlerBasedRunConfiguration( diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/java/JavaRuntimeGroup.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/java/JavaRuntimeGroup.kt index 36636035c9..6f23e25cd9 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/java/JavaRuntimeGroup.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/java/JavaRuntimeGroup.kt @@ -23,7 +23,7 @@ class JavaRuntimeGroup : SdkBasedRuntimeGroup() { override val supportedRuntimes: List = listOf( RuntimeInfo(Runtime.JAVA8), - RuntimeInfo(Runtime.JAVA8_AL2, SemVer("1.1.0", 1, 1, 0)), + RuntimeInfo(Runtime.JAVA8_AL2, SemVer("1.2.0", 1, 2, 0)), RuntimeInfo(Runtime.JAVA11) ) diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/java/JavaSamDebugSupport.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/java/JavaSamDebugSupport.kt index ba5b14ff63..93716fd92f 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/java/JavaSamDebugSupport.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/java/JavaSamDebugSupport.kt @@ -13,7 +13,6 @@ import com.intellij.xdebugger.XDebugProcess import com.intellij.xdebugger.XDebugProcessStarter import com.intellij.xdebugger.XDebugSession import com.intellij.xdebugger.impl.XDebugSessionImpl -import software.amazon.awssdk.services.lambda.model.Runtime import software.aws.toolkits.jetbrains.services.lambda.execution.local.SamDebugSupport import software.aws.toolkits.jetbrains.services.lambda.execution.local.SamRunningState @@ -44,6 +43,4 @@ class JavaSamDebugSupport : SamDebugSupport { } } } - - override fun isSupported(runtime: Runtime): Boolean = runtime != Runtime.JAVA8_AL2 } From 37fc6cecd9c8556282364c42094191bda03e93e5 Mon Sep 17 00:00:00 2001 From: Christopher Christou <39839589+awschristou@users.noreply.github.com> Date: Mon, 24 Aug 2020 07:57:39 -0700 Subject: [PATCH 0036/1546] Typo and grammar in credentials doc (#2010) --- designs/credentialManagement/credentialManagement.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/designs/credentialManagement/credentialManagement.md b/designs/credentialManagement/credentialManagement.md index 6cb560e707..c75a21c0c3 100644 --- a/designs/credentialManagement/credentialManagement.md +++ b/designs/credentialManagement/credentialManagement.md @@ -17,14 +17,14 @@ by third party plugins. ## Classes and Concepts ![ClassDiagram] -1. `AwsRegion` - Date class that represents an AWS Region and joins together related data for that region. This data is sourced from the endpoints.json file. +1. `AwsRegion` - Data class that represents an AWS Region and joins together related data for that region. This data is sourced from the endpoints.json file. It contains the following data: 1. `ID` - Contains the ID of the region (e.g. `us-west-2`) 1. `Name` - Contains the human readable name for the region (e.g. `US West (Oregon)`) 1. `Partiton ID` - Contains the ID of the top level AWS partition (e.g. `aws`, `aws-cn`) -1. `CredentialIdentifier` - Represents the globally unique identifier for a possible credential profile in the toolkit. This identifier must be deterministic -meaning that if two `CredentialIdentifier`s for the same credential source should be equal even across different IDE sessions. +1. `CredentialIdentifier` - Represents the globally unique identifier for a possible credential profile in the toolkit. This identifier must be deterministic, +meaning a credential source should have identical `CredentialIdentifier` values when represented by different instances, or when used across different IDE sessions. This is shown to the user as the **Profile** in the UI. 1. `AwsCredentialsProvider` - SDK interface that resolves AWS Credentials from the provider. For more info, see [AwsCredentialsProvider] in the SDK. @@ -69,8 +69,9 @@ If the call fails, we consider the credentials to be invalid. The class [AwsConnectionManager] is the entry point into this system. -The concept of _Active Connection Settings_ represents the current user selected credentials and region that the toolkit uses to perform actions in the AWS Explorer as well as -being used as defaults when more than one option is possible. +The concept of _Active Connection Settings_ represents the user's currently selected credentials and region. They are used by the toolkit: +* to perform actions in the AWS Explorer +* as defaults when presenting more than one option in a dialog Due to the nature of the IntellJ projects (project level) each has their own windows while existing in one JVM (application level). Since we store active connection settings at the project level, each window can have a different active `CredenitalIdentifier` and/or `AwsRegion` selected. From af73055efba90d4e0a62a075b6dec7fcccbed597 Mon Sep 17 00:00:00 2001 From: Austin Brooks Date: Mon, 24 Aug 2020 15:17:54 -0700 Subject: [PATCH 0037/1546] Expire the swtich region notification after action is pressed (#2013) --- .../core/credentials/CredentialsRegionHandler.kt | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/core/credentials/CredentialsRegionHandler.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/core/credentials/CredentialsRegionHandler.kt index 8b31aca35a..218e7826a7 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/core/credentials/CredentialsRegionHandler.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/core/credentials/CredentialsRegionHandler.kt @@ -49,14 +49,16 @@ internal open class DefaultCredentialsRegionHandler(private val project: Project message("settings.credentials.prompt_for_default_region_switch", defaultCredentialRegion.id), project = project, notificationActions = listOf( - NotificationAction.create(message("settings.credentials.prompt_for_default_region_switch.yes")) { event, _ -> + NotificationAction.create(message("settings.credentials.prompt_for_default_region_switch.yes")) { event, notification -> ChangeRegionAction(defaultCredentialRegion).actionPerformed(event) + notification.expire() }, - NotificationAction.create(message("settings.credentials.prompt_for_default_region_switch.always")) { event, _ -> + NotificationAction.create(message("settings.credentials.prompt_for_default_region_switch.always")) { event, notification -> settings.useDefaultCredentialRegion = UseAwsCredentialRegion.Always ChangeRegionAction(defaultCredentialRegion).actionPerformed(event) + notification.expire() }, - NotificationAction.createSimple(message("settings.credentials.prompt_for_default_region_switch.never")) { + NotificationAction.createSimpleExpiring(message("settings.credentials.prompt_for_default_region_switch.never")) { settings.useDefaultCredentialRegion = UseAwsCredentialRegion.Never } ) From 934b3b1ed181cd4365af86bfb0879d93120835d3 Mon Sep 17 00:00:00 2001 From: Austin Brooks Date: Mon, 24 Aug 2020 15:55:11 -0700 Subject: [PATCH 0038/1546] Fix bugs in CredentialManager (#2014) --- .../core/credentials/CredentialManager.kt | 26 ++-- .../core/credentials/CredentialManagerTest.kt | 134 ++++++++++-------- 2 files changed, 90 insertions(+), 70 deletions(-) diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/core/credentials/CredentialManager.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/core/credentials/CredentialManager.kt index 1757d3b95c..74673aac11 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/core/credentials/CredentialManager.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/core/credentials/CredentialManager.kt @@ -25,13 +25,13 @@ import java.util.concurrent.ConcurrentHashMap abstract class CredentialManager : SimpleModificationTracker() { private val providerIds = ConcurrentHashMap() - private val awsCredentialProviderCache = ConcurrentHashMap>() + private val awsCredentialProviderCache = ConcurrentHashMap>() protected abstract fun factoryMapping(): Map @Throws(CredentialProviderNotFoundException::class) fun getAwsCredentialProvider(providerId: CredentialIdentifier, region: AwsRegion): ToolkitCredentialsProvider = - ToolkitCredentialsProvider(providerId, AwsCredentialProviderProxy(providerId, region)) + ToolkitCredentialsProvider(providerId, AwsCredentialProviderProxy(providerId.id, region)) fun getCredentialIdentifiers(): List = providerIds.values .sortedWith(compareBy(String.CASE_INSENSITIVE_ORDER) { it.displayName }) @@ -47,7 +47,8 @@ abstract class CredentialManager : SimpleModificationTracker() { } protected fun modifyProvider(identifier: CredentialIdentifier) { - awsCredentialProviderCache.remove(identifier) + awsCredentialProviderCache.remove(identifier.id) + providerIds[identifier.id] = identifier incModificationCount() ApplicationManager.getApplication().messageBus.syncPublisher(CREDENTIALS_CHANGED).providerModified(identifier) @@ -55,7 +56,7 @@ abstract class CredentialManager : SimpleModificationTracker() { protected fun removeProvider(identifier: CredentialIdentifier) { providerIds.remove(identifier.id) - awsCredentialProviderCache.remove(identifier) + awsCredentialProviderCache.remove(identifier.id) incModificationCount() ApplicationManager.getApplication().messageBus.syncPublisher(CREDENTIALS_CHANGED).providerRemoved(identifier) @@ -66,20 +67,23 @@ abstract class CredentialManager : SimpleModificationTracker() { * ToolkitCredentialsProvider (like ones passed to existing SDK clients), to keep operating even if the credentials they represent have been updated such * as loading from disk when new values. */ - private inner class AwsCredentialProviderProxy(private val providerId: CredentialIdentifier, private val region: AwsRegion) : - AwsCredentialsProvider { + private inner class AwsCredentialProviderProxy(private val providerId: String, private val region: AwsRegion) : AwsCredentialsProvider { override fun resolveCredentials(): AwsCredentials = getOrCreateAwsCredentialsProvider(providerId, region).resolveCredentials() - private fun getOrCreateAwsCredentialsProvider(providerId: CredentialIdentifier, region: AwsRegion): AwsCredentialsProvider { + private fun getOrCreateAwsCredentialsProvider(providerId: String, region: AwsRegion): AwsCredentialsProvider { + // Validate that the provider ID is still valid and get the latest copy + val identifier = providerIds[providerId] + ?: throw CredentialProviderNotFoundException("Provider ID $providerId was removed, can't resolve credentials") + val partitionCache = awsCredentialProviderCache.computeIfAbsent(providerId) { ConcurrentHashMap() } - // If we already resolved creds for this partition and provider ID, just return it + // If we already resolved creds for this partition and provider ID, just return it, else compute new one return partitionCache.computeIfAbsent(region.partitionId) { - val providerFactory = factoryMapping()[providerId.factoryId] - ?: throw CredentialProviderNotFoundException("No provider found with ID ${providerId.id}") + val providerFactory = factoryMapping()[identifier.factoryId] + ?: throw CredentialProviderNotFoundException("No provider factory found with ID ${identifier.factoryId}") try { - providerFactory.createAwsCredentialProvider(providerId, region) { AwsSdkClient.getInstance().sdkHttpClient } + providerFactory.createAwsCredentialProvider(identifier, region) { AwsSdkClient.getInstance().sdkHttpClient } } catch (e: Exception) { throw CredentialProviderNotFoundException("Failed to create underlying AwsCredentialProvider", e) } diff --git a/jetbrains-core/tst/software/aws/toolkits/jetbrains/core/credentials/CredentialManagerTest.kt b/jetbrains-core/tst/software/aws/toolkits/jetbrains/core/credentials/CredentialManagerTest.kt index e3af655952..15700c1023 100644 --- a/jetbrains-core/tst/software/aws/toolkits/jetbrains/core/credentials/CredentialManagerTest.kt +++ b/jetbrains-core/tst/software/aws/toolkits/jetbrains/core/credentials/CredentialManagerTest.kt @@ -40,17 +40,11 @@ class CredentialManagerTest { addFactories( createTestCredentialFactory( "testFactory1", - mapOf( - "testFoo1" to region to createCredentials("testFoo1"), - "testBar1" to region to createCredentials("testBar1") - ) + listOf("testFoo1", "testBar1") ), createTestCredentialFactory( "testFactory2", - mapOf( - "testFoo2" to region to createCredentials("testFoo2"), - "testBar2" to region to createCredentials("testBar2") - ) + listOf("testFoo2", "testBar2") ) ) @@ -64,8 +58,8 @@ class CredentialManagerTest { val credentialProvider = credentialManager.getAwsCredentialProvider(credentialsIdentifier, region) assertThat(credentialProvider.resolveCredentials()).isInstanceOfSatisfying(AwsBasicCredentials::class.java) { - assertThat(it.accessKeyId()).isEqualTo("testFoo2Access") - assertThat(it.secretAccessKey()).isEqualTo("testFoo2Secret") + assertThat(it.accessKeyId()).isEqualTo("testFoo2-aws-Access") + assertThat(it.secretAccessKey()).isEqualTo("testFoo2-aws-Secret") } } @@ -77,10 +71,7 @@ class CredentialManagerTest { addFactories( createTestCredentialFactory( "testFactory1", - mapOf( - "testFoo1" to partition1 to createCredentials(partition1.partitionId), - "testFoo1" to partition2 to createCredentials(partition2.partitionId) - ) + listOf("testFoo1") ) ) @@ -92,15 +83,15 @@ class CredentialManagerTest { val credentialProvider = credentialManager.getAwsCredentialProvider(credentialsIdentifier, partition1) assertThat(credentialProvider.resolveCredentials()).isInstanceOfSatisfying(AwsBasicCredentials::class.java) { - assertThat(it.accessKeyId()).isEqualTo("aws-test-1Access") - assertThat(it.secretAccessKey()).isEqualTo("aws-test-1Secret") + assertThat(it.accessKeyId()).isEqualTo("testFoo1-aws-test-1-Access") + assertThat(it.secretAccessKey()).isEqualTo("testFoo1-aws-test-1-Secret") } val credentialProvider2 = credentialManager.getAwsCredentialProvider(credentialsIdentifier, partition2) assertThat(credentialProvider2.resolveCredentials()).isInstanceOfSatisfying(AwsBasicCredentials::class.java) { - assertThat(it.accessKeyId()).isEqualTo("aws-test-2Access") - assertThat(it.secretAccessKey()).isEqualTo("aws-test-2Secret") + assertThat(it.accessKeyId()).isEqualTo("testFoo1-aws-test-2-Access") + assertThat(it.secretAccessKey()).isEqualTo("testFoo1-aws-test-2-Secret") } } @@ -109,9 +100,7 @@ class CredentialManagerTest { val region = MockRegionProvider.getInstance().defaultRegion() val credentialFactory = createTestCredentialFactory( "testFactory1", - mapOf( - "testFoo1" to region to createCredentials("testFoo1") - ) + listOf("testFoo1") ) addFactories(credentialFactory) @@ -124,21 +113,16 @@ class CredentialManagerTest { val credentialProvider = credentialManager.getAwsCredentialProvider(credentialsIdentifier, region) assertThat(credentialProvider.resolveCredentials()).isInstanceOfSatisfying(AwsBasicCredentials::class.java) { - assertThat(it.accessKeyId()).isEqualTo("testFoo1Access") - assertThat(it.secretAccessKey()).isEqualTo("testFoo1Secret") + assertThat(it.accessKeyId()).isEqualTo("testFoo1-aws-Access") + assertThat(it.secretAccessKey()).isEqualTo("testFoo1-aws-Secret") } - credentialFactory.updateCredentials( - "testFoo1", - mapOf( - region to createCredentials("testFoo1Updated") - ) - ) + credentialFactory.updateCredentials("testFoo1", region, "Updated") // Existing references are good assertThat(credentialProvider.resolveCredentials()).isInstanceOfSatisfying(AwsBasicCredentials::class.java) { - assertThat(it.accessKeyId()).isEqualTo("testFoo1UpdatedAccess") - assertThat(it.secretAccessKey()).isEqualTo("testFoo1UpdatedSecret") + assertThat(it.accessKeyId()).isEqualTo("testFoo1-aws-Access-Updated") + assertThat(it.secretAccessKey()).isEqualTo("testFoo1-aws-Secret-Updated") } // New ones are good too @@ -148,8 +132,8 @@ class CredentialManagerTest { region ).resolveCredentials() ).isInstanceOfSatisfying(AwsBasicCredentials::class.java) { - assertThat(it.accessKeyId()).isEqualTo("testFoo1UpdatedAccess") - assertThat(it.secretAccessKey()).isEqualTo("testFoo1UpdatedSecret") + assertThat(it.accessKeyId()).isEqualTo("testFoo1-aws-Access-Updated") + assertThat(it.secretAccessKey()).isEqualTo("testFoo1-aws-Secret-Updated") } } @@ -158,9 +142,7 @@ class CredentialManagerTest { val region = MockRegionProvider.getInstance().defaultRegion() val credentialFactory = createTestCredentialFactory( "testFactory1", - mapOf( - "testFoo1" to region to createCredentials("testFoo1") - ) + listOf("testFoo1") ) addFactories(credentialFactory) @@ -173,8 +155,8 @@ class CredentialManagerTest { val credentialProvider = credentialManager.getAwsCredentialProvider(credentialsIdentifier, region) assertThat(credentialProvider.resolveCredentials()).isInstanceOfSatisfying(AwsBasicCredentials::class.java) { - assertThat(it.accessKeyId()).isEqualTo("testFoo1Access") - assertThat(it.secretAccessKey()).isEqualTo("testFoo1Secret") + assertThat(it.accessKeyId()).isEqualTo("testFoo1-aws-Access") + assertThat(it.secretAccessKey()).isEqualTo("testFoo1-aws-Secret") } credentialFactory.removeCredentials("testFoo1") @@ -193,37 +175,56 @@ class CredentialManagerTest { assertThat(credentialManager.getCredentialIdentifierById("testFoo1")).isNull() } + @Test + fun testUpdatedCredentialIdentifierIsApplied() { + val region = MockRegionProvider.getInstance().defaultRegion() + val credentialFactory = createTestCredentialFactory( + "testFactory1", + listOf("testFoo1") + ) + + addFactories(credentialFactory) + + val credentialManager = DefaultCredentialManager() + + assertThat(credentialManager.getCredentialIdentifierById("testFoo1")?.defaultRegionId).isEqualTo(region.id) + + val newRegion = MockRegionProvider.getInstance().addRegion(AwsRegion("test", "test", "test")) + + credentialFactory.updateCredentials( + "testFoo1", + newRegion + ) + + assertThat(credentialManager.getCredentialIdentifierById("testFoo1")?.defaultRegionId).isEqualTo(newRegion.id) + } + private fun addFactories(vararg factories: CredentialProviderFactory) { ExtensionTestUtil.maskExtensions(DefaultCredentialManager.EP_NAME, factories.toList(), disposableRule.disposable) } - private fun createCredentials(id: String) = StaticCredentialsProvider.create(AwsBasicCredentials.create("${id}Access", "${id}Secret")) - private fun createTestCredentialFactory( id: String, - initialCredentials: Map, AwsCredentialsProvider> - ): TestCredentialProviderFactory = TestCredentialProviderFactory(id, initialCredentials) + initialProviderIds: List + ): TestCredentialProviderFactory = TestCredentialProviderFactory(id, initialProviderIds) private class TestCredentialProviderFactory( override val id: String, - private val initialCredentials: Map, AwsCredentialsProvider> + private val initialProviderIds: List ) : CredentialProviderFactory { - private val credentialsMapping = mutableMapOf>() + private val credentialsMapping = mutableMapOf() private lateinit var callback: CredentialsChangeListener override fun setUp(credentialLoadCallback: CredentialsChangeListener) { callback = credentialLoadCallback - val credentialsAdded = initialCredentials - .onEach { - val providerIdCredentials = credentialsMapping.computeIfAbsent(it.key.first) { mutableMapOf() } - providerIdCredentials[it.key.second] = it.value - } - .map { createCredentialIdentifier(it.key.first) } + initialProviderIds.forEach { + credentialsMapping[it] = TestCredentialProviderIdentifier(it, id, MockRegionProvider.getInstance().defaultRegion().id) + } callback( CredentialsChangeEvent( - credentialsAdded, + credentialsMapping.values.toList(), emptyList(), emptyList() ) @@ -234,34 +235,49 @@ class CredentialManagerTest { providerId: CredentialIdentifier, region: AwsRegion, sdkHttpClientSupplier: () -> SdkHttpClient - ): AwsCredentialsProvider = credentialsMapping.getValue(providerId.id).getValue(region) + ): AwsCredentialsProvider { + val echoField = (providerId as TestCredentialProviderIdentifier).credentialsEchoField + val echoSuffix = echoField?.let { "-$it" } ?: "" + + return StaticCredentialsProvider.create( + AwsBasicCredentials.create( + "${providerId.id}-${region.partitionId}-Access$echoSuffix", + "${providerId.id}-${region.partitionId}-Secret$echoSuffix" + ) + ) + } - private fun createCredentialIdentifier(providerId: String): TestCredentialProviderIdentifier = TestCredentialProviderIdentifier(providerId, id) + fun updateCredentials(providerId: String, region: AwsRegion, echoField: String? = null) { + val identifier = TestCredentialProviderIdentifier(providerId, id, region.id, echoField) + + credentialsMapping[providerId] = identifier - fun updateCredentials(providerId: String, credentials: Map) { - credentialsMapping[providerId] = credentials.toMutableMap() callback( CredentialsChangeEvent( emptyList(), - listOf(createCredentialIdentifier(providerId)), + listOf(identifier), emptyList() ) ) } fun removeCredentials(providerId: String) { - credentialsMapping.remove(providerId) callback( CredentialsChangeEvent( emptyList(), emptyList(), - listOf(createCredentialIdentifier(providerId)) + listOf(credentialsMapping.remove(providerId)!!) ) ) } } - private class TestCredentialProviderIdentifier(override val id: String, override val factoryId: String) : CredentialIdentifierBase() { + private class TestCredentialProviderIdentifier( + override val id: String, + override val factoryId: String, + override val defaultRegionId: String, + val credentialsEchoField: String? = null + ) : CredentialIdentifierBase() { override val displayName: String = "$factoryId:$id" } } From 6a655bd4c4cdd2d704dd6eabe1c0ae5a1e157315 Mon Sep 17 00:00:00 2001 From: Hunter Werlla Date: Tue, 25 Aug 2020 09:43:14 -0700 Subject: [PATCH 0039/1546] Save Lambda update settings between updates (#2011) --- ...-eda3bf80-7f8a-4e30-9fff-4826e84b64ff.json | 4 ++ jetbrains-core/resources/META-INF/plugin.xml | 1 + .../lambda/upload/EditFunctionDialog.kt | 18 +++++ .../settings/UpdateLambdaSettings.kt | 53 ++++++++++++++ .../lambda/upload/EditFunctionDialogTest.kt | 72 +++++++++++++++---- 5 files changed, 135 insertions(+), 13 deletions(-) create mode 100644 .changes/next-release/feature-eda3bf80-7f8a-4e30-9fff-4826e84b64ff.json create mode 100644 jetbrains-core/src/software/aws/toolkits/jetbrains/settings/UpdateLambdaSettings.kt diff --git a/.changes/next-release/feature-eda3bf80-7f8a-4e30-9fff-4826e84b64ff.json b/.changes/next-release/feature-eda3bf80-7f8a-4e30-9fff-4826e84b64ff.json new file mode 100644 index 0000000000..489b874dec --- /dev/null +++ b/.changes/next-release/feature-eda3bf80-7f8a-4e30-9fff-4826e84b64ff.json @@ -0,0 +1,4 @@ +{ + "type" : "feature", + "description" : "Save update Lambda code settings" +} \ No newline at end of file diff --git a/jetbrains-core/resources/META-INF/plugin.xml b/jetbrains-core/resources/META-INF/plugin.xml index e6826e55bc..fafb208490 100644 --- a/jetbrains-core/resources/META-INF/plugin.xml +++ b/jetbrains-core/resources/META-INF/plugin.xml @@ -155,6 +155,7 @@ + CreateNewLambdaOkAction() @@ -168,6 +172,8 @@ class EditFunctionDialog( } } } + + loadSettings() } private fun configurationChanged(): Boolean = mode != NEW && !(name == view.name.text && @@ -295,6 +301,7 @@ class EditFunctionDialog( // We normally don't validate the deploy settings in case they are editing settings only, but they requested // to deploy so start validating that too super.doAction(e) + saveSettings() if (doValidateAll().isNotEmpty()) return upsertLambdaCode() } @@ -308,12 +315,23 @@ class EditFunctionDialog( override fun doAction(e: ActionEvent?) { super.doAction(e) + saveSettings() if (validation() == null) { performUpdate() } } } + private fun loadSettings() { + view.sourceBucket.selectedItem = updateSettings.bucketName + view.buildInContainer.isSelected = updateSettings.useContainer ?: false + } + + private fun saveSettings() { + updateSettings.bucketName = view.sourceBucket.selectedItem?.toString() + updateSettings.useContainer = view.buildInContainer.isSelected + } + @TestOnly fun getViewForTestAssertions() = view } diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/settings/UpdateLambdaSettings.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/settings/UpdateLambdaSettings.kt new file mode 100644 index 0000000000..39a8b7b99c --- /dev/null +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/settings/UpdateLambdaSettings.kt @@ -0,0 +1,53 @@ +// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package software.aws.toolkits.jetbrains.settings + +import com.intellij.openapi.components.PersistentStateComponent +import com.intellij.openapi.components.ServiceManager +import com.intellij.openapi.components.State +import com.intellij.openapi.components.Storage + +@State(name = "updateLambdaState", storages = [Storage("aws.xml")]) +private class UpdateLambdaState : PersistentStateComponent { + private var settings = UpdateLambda() + + override fun getState(): UpdateLambda = settings + override fun loadState(state: UpdateLambda) { + this.settings = state + } + + companion object { + @JvmStatic + internal fun getInstance(): UpdateLambdaState = ServiceManager.getService(UpdateLambdaState::class.java) + } +} + +class UpdateLambdaSettings private constructor(private val arn: String) { + private val stateService = UpdateLambdaState.getInstance() + + var useContainer: Boolean? + get() = stateService.state.configs[arn]?.useContainer + set(value) { + stateService.state.configs.computeIfAbsent(arn) { UpdateConfig() }.useContainer = value ?: false + } + + var bucketName: String? + get() = stateService.state.configs[arn]?.bucketName + set(value) { + stateService.state.configs.computeIfAbsent(arn) { UpdateConfig() }.bucketName = value + } + + companion object { + fun getInstance(arn: String) = UpdateLambdaSettings(arn) + } +} + +data class UpdateLambda( + var configs: MutableMap = mutableMapOf() +) + +data class UpdateConfig( + var bucketName: String? = null, + var useContainer: Boolean = false +) diff --git a/jetbrains-core/tst/software/aws/toolkits/jetbrains/services/lambda/upload/EditFunctionDialogTest.kt b/jetbrains-core/tst/software/aws/toolkits/jetbrains/services/lambda/upload/EditFunctionDialogTest.kt index dfd3ec6ebf..95853e385f 100644 --- a/jetbrains-core/tst/software/aws/toolkits/jetbrains/services/lambda/upload/EditFunctionDialogTest.kt +++ b/jetbrains-core/tst/software/aws/toolkits/jetbrains/services/lambda/upload/EditFunctionDialogTest.kt @@ -10,13 +10,12 @@ import com.intellij.testFramework.IdeaTestUtil import com.intellij.testFramework.runInEdtAndGet import com.intellij.testFramework.runInEdtAndWait import com.nhaarman.mockitokotlin2.any -import com.nhaarman.mockitokotlin2.mock import com.nhaarman.mockitokotlin2.whenever +import kotlinx.coroutines.runBlocking import org.assertj.core.api.Assertions.assertThat import org.junit.Before import org.junit.Rule import org.junit.Test -import software.amazon.awssdk.http.SdkHttpResponse import software.amazon.awssdk.services.iam.IamClient import software.amazon.awssdk.services.iam.model.ListRolesRequest import software.amazon.awssdk.services.iam.model.ListRolesResponse @@ -25,15 +24,18 @@ import software.amazon.awssdk.services.iam.paginators.ListRolesIterable import software.amazon.awssdk.services.lambda.model.Runtime import software.amazon.awssdk.services.s3.S3Client import software.amazon.awssdk.services.s3.model.Bucket -import software.amazon.awssdk.services.s3.model.HeadBucketRequest -import software.amazon.awssdk.services.s3.model.HeadBucketResponse -import software.amazon.awssdk.services.s3.model.ListBucketsResponse import software.aws.toolkits.core.region.AwsRegion +import software.aws.toolkits.core.utils.RuleUtils import software.aws.toolkits.jetbrains.core.MockClientManagerRule +import software.aws.toolkits.jetbrains.core.MockResourceCacheRule import software.aws.toolkits.jetbrains.core.credentials.AwsConnectionManager import software.aws.toolkits.jetbrains.core.credentials.MockAwsConnectionManager import software.aws.toolkits.jetbrains.services.lambda.LambdaHandlerResolver +import software.aws.toolkits.jetbrains.services.s3.resources.S3Resources +import software.aws.toolkits.jetbrains.settings.UpdateLambdaSettings +import software.aws.toolkits.jetbrains.ui.ResourceSelector import software.aws.toolkits.jetbrains.utils.rules.JavaCodeInsightTestFixtureRule +import software.aws.toolkits.jetbrains.utils.waitForFalse class EditFunctionDialogTest { @@ -45,6 +47,10 @@ class EditFunctionDialogTest { @Rule val mockClientManager = MockClientManagerRule(projectRule) + @JvmField + @Rule + val mockResourceCache = MockResourceCacheRule(projectRule) + private val mockSettingsManager by lazy { AwsConnectionManager.getInstance(projectRule.project) as MockAwsConnectionManager } private lateinit var s3Client: S3Client @@ -80,6 +86,42 @@ class EditFunctionDialogTest { assertThat(dialog2.getViewForTestAssertions().runtime.model.size).isNotEqualTo(Runtime.knownValues().size) } + @Test + fun `Loads saved settings if function name matches`() { + mockBuckets() + + val arn = RuleUtils.randomName() + val settings = UpdateLambdaSettings.getInstance(arn) + + settings.bucketName = "hello2" + settings.useContainer = true + + val dialog = runInEdtAndGet { + EditFunctionDialog(project = projectRule.project, mode = EditFunctionMode.UPDATE_CODE, arn = arn) + } + dialog.getViewForTestAssertions().sourceBucket.waitToLoad() + assertThat(dialog.getViewForTestAssertions().buildInContainer.isSelected).isEqualTo(true) + assertThat(dialog.getViewForTestAssertions().sourceBucket.selectedItem?.toString()).isEqualTo("hello2") + } + + @Test + fun `Does not load saved settings if function name does not match`() { + mockBuckets() + + val arn = RuleUtils.randomName() + val settings = UpdateLambdaSettings.getInstance(arn) + + settings.bucketName = "hello2" + settings.useContainer = true + + val dialog = runInEdtAndGet { + EditFunctionDialog(project = projectRule.project, mode = EditFunctionMode.UPDATE_CODE, arn = "not$arn") + } + dialog.getViewForTestAssertions().sourceBucket.waitToLoad() + assertThat(dialog.getViewForTestAssertions().buildInContainer.isSelected).isEqualTo(false) + assertThat(dialog.getViewForTestAssertions().sourceBucket.selectedItem).isNull() + } + @Test fun `On update configuration, dialog shows all runtimes`() { val dialog = runInEdtAndGet { @@ -93,7 +135,6 @@ class EditFunctionDialogTest { @Test fun newShowsConfigurationDeploymentAndBuildSettings() { - mockBuckets() mockRoles() val dialog = runInEdtAndGet { @@ -107,7 +148,6 @@ class EditFunctionDialogTest { @Test fun updateConfigurationShowsOnlyConfigurationSettings() { - mockBuckets() mockRoles() val dialog = runInEdtAndGet { @@ -121,7 +161,6 @@ class EditFunctionDialogTest { @Test fun updateCodeShowsOnlyDeploymentSettingsHandlerAndBuild() { - mockBuckets() mockRoles() val dialog = runInEdtAndGet { @@ -145,11 +184,12 @@ class EditFunctionDialogTest { } private fun mockBuckets() { - whenever(s3Client.listBuckets()).thenReturn(ListBucketsResponse.builder().buckets(Bucket.builder().name("hello").build()).build()) - val mockSdkResponse = mock() - whenever(mockSdkResponse.headers()).thenReturn(mapOf("x-amz-bucket-region" to listOf("us-west-1"))) - whenever(s3Client.headBucket(HeadBucketRequest.builder().bucket("hello").build())).thenReturn( - HeadBucketResponse.builder().sdkHttpResponse(mockSdkResponse).build() as HeadBucketResponse + mockResourceCache.get().addEntry( + S3Resources.LIST_REGIONALIZED_BUCKETS, + listOf( + S3Resources.RegionalizedBucket(Bucket.builder().name("hello").build(), mockSettingsManager.activeRegion), + S3Resources.RegionalizedBucket(Bucket.builder().name("hello2").build(), mockSettingsManager.activeRegion) + ) ) } @@ -168,4 +208,10 @@ class EditFunctionDialogTest { ).isTruncated(false).build() ) } + + private fun ResourceSelector<*>.waitToLoad() { + runBlocking { + waitForFalse { isLoading } + } + } } From 4ce842280fc669b5244ef57a92b6f64f7d837395 Mon Sep 17 00:00:00 2001 From: Christopher Christou <39839589+awschristou@users.noreply.github.com> Date: Tue, 25 Aug 2020 10:04:33 -0700 Subject: [PATCH 0040/1546] Updated Credentials design diagram to reflect current implementation (#2012) --- .../images/classDiagram.svg | 104 +++++------------- 1 file changed, 28 insertions(+), 76 deletions(-) diff --git a/designs/credentialManagement/images/classDiagram.svg b/designs/credentialManagement/images/classDiagram.svg index de97bcd5a8..58b3d9df14 100644 --- a/designs/credentialManagement/images/classDiagram.svg +++ b/designs/credentialManagement/images/classDiagram.svg @@ -1,20 +1,19 @@ -AwsRegionString partitionIdString nameString idToolkitCredentialsIdentifierString factoryIdString displayNameString idCredentialsChangeEventadded: List<ToolkitCredentialsIdentifier>,modified: List<ToolkitCredentialsIdentifier>,removed: List<ToolkitCredentialsIdentifier>CredentialsChangeListeneronChange(CredentialsChangeEventCredentialManagerList<ToolkitCredentialsIdentifier> getCredentialIdentifiers()ToolkitCredentialsProvider getCredentialProvider(String id)CredentialProviderFactoryString idvoid setUp(callback CredentialsChangeListener)ToolkitCredentialsProvider createAwsCredentialProvider(ToolkitCredentialsIdentifier, AwsRegion, Suppier<SdkHttpClient>)ToolkitCredentialsProviderString displayNameString idAwsCredentialsProviderAwsCredentials resolveCredentials()InheritanceCreatesmany1Ownsmany1AwsRegionString partitionIdString nameString idCredentialIdentifierString factoryIdString displayNameString idCredentialsChangeEventadded: List<CredentialIdentifier>,modified: List<CredentialIdentifier>,removed: List<CredentialIdentifier>CredentialsChangeListeneronChange(CredentialsChangeEventCredentialManagerList<CredentialIdentifier> getCredentialIdentifiers()ToolkitCredentialsProvider getCredentialProvider(String id)ToolkitCredentialsProvider getAwsCredentialProvider(CredentialIdentifier, AwsRegion)CredentialProviderFactoryString idvoid setUp(callback CredentialsChangeListener)ToolkitCredentialsProvider createAwsCredentialProvider(CredentialIdentifier, AwsRegion, Suppier<SdkHttpClient>)ToolkitCredentialsProviderString displayNameString idAwsCredentialsProviderAwsCredentials resolveCredentials()InheritanceCreatesmany1Ownsmany1 From b1b9219a1aa810f100f1f830ead9e460865126e7 Mon Sep 17 00:00:00 2001 From: Austin Brooks Date: Tue, 25 Aug 2020 11:05:03 -0700 Subject: [PATCH 0041/1546] Upgrade test logger (#2015) --- build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index 19fb8d042e..89900757a4 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -31,7 +31,7 @@ buildscript { dependencies { classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion") classpath("gradle.plugin.org.jetbrains.intellij.plugins:gradle-intellij-plugin:$ideaPluginVersion") - classpath("com.adarshr:gradle-test-logger-plugin:1.7.0") + classpath("com.adarshr:gradle-test-logger-plugin:2.1.0") } } From d13071777542f517b713f56de416aed3f924fe68 Mon Sep 17 00:00:00 2001 From: Hunter Werlla Date: Tue, 25 Aug 2020 13:21:05 -0700 Subject: [PATCH 0042/1546] =?UTF-8?q?Fix=20the=20CloudWatch=20Logs=20table?= =?UTF-8?q?=20breaking=20when=20the=20service=20returns=20an=20ex=E2=80=A6?= =?UTF-8?q?=20(#2017)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...-f0fc84aa-d075-4441-88d3-e17609b267bf.json | 4 +++ .../services/cloudwatch/logs/LogActor.kt | 28 ++++++++++++++++--- .../cloudwatch/logs/editor/LogGroupTable.kt | 2 ++ .../resources/localized_messages.properties | 2 ++ 4 files changed, 32 insertions(+), 4 deletions(-) create mode 100644 .changes/next-release/bugfix-f0fc84aa-d075-4441-88d3-e17609b267bf.json diff --git a/.changes/next-release/bugfix-f0fc84aa-d075-4441-88d3-e17609b267bf.json b/.changes/next-release/bugfix-f0fc84aa-d075-4441-88d3-e17609b267bf.json new file mode 100644 index 0000000000..4677828bc5 --- /dev/null +++ b/.changes/next-release/bugfix-f0fc84aa-d075-4441-88d3-e17609b267bf.json @@ -0,0 +1,4 @@ +{ + "type" : "bugfix", + "description" : "Fix the CloudWatch Logs table breaking when the service returns an exception during loading more entries (#1951)" +} diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/LogActor.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/LogActor.kt index f57b0bb043..0e17b8cebf 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/LogActor.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/LogActor.kt @@ -5,7 +5,6 @@ package software.aws.toolkits.jetbrains.services.cloudwatch.logs import com.intellij.openapi.Disposable import com.intellij.openapi.project.Project -import com.intellij.openapi.util.Disposer import com.intellij.ui.TableUtil import com.intellij.ui.table.TableView import kotlinx.coroutines.CoroutineExceptionHandler @@ -22,6 +21,7 @@ import software.amazon.awssdk.services.cloudwatchlogs.model.OrderBy import software.amazon.awssdk.services.cloudwatchlogs.model.ResourceNotFoundException import software.aws.toolkits.core.utils.error import software.aws.toolkits.core.utils.getLogger +import software.aws.toolkits.core.utils.warn import software.aws.toolkits.jetbrains.utils.ApplicationThreadPoolScope import software.aws.toolkits.jetbrains.utils.getCoroutineUiContext import software.aws.toolkits.jetbrains.utils.notifyError @@ -46,7 +46,7 @@ sealed class LogActor( LOG.error(e) { "Exception thrown in the LogStreamActor not handled:" } notifyError(title = message("general.unknown_error"), project = project) table.setPaintBusy(false) - Disposer.dispose(this) + channel.close() } sealed class Message { @@ -68,7 +68,17 @@ sealed class LogActor( when (message) { is Message.LoadForward -> if (!nextForwardToken.isNullOrEmpty()) { withContext(edtContext) { table.setPaintBusy(true) } - val items = loadMore(nextForwardToken, saveForwardToken = true) + val items = try { + loadMore(nextForwardToken, saveForwardToken = true) + } catch (e: Exception) { + LOG.warn(e) { "Exception thrown while trying to load forwards" } + notifyError( + project = project, + title = message("cloudwatch.logs.client_exception"), + content = message("cloudwatch.logs.failed_to_load_more") + ) + listOf() + } withContext(edtContext) { table.listTableModel.addRows(items) table.setPaintBusy(false) @@ -76,7 +86,17 @@ sealed class LogActor( } is Message.LoadBackward -> if (!nextBackwardToken.isNullOrEmpty()) { withContext(edtContext) { table.setPaintBusy(true) } - val items = loadMore(nextBackwardToken, saveBackwardToken = true) + val items = try { + loadMore(nextBackwardToken, saveBackwardToken = true) + } catch (e: Exception) { + LOG.warn(e) { "Exception thrown while trying to load backwards" } + notifyError( + project = project, + title = message("cloudwatch.logs.client_exception"), + content = message("cloudwatch.logs.failed_to_load_more") + ) + listOf() + } if (items.isNotEmpty()) { // Selected rows can be non contiguous so we have to save every row selected val newSelection = table.selectedRows.map { it + items.size } diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/editor/LogGroupTable.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/editor/LogGroupTable.kt index 1cca277c9c..b2565d0865 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/editor/LogGroupTable.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/editor/LogGroupTable.kt @@ -9,6 +9,7 @@ import com.intellij.openapi.actionSystem.ActionPlaces import com.intellij.openapi.actionSystem.Constraints import com.intellij.openapi.actionSystem.DefaultActionGroup import com.intellij.openapi.project.Project +import com.intellij.openapi.util.Disposer import com.intellij.ui.DoubleClickListener import com.intellij.ui.PopupHandler import com.intellij.ui.ScrollPaneFactory @@ -75,6 +76,7 @@ class LogGroupTable( TableType.LIST -> LogGroupActor(project, client, groupTable, logGroup) TableType.FILTER -> LogGroupSearchActor(project, client, groupTable, logGroup) } + Disposer.register(this@LogGroupTable, logGroupActor) channel = logGroupActor.channel diff --git a/resources/resources/software/aws/toolkits/resources/localized_messages.properties b/resources/resources/software/aws/toolkits/resources/localized_messages.properties index 8d8cbbe366..01371e3c2c 100644 --- a/resources/resources/software/aws/toolkits/resources/localized_messages.properties +++ b/resources/resources/software/aws/toolkits/resources/localized_messages.properties @@ -196,7 +196,9 @@ cloudwatch.logs.copy_action=Copy {0, choice, 1#message|2#messages} cloudwatch.logs.delete_log_group=Delete Log Group cloudwatch.logs.download=Download Log Stream cloudwatch.logs.download.description=Select a folder to save the Log Stream to +cloudwatch.logs.exception=CloudWatch Logs Exception cloudwatch.logs.export=Export Log Stream +cloudwatch.logs.failed_to_load_more=Failed to load more cloudwatch.logs.failed_to_load_stream=Failed to load log stream {0} cloudwatch.logs.failed_to_load_streams=Failed to load log streams for log group {0} cloudwatch.logs.filter_loggroup=Filter streams From 918699df9661fb880e07efe50a4b8e330ba36d98 Mon Sep 17 00:00:00 2001 From: Austin Brooks Date: Tue, 25 Aug 2020 15:09:35 -0700 Subject: [PATCH 0043/1546] Fix integration tests using real creds after they were reset (#2018) --- .../services/lambda/deploy/SamDeployTest.kt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/jetbrains-core/it/software/aws/toolkits/jetbrains/services/lambda/deploy/SamDeployTest.kt b/jetbrains-core/it/software/aws/toolkits/jetbrains/services/lambda/deploy/SamDeployTest.kt index 3007bf7a8d..83f2fb3498 100644 --- a/jetbrains-core/it/software/aws/toolkits/jetbrains/services/lambda/deploy/SamDeployTest.kt +++ b/jetbrains-core/it/software/aws/toolkits/jetbrains/services/lambda/deploy/SamDeployTest.kt @@ -171,9 +171,10 @@ class SamDeployTest { } } - private fun createChangeSet(templateFile: VirtualFile, stackName: String, parameters: Map = emptyMap()): String? { - val deployDialog = runInEdtAndGet { - runUnderRealCredentials(projectRule.project) { + private fun createChangeSet(templateFile: VirtualFile, stackName: String, parameters: Map = emptyMap()): String? = + runUnderRealCredentials(projectRule.project) { + val deployDialog = runInEdtAndGet { + SamDeployDialog( projectRule.project, stackName, @@ -187,10 +188,9 @@ class SamDeployTest { Disposer.register(projectRule.fixture.testRootDisposable, it.disposable) } } - } - return deployDialog.deployFuture.get(5, TimeUnit.MINUTES) - } + deployDialog.deployFuture.get(5, TimeUnit.MINUTES) + } private fun runAssertsAndClean(stackName: String, asserts: () -> Unit) { try { From 804aad6923e009609fc378afee2130b7b4891755 Mon Sep 17 00:00:00 2001 From: Austin Brooks Date: Wed, 26 Aug 2020 13:09:41 -0700 Subject: [PATCH 0044/1546] Move cloud debug executable into clouddebug package (#2021) --- jetbrains-core/resources/META-INF/plugin.xml | 2 +- .../clouddebug}/CloudDebugExecutable.kt | 8 ++++++-- .../jetbrains/services/clouddebug/CloudDebugResolver.kt | 1 - .../clouddebug}/CloudDebugVersionCache.kt | 5 +++-- .../jetbrains/services/clouddebug/actions/PseCliAction.kt | 2 +- .../services/clouddebug/actions/StartRemoteShellAction.kt | 2 +- .../clouddebug/execution/steps/CloudDebugCliValidate.kt | 2 +- .../clouddebug/resources/CloudDebuggingResources.kt | 2 +- .../jetbrains/settings/AwsSettingsConfigurable.java | 2 +- 9 files changed, 15 insertions(+), 11 deletions(-) rename jetbrains-core/src/software/aws/toolkits/jetbrains/{core/executables => services/clouddebug}/CloudDebugExecutable.kt (80%) rename jetbrains-core/src/software/aws/toolkits/jetbrains/{core/executables => services/clouddebug}/CloudDebugVersionCache.kt (87%) diff --git a/jetbrains-core/resources/META-INF/plugin.xml b/jetbrains-core/resources/META-INF/plugin.xml index fafb208490..f7b496eb6d 100644 --- a/jetbrains-core/resources/META-INF/plugin.xml +++ b/jetbrains-core/resources/META-INF/plugin.xml @@ -234,7 +234,7 @@ - + diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/core/executables/CloudDebugExecutable.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/clouddebug/CloudDebugExecutable.kt similarity index 80% rename from jetbrains-core/src/software/aws/toolkits/jetbrains/core/executables/CloudDebugExecutable.kt rename to jetbrains-core/src/software/aws/toolkits/jetbrains/services/clouddebug/CloudDebugExecutable.kt index 032dca7d64..79fab802a0 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/core/executables/CloudDebugExecutable.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/clouddebug/CloudDebugExecutable.kt @@ -1,10 +1,14 @@ -// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -package software.aws.toolkits.jetbrains.core.executables +package software.aws.toolkits.jetbrains.services.clouddebug import com.intellij.openapi.util.SystemInfo import com.intellij.util.text.SemVer +import software.aws.toolkits.jetbrains.core.executables.AutoResolvable +import software.aws.toolkits.jetbrains.core.executables.ExecutableCommon +import software.aws.toolkits.jetbrains.core.executables.ExecutableType +import software.aws.toolkits.jetbrains.core.executables.Validatable import software.aws.toolkits.jetbrains.settings.ExecutableDetector import java.nio.file.Path diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/clouddebug/CloudDebugResolver.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/clouddebug/CloudDebugResolver.kt index 3fe2496038..302ce08564 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/clouddebug/CloudDebugResolver.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/clouddebug/CloudDebugResolver.kt @@ -10,7 +10,6 @@ import com.intellij.openapi.project.Project import com.intellij.openapi.util.SystemInfo import com.intellij.util.text.SemVer import org.apache.commons.codec.digest.DigestUtils -import software.aws.toolkits.jetbrains.core.executables.CloudDebugExecutable import software.aws.toolkits.jetbrains.core.executables.ExecutableInstance import software.aws.toolkits.jetbrains.core.executables.ExecutableManager import software.aws.toolkits.jetbrains.core.executables.ExecutableType diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/core/executables/CloudDebugVersionCache.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/clouddebug/CloudDebugVersionCache.kt similarity index 87% rename from jetbrains-core/src/software/aws/toolkits/jetbrains/core/executables/CloudDebugVersionCache.kt rename to jetbrains-core/src/software/aws/toolkits/jetbrains/services/clouddebug/CloudDebugVersionCache.kt index 974c6fe28e..d82e01dedf 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/core/executables/CloudDebugVersionCache.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/clouddebug/CloudDebugVersionCache.kt @@ -1,11 +1,12 @@ -// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -package software.aws.toolkits.jetbrains.core.executables +package software.aws.toolkits.jetbrains.services.clouddebug import com.intellij.execution.process.CapturingProcessHandler import com.intellij.util.text.SemVer import com.intellij.util.text.nullize +import software.aws.toolkits.jetbrains.core.executables.ExecutableCommon import software.aws.toolkits.jetbrains.utils.FileInfoCache import software.aws.toolkits.resources.message diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/clouddebug/actions/PseCliAction.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/clouddebug/actions/PseCliAction.kt index c8d296dedd..bea9249642 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/clouddebug/actions/PseCliAction.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/clouddebug/actions/PseCliAction.kt @@ -32,7 +32,7 @@ import software.aws.toolkits.core.utils.getLogger import software.aws.toolkits.jetbrains.core.AwsResourceCache import software.aws.toolkits.jetbrains.core.credentials.AwsConnectionManager import software.aws.toolkits.jetbrains.core.credentials.toEnvironmentVariables -import software.aws.toolkits.jetbrains.core.executables.CloudDebugExecutable +import software.aws.toolkits.jetbrains.services.clouddebug.CloudDebugExecutable import software.aws.toolkits.jetbrains.core.executables.ExecutableInstance import software.aws.toolkits.jetbrains.core.executables.ExecutableManager import software.aws.toolkits.jetbrains.core.executables.getExecutable diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/clouddebug/actions/StartRemoteShellAction.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/clouddebug/actions/StartRemoteShellAction.kt index 8b8e95aad2..6556c83526 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/clouddebug/actions/StartRemoteShellAction.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/clouddebug/actions/StartRemoteShellAction.kt @@ -19,7 +19,7 @@ import software.aws.toolkits.jetbrains.core.credentials.AwsConnectionManager import software.aws.toolkits.jetbrains.core.credentials.activeCredentialProvider import software.aws.toolkits.jetbrains.core.credentials.activeRegion import software.aws.toolkits.jetbrains.core.credentials.toEnvironmentVariables -import software.aws.toolkits.jetbrains.core.executables.CloudDebugExecutable +import software.aws.toolkits.jetbrains.services.clouddebug.CloudDebugExecutable import software.aws.toolkits.jetbrains.core.executables.ExecutableInstance import software.aws.toolkits.jetbrains.core.executables.ExecutableManager import software.aws.toolkits.jetbrains.core.executables.getExecutable diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/clouddebug/execution/steps/CloudDebugCliValidate.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/clouddebug/execution/steps/CloudDebugCliValidate.kt index 890289727d..030c66a5d0 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/clouddebug/execution/steps/CloudDebugCliValidate.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/clouddebug/execution/steps/CloudDebugCliValidate.kt @@ -7,7 +7,7 @@ import software.aws.toolkits.core.utils.AttributeBagKey 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.executables.CloudDebugExecutable +import software.aws.toolkits.jetbrains.services.clouddebug.CloudDebugExecutable import software.aws.toolkits.jetbrains.services.clouddebug.CloudDebugResolver import software.aws.toolkits.jetbrains.services.clouddebug.execution.Context import software.aws.toolkits.jetbrains.services.clouddebug.execution.MessageEmitter diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/clouddebug/resources/CloudDebuggingResources.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/clouddebug/resources/CloudDebuggingResources.kt index 3b5f710c7d..49386536e2 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/clouddebug/resources/CloudDebuggingResources.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/clouddebug/resources/CloudDebuggingResources.kt @@ -22,7 +22,7 @@ import software.aws.toolkits.core.utils.warn import software.aws.toolkits.jetbrains.core.ExecutableBackedCacheResource import software.aws.toolkits.jetbrains.core.Resource import software.aws.toolkits.jetbrains.core.credentials.toEnvironmentVariables -import software.aws.toolkits.jetbrains.core.executables.CloudDebugExecutable +import software.aws.toolkits.jetbrains.services.clouddebug.CloudDebugExecutable import software.aws.toolkits.jetbrains.services.clouddebug.execution.MessageEmitter import software.aws.toolkits.jetbrains.services.clouddebug.execution.steps.CloudDebugCliValidate import software.aws.toolkits.jetbrains.services.ecs.EcsUtils diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/settings/AwsSettingsConfigurable.java b/jetbrains-core/src/software/aws/toolkits/jetbrains/settings/AwsSettingsConfigurable.java index caad92a73d..d0bcc9343d 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/settings/AwsSettingsConfigurable.java +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/settings/AwsSettingsConfigurable.java @@ -30,7 +30,7 @@ import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import software.aws.toolkits.jetbrains.core.executables.CloudDebugExecutable; +import software.aws.toolkits.jetbrains.services.clouddebug.CloudDebugExecutable; import software.aws.toolkits.jetbrains.core.executables.ExecutableInstance; import software.aws.toolkits.jetbrains.core.executables.ExecutableManager; import software.aws.toolkits.jetbrains.core.executables.ExecutableType; From bac0350cd756173f505fd3bb8f53720822786603 Mon Sep 17 00:00:00 2001 From: Ran Yoo <30319724+ranyoo2367@users.noreply.github.com> Date: Wed, 26 Aug 2020 16:46:52 -0400 Subject: [PATCH 0045/1546] SQS: Add Configure to Lambda action (#2016) * Start configure Lambda trigger * Setup for follow up * Added test for ConfigureLambdaDialog * Fix style * Iam dialog fix * Added error handling for configuration after iam * PR revisions * PR revisions pt 2 * Waiter fix --- jetbrains-core/resources/META-INF/plugin.xml | 2 + .../services/lambda/upload/LambdaPolicies.kt | 18 +++ .../services/sqs/ConfigureLambdaDialog.kt | 147 ++++++++++++++++++ .../services/sqs/ConfigureLambdaPanel.form | 44 ++++++ .../services/sqs/ConfigureLambdaPanel.kt | 36 +++++ .../services/sqs/ConfirmIamPolicyDialog.kt | 89 +++++++++++ .../services/sqs/ConfirmIamPolicyPanel.form | 27 ++++ .../services/sqs/ConfirmIamPolicyPanel.kt | 26 ++++ .../sqs/actions/ConfigureLambdaAction.kt | 17 ++ .../services/sqs/ConfigureLambdaDialogTest.kt | 135 ++++++++++++++++ .../resources/localized_messages.properties | 17 +- 11 files changed, 557 insertions(+), 1 deletion(-) create mode 100644 jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/ConfigureLambdaDialog.kt create mode 100644 jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/ConfigureLambdaPanel.form create mode 100644 jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/ConfigureLambdaPanel.kt create mode 100644 jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/ConfirmIamPolicyDialog.kt create mode 100644 jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/ConfirmIamPolicyPanel.form create mode 100644 jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/ConfirmIamPolicyPanel.kt create mode 100644 jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/actions/ConfigureLambdaAction.kt create mode 100644 jetbrains-core/tst/software/aws/toolkits/jetbrains/services/sqs/ConfigureLambdaDialogTest.kt diff --git a/jetbrains-core/resources/META-INF/plugin.xml b/jetbrains-core/resources/META-INF/plugin.xml index aefa717d5f..11b72d6238 100644 --- a/jetbrains-core/resources/META-INF/plugin.xml +++ b/jetbrains-core/resources/META-INF/plugin.xml @@ -349,6 +349,8 @@ + diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/upload/LambdaPolicies.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/upload/LambdaPolicies.kt index f3f1256241..a208cfe673 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/upload/LambdaPolicies.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/upload/LambdaPolicies.kt @@ -40,3 +40,21 @@ val DEFAULT_POLICY = """ ] } """.trim() + +@Language("JSON") +fun createSqsPollerPolicy(arn: String): String = """ +{ + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Action": [ + "sqs:DeleteMessage", + "sqs:GetQueueAttributes", + "sqs:ReceiveMessage" + ], + "Resource": "$arn" + } + ] +} +""".trim() diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/ConfigureLambdaDialog.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/ConfigureLambdaDialog.kt new file mode 100644 index 0000000000..59c2f9f1c7 --- /dev/null +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/ConfigureLambdaDialog.kt @@ -0,0 +1,147 @@ +// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package software.aws.toolkits.jetbrains.services.sqs + +import com.intellij.openapi.application.ModalityState +import com.intellij.openapi.application.runInEdt +import com.intellij.openapi.project.Project +import com.intellij.openapi.ui.DialogWrapper +import com.intellij.openapi.ui.ValidationInfo +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.launch +import software.amazon.awssdk.services.iam.IamClient +import software.amazon.awssdk.services.lambda.LambdaClient +import software.amazon.awssdk.services.lambda.model.InvalidParameterValueException +import software.amazon.awssdk.services.lambda.model.LambdaException +import software.amazon.awssdk.services.lambda.model.ResourceConflictException +import software.amazon.awssdk.services.lambda.model.ResourceNotFoundException +import software.amazon.awssdk.services.lambda.model.ServiceException +import software.aws.toolkits.core.utils.WaiterTimeoutException +import software.aws.toolkits.core.utils.Waiters.waitUntil +import software.aws.toolkits.core.utils.getLogger +import software.aws.toolkits.core.utils.warn +import software.aws.toolkits.jetbrains.core.awsClient +import software.aws.toolkits.jetbrains.utils.ApplicationThreadPoolScope +import software.aws.toolkits.jetbrains.utils.notifyInfo +import software.aws.toolkits.resources.message +import java.time.Duration +import javax.swing.JComponent + +class ConfigureLambdaDialog( + private val project: Project, + private val queue: Queue +) : DialogWrapper(project), CoroutineScope by ApplicationThreadPoolScope("ConfigureLambda") { + private val lambdaClient: LambdaClient = project.awsClient() + private val iamClient: IamClient = project.awsClient() + val view = ConfigureLambdaPanel(project) + + init { + title = message("sqs.configure.lambda") + setOKButtonText(message("general.configure_button")) + setOKButtonTooltip(message("sqs.configure.lambda.configure.tooltip")) + + init() + } + + override fun createCenterPanel(): JComponent? = view.component + + override fun getPreferredFocusedComponent(): JComponent? = view.lambdaFunction + + override fun doValidate(): ValidationInfo? { + if (functionSelected().isEmpty()) { + return ValidationInfo(message("sqs.configure.lambda.validation.function"), view.lambdaFunction) + } + return null + } + + override fun doOKAction() { + if (!isOKActionEnabled) { + return + } + + isOKActionEnabled = false + setOKButtonText(message("sqs.configure.lambda.in_progress")) + + launch { + try { + configureLambda(functionSelected()) + runInEdt(ModalityState.any()) { + close(OK_EXIT_CODE) + } + notifyInfo(message("sqs.service_name"), message("sqs.configure.lambda.success", functionSelected()), project) + } catch (e: InvalidParameterValueException) { + // Exception thrown for invalid permission + runInEdt(ModalityState.any()) { + if (ConfirmIamPolicyDialog(project, iamClient, lambdaClient, functionSelected(), queue, view.component).showAndGet()) { + retryConfiguration(functionSelected()) + } else { + setOKButtonText(message("general.configure_button")) + isOKActionEnabled = true + } + } + } catch (e: Exception) { + LOG.warn(e) { message("sqs.configure.lambda.error", functionSelected()) } + setErrorText(e.message) + setOKButtonText(message("general.configure_button")) + isOKActionEnabled = true + } + } + } + + private fun functionSelected(): String = view.lambdaFunction.selected()?.functionName() ?: "" + + internal fun configureLambda(functionName: String) { + lambdaClient.createEventSourceMapping { + it.functionName(functionName) + it.eventSourceArn(queue.arn) + } + } + + // It takes a few seconds for the role policy to update, so this function will attempt configuration for a duration of time until it succeeds. + internal suspend fun waitUntilConfigured(functionName: String): String? { + var identifier: String? = null + try { + waitUntil( + succeedOn = { + it.eventSourceArn().isNotEmpty() + }, + exceptionsToIgnore = setOf(InvalidParameterValueException::class), + exceptionsToStopOn = setOf(LambdaException::class, ResourceConflictException::class, ResourceNotFoundException::class, ServiceException::class), + maxDuration = Duration.ofSeconds(CONFIGURATION_WAIT_TIME), + call = { + lambdaClient.createEventSourceMapping { + it.functionName(functionName) + it.eventSourceArn(queue.arn) + }.apply { + identifier = this.uuid() + } + } + ) + } catch (e: WaiterTimeoutException) { + identifier = null + } + return identifier + } + + private fun retryConfiguration(functionName: String) { + launch { + val identifier = waitUntilConfigured(functionName) + if (!identifier.isNullOrEmpty()) { + runInEdt(ModalityState.any()) { + close(OK_EXIT_CODE) + } + notifyInfo(message("sqs.service_name"), message("sqs.configure.lambda.success", functionName), project) + } else { + setErrorText(message("sqs.configure.lambda.error", functionName)) + setOKButtonText(message("general.configure_button")) + isOKActionEnabled = true + } + } + } + + private companion object { + val LOG = getLogger() + const val CONFIGURATION_WAIT_TIME: Long = 30 + } +} diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/ConfigureLambdaPanel.form b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/ConfigureLambdaPanel.form new file mode 100644 index 0000000000..3c3b09920b --- /dev/null +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/ConfigureLambdaPanel.form @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/ConfigureLambdaPanel.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/ConfigureLambdaPanel.kt new file mode 100644 index 0000000000..799f2da86b --- /dev/null +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/ConfigureLambdaPanel.kt @@ -0,0 +1,36 @@ +// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +package software.aws.toolkits.jetbrains.services.sqs + +import com.intellij.icons.AllIcons +import com.intellij.ide.HelpTooltip +import com.intellij.openapi.project.Project +import com.intellij.ui.IdeBorderFactory +import software.amazon.awssdk.services.lambda.model.FunctionConfiguration +import software.aws.toolkits.jetbrains.services.lambda.resources.LambdaResources +import software.aws.toolkits.jetbrains.ui.ResourceSelector +import software.aws.toolkits.resources.message +import javax.swing.JLabel +import javax.swing.JPanel + +class ConfigureLambdaPanel(private val project: Project) { + lateinit var component: JPanel + lateinit var lambdaFunction: ResourceSelector + lateinit var functionContextHelp: JLabel + + init { + component.border = IdeBorderFactory.createTitledBorder(message("sqs.configure.lambda.select")) + functionContextHelp.icon = AllIcons.General.ContextHelp + HelpTooltip().apply { + setDescription(message("sqs.configure.lambda.tooltip")) + installOn(functionContextHelp) + } + } + + private fun createUIComponents() { + lambdaFunction = ResourceSelector.builder(project) + .resource(LambdaResources.LIST_FUNCTIONS) + .customRenderer { value, component -> component.append(value.functionName()); component } + .build() + } +} diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/ConfirmIamPolicyDialog.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/ConfirmIamPolicyDialog.kt new file mode 100644 index 0000000000..8674172182 --- /dev/null +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/ConfirmIamPolicyDialog.kt @@ -0,0 +1,89 @@ +// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package software.aws.toolkits.jetbrains.services.sqs + +import com.intellij.json.JsonLanguage +import com.intellij.openapi.application.ModalityState +import com.intellij.openapi.application.runInEdt +import com.intellij.openapi.project.Project +import com.intellij.openapi.ui.DialogWrapper +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.launch +import software.amazon.awssdk.services.iam.IamClient +import software.amazon.awssdk.services.lambda.LambdaClient +import software.aws.toolkits.core.utils.getLogger +import software.aws.toolkits.core.utils.warn +import software.aws.toolkits.jetbrains.services.lambda.upload.createSqsPollerPolicy +import software.aws.toolkits.jetbrains.utils.ApplicationThreadPoolScope +import software.aws.toolkits.jetbrains.utils.ui.formatAndSet +import software.aws.toolkits.resources.message +import java.awt.Component +import javax.swing.JComponent + +class ConfirmIamPolicyDialog( + private val project: Project, + private val iamClient: IamClient, + private val lambdaClient: LambdaClient, + private val functionName: String, + private val queue: Queue, + private val parent: Component? = null +) : DialogWrapper(project, parent, false, IdeModalityType.PROJECT), CoroutineScope by ApplicationThreadPoolScope("ConfirmIamPolicy") { + private val rolePolicy: String by lazy { createSqsPollerPolicy(queue.arn) } + private val policyName: String by lazy { "AWSLambdaSQSPollerExecutionRole-$functionName-${queue.queueName}-${queue.region.id}" } + val view = ConfirmIamPolicyPanel(project) + + init { + title = message("sqs.confirm.iam") + setOKButtonText(message("sqs.confirm.iam.create")) + view.policyDocument.formatAndSet(rolePolicy, JsonLanguage.INSTANCE) + init() + } + + override fun createCenterPanel(): JComponent? = view.component + + override fun doOKAction() { + if (!isOKActionEnabled) { + return + } + + setOKButtonText(message("sqs.confirm.iam.in_progress")) + isOKActionEnabled = false + launch { + try { + val policyArn = createPolicy() + attachPolicy(policyArn) + runInEdt(ModalityState.any()) { + close(OK_EXIT_CODE) + } + } catch (e: Exception) { + LOG.warn(e) { message("sqs.confirm.iam.failed") } + setErrorText(e.message) + setOKButtonText(message("sqs.confirm.iam.create")) + isOKActionEnabled = true + } + } + } + + private fun createPolicy(): String { + val policy = iamClient.createPolicy { + it.policyName(policyName) + it.policyDocument(rolePolicy) + }.policy() + return policy.arn() + } + + private fun attachPolicy(policyArn: String) { + // getFunctionConfiguration().role() returns the ARN of the role like this: arn:aws:iam::123456789012:role/service-role/ROLE-NAME. + // We must use substringAfterLast to extract only the role name. + val role = lambdaClient.getFunctionConfiguration { it.functionName(functionName) }.role().substringAfterLast('/') + iamClient.attachRolePolicy { + it.policyArn(policyArn) + it.roleName(role) + } + } + + private companion object { + val LOG = getLogger() + } +} diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/ConfirmIamPolicyPanel.form b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/ConfirmIamPolicyPanel.form new file mode 100644 index 0000000000..3df57aecf6 --- /dev/null +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/ConfirmIamPolicyPanel.form @@ -0,0 +1,27 @@ + +
+ + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/ConfirmIamPolicyPanel.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/ConfirmIamPolicyPanel.kt new file mode 100644 index 0000000000..828f339aac --- /dev/null +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/ConfirmIamPolicyPanel.kt @@ -0,0 +1,26 @@ +// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +package software.aws.toolkits.jetbrains.services.sqs + +import com.intellij.json.JsonLanguage +import com.intellij.openapi.project.Project +import com.intellij.ui.EditorTextField +import com.intellij.ui.EditorTextFieldProvider +import com.intellij.ui.components.JBLabel +import software.aws.toolkits.resources.message +import java.util.Collections +import javax.swing.JPanel + +class ConfirmIamPolicyPanel(private val project: Project) { + lateinit var component: JPanel + lateinit var policyDocument: EditorTextField + lateinit var warningText: JBLabel + + init { + warningText.text = message("sqs.confirm.iam.warning.text") + } + + private fun createUIComponents() { + policyDocument = EditorTextFieldProvider.getInstance().getEditorField(JsonLanguage.INSTANCE, project, Collections.emptyList()) + } +} diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/actions/ConfigureLambdaAction.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/actions/ConfigureLambdaAction.kt new file mode 100644 index 0000000000..8fdd7ca26f --- /dev/null +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/sqs/actions/ConfigureLambdaAction.kt @@ -0,0 +1,17 @@ +// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package software.aws.toolkits.jetbrains.services.sqs.actions + +import com.intellij.openapi.actionSystem.AnActionEvent +import com.intellij.openapi.project.DumbAware +import software.aws.toolkits.jetbrains.core.explorer.actions.SingleResourceNodeAction +import software.aws.toolkits.jetbrains.services.sqs.ConfigureLambdaDialog +import software.aws.toolkits.jetbrains.services.sqs.SqsQueueNode +import software.aws.toolkits.resources.message + +class ConfigureLambdaAction : SingleResourceNodeAction(message("sqs.configure.lambda")), DumbAware { + override fun actionPerformed(selected: SqsQueueNode, e: AnActionEvent) { + ConfigureLambdaDialog(selected.nodeProject, selected.queue).show() + } +} diff --git a/jetbrains-core/tst/software/aws/toolkits/jetbrains/services/sqs/ConfigureLambdaDialogTest.kt b/jetbrains-core/tst/software/aws/toolkits/jetbrains/services/sqs/ConfigureLambdaDialogTest.kt new file mode 100644 index 0000000000..5eca99689e --- /dev/null +++ b/jetbrains-core/tst/software/aws/toolkits/jetbrains/services/sqs/ConfigureLambdaDialogTest.kt @@ -0,0 +1,135 @@ +// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package software.aws.toolkits.jetbrains.services.sqs + +import com.intellij.testFramework.ProjectRule +import com.intellij.testFramework.runInEdtAndWait +import com.nhaarman.mockitokotlin2.argumentCaptor +import com.nhaarman.mockitokotlin2.doReturn +import com.nhaarman.mockitokotlin2.doThrow +import com.nhaarman.mockitokotlin2.stub +import kotlinx.coroutines.runBlocking +import org.assertj.core.api.Assertions.assertThat +import org.assertj.core.api.Assertions.assertThatThrownBy +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import software.amazon.awssdk.services.iam.IamClient +import software.amazon.awssdk.services.lambda.LambdaClient +import software.amazon.awssdk.services.lambda.model.CreateEventSourceMappingRequest +import software.amazon.awssdk.services.lambda.model.CreateEventSourceMappingResponse +import software.amazon.awssdk.services.lambda.model.InvalidParameterValueException +import software.amazon.awssdk.services.lambda.model.ResourceConflictException +import software.amazon.awssdk.services.sqs.SqsClient +import software.aws.toolkits.core.region.AwsRegion +import software.aws.toolkits.jetbrains.core.MockClientManagerRule +import software.aws.toolkits.jetbrains.core.region.MockRegionProvider + +class ConfigureLambdaDialogTest { + lateinit var sqsClient: SqsClient + lateinit var lambdaClient: LambdaClient + lateinit var iamClient: IamClient + lateinit var region: AwsRegion + lateinit var queue: Queue + + @Rule + @JvmField + val projectRule = ProjectRule() + + @JvmField + @Rule + val mockClientManagerRule = MockClientManagerRule(projectRule) + + @Before + fun setup() { + sqsClient = mockClientManagerRule.create() + lambdaClient = mockClientManagerRule.create() + iamClient = mockClientManagerRule.create() + region = MockRegionProvider.getInstance().defaultRegion() + queue = Queue("https://sqs.us-east-1.amazonaws.com/123456789012/test", region) + } + + @Test + fun `No function selected`() { + runInEdtAndWait { + val dialog = ConfigureLambdaDialog(projectRule.project, queue) + val validationInfo = dialog.validate() + assertThat(validationInfo).isNotNull() + } + } + + @Test + fun `Function configuration succeeds`() { + val configureCaptor = argumentCaptor() + lambdaClient.stub { + on { createEventSourceMapping(configureCaptor.capture()) } doReturn CreateEventSourceMappingResponse.builder().build() + } + + runInEdtAndWait { + ConfigureLambdaDialog(projectRule.project, queue).apply { + configureLambda(TEST_FUNCTION_NAME) + } + } + assertThat(configureCaptor.firstValue.functionName()).isEqualTo(TEST_FUNCTION_NAME) + assertThat(configureCaptor.firstValue.eventSourceArn()).isEqualTo(queue.arn) + } + + @Test + fun `Error configuring function`() { + val configureCaptor = argumentCaptor() + lambdaClient.stub { + on { createEventSourceMapping(configureCaptor.capture()) } doThrow InvalidParameterValueException.builder().message(ERROR_MESSAGE).build() + } + + runInEdtAndWait { + val dialog = ConfigureLambdaDialog(projectRule.project, queue) + assertThatThrownBy { dialog.configureLambda(TEST_FUNCTION_NAME) }.hasMessage(ERROR_MESSAGE) + } + assertThat(configureCaptor.firstValue.functionName()).isEqualTo(TEST_FUNCTION_NAME) + assertThat(configureCaptor.firstValue.eventSourceArn()).isEqualTo(queue.arn) + } + + @Test + fun `Error configuring after policy added`() { + val configureCaptor = argumentCaptor() + lambdaClient.stub { + on { createEventSourceMapping(configureCaptor.capture()) } doThrow ResourceConflictException.builder().message(ERROR_MESSAGE).build() + } + + runInEdtAndWait { + val dialog = ConfigureLambdaDialog(projectRule.project, queue) + runBlocking { + val identifier = dialog.waitUntilConfigured(TEST_FUNCTION_NAME) + assertThat(identifier).isNull() + } + } + assertThat(configureCaptor.firstValue.functionName()).isEqualTo(TEST_FUNCTION_NAME) + assertThat(configureCaptor.firstValue.eventSourceArn()).isEqualTo(queue.arn) + } + + @Test + fun `Success configuring after policy added`() { + val configureCaptor = argumentCaptor() + lambdaClient.stub { + on { createEventSourceMapping(configureCaptor.capture()) } doReturn + CreateEventSourceMappingResponse.builder().eventSourceArn(queue.arn).uuid(EVENT_IDENTIFIER).build() + } + + runInEdtAndWait { + val dialog = ConfigureLambdaDialog(projectRule.project, queue) + runBlocking { + val identifier = dialog.waitUntilConfigured(TEST_FUNCTION_NAME) + assertThat(identifier).isEqualTo(EVENT_IDENTIFIER) + } + } + assertThat(configureCaptor.firstValue.functionName()).isEqualTo(TEST_FUNCTION_NAME) + assertThat(configureCaptor.firstValue.eventSourceArn()).isEqualTo(queue.arn) + } + + private companion object { + const val TEST_FUNCTION_NAME = "Function" + const val EVENT_IDENTIFIER = "abc" + const val ERROR_MESSAGE = "Function has invalid permission" + } +} diff --git a/resources/resources/software/aws/toolkits/resources/localized_messages.properties b/resources/resources/software/aws/toolkits/resources/localized_messages.properties index fcc51dd50a..cb0c36ac8a 100644 --- a/resources/resources/software/aws/toolkits/resources/localized_messages.properties +++ b/resources/resources/software/aws/toolkits/resources/localized_messages.properties @@ -363,6 +363,7 @@ feedback.validation.comment_too_long=Comment is too long. feedback.validation.empty_comment=Please provide a comment. feedback.validation.no_sentiment=Please select how you're feeling. general.close_button=Close +general.configure_button=Configure general.create_button=Create general.file_not_found=File not found: "{0}" general.message=Message @@ -706,6 +707,20 @@ settings.states.invalid.short=Unable to connect settings.states.validating=Validating connection to AWS... settings.states.validating.short=Validating connection settings.title=AWS Connection Settings +sqs.configure.lambda=Configure Lambda Trigger +sqs.configure.lambda.configure.tooltip=It may take a few seconds to configure. +sqs.configure.lambda.error=Failed to add trigger for function {0}. Please try again. +sqs.configure.lambda.function=Lambda Function: +sqs.configure.lambda.in_progress=Configuring... +sqs.configure.lambda.select=Select Lambda Function: +sqs.configure.lambda.success=Trigger successfully added to function {0} +sqs.configure.lambda.tooltip=

Configure the queue to trigger an AWS Lambda function when new messages arrive in the queue.

The queue and Lambda function must be in the same AWS Region.

+sqs.configure.lambda.validation.function=Lambda function must be specified. +sqs.confirm.iam=Create IAM Role +sqs.confirm.iam.create=Create +sqs.confirm.iam.failed=Failed to create and attach role policy. +sqs.confirm.iam.in_progress=Creating... +sqs.confirm.iam.warning.text=The selected Lambda function does not have permission to access SQS.

The following IAM role policy will be added to the function's execution role:

sqs.create.fifo.label=FIFO sqs.create.queue.create=Create sqs.create.queue.failed=Failed to create queue {0} @@ -751,7 +766,7 @@ sqs.send.message.success=Sent message ID: {0} sqs.service_name=Amazon SQS sqs.standard.queue.tooltip=Supports at-least-once delivery and message ordering is not preserved. sqs.subscribe.sns=Subscribe to SNS topic -sqs.subscribe.sns.failed=Failed to subscribe {0} to {0} +sqs.subscribe.sns.failed=Failed to subscribe {0} to {1} sqs.subscribe.sns.in_progress=Subscribing... sqs.subscribe.sns.select=Select SNS topic sqs.subscribe.sns.select.tooltip=Messages from the selected topic are sent to the queue. From 873006f855d003a96f3fc5953943720d7d397ae7 Mon Sep 17 00:00:00 2001 From: Hunter Werlla Date: Thu, 27 Aug 2020 08:01:33 -0700 Subject: [PATCH 0046/1546] Fix SAM Gradle Hello World syncing twice (#2024) Don't call ExternalSystemUtil.refreshProjects twice. It's called in the parent class, and removed from all of the child classes except for the one for Hello World. Also, move the call to super to the bottom to align with the other child classes (setup everything then sync) Fixes #2003 --- .../bugfix-c6ad4272-6b5b-4ca5-b8a2-4c6684432870.json | 4 ++++ .../services/lambda/java/JavaSamProjectWizard.kt | 12 ++++-------- 2 files changed, 8 insertions(+), 8 deletions(-) create mode 100644 .changes/next-release/bugfix-c6ad4272-6b5b-4ca5-b8a2-4c6684432870.json diff --git a/.changes/next-release/bugfix-c6ad4272-6b5b-4ca5-b8a2-4c6684432870.json b/.changes/next-release/bugfix-c6ad4272-6b5b-4ca5-b8a2-4c6684432870.json new file mode 100644 index 0000000000..5a0987d8b9 --- /dev/null +++ b/.changes/next-release/bugfix-c6ad4272-6b5b-4ca5-b8a2-4c6684432870.json @@ -0,0 +1,4 @@ +{ + "type" : "bugfix", + "description" : "Fix SAM Gradle Hello World syncing twice (#2003)" +} diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/java/JavaSamProjectWizard.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/java/JavaSamProjectWizard.kt index 4f51c8ab33..fbeab8b2b2 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/java/JavaSamProjectWizard.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/java/JavaSamProjectWizard.kt @@ -98,7 +98,9 @@ abstract class JavaGradleSamProjectTemplate : JavaSamProjectTemplate() { val importSpecBuilder = ImportSpecBuilder(rootModel.project, GradleConstants.SYSTEM_ID) .forceWhenUptodate() - .useDefaultCallback() + // replaces useDefaultCallback which is removed in 2020.3 + // FIX_WHEN_MIN_IS_202 remove this callback (since we don't use it) + .callback(null) .use(ProgressExecutionMode.IN_BACKGROUND_ASYNC) ExternalSystemUtil.refreshProjects(importSpecBuilder) @@ -140,7 +142,6 @@ class SamHelloWorldGradle : JavaGradleSamProjectTemplate() { sourceCreatingProject: Project, indicator: ProgressIndicator ) { - super.postCreationAction(settings, contentRoot, rootModel, sourceCreatingProject, indicator) val buildFile = locateBuildFile(contentRoot, "build.gradle") ?: return val gradleProjectSettings = GradleProjectSettings().apply { @@ -151,12 +152,7 @@ class SamHelloWorldGradle : JavaGradleSamProjectTemplate() { val externalSystemSettings = ExternalSystemApiUtil.getSettings(rootModel.project, GradleConstants.SYSTEM_ID) externalSystemSettings.setLinkedProjectsSettings(setOf(gradleProjectSettings)) - val importSpecBuilder = ImportSpecBuilder(rootModel.project, GradleConstants.SYSTEM_ID) - .forceWhenUptodate() - .useDefaultCallback() - .use(ProgressExecutionMode.IN_BACKGROUND_ASYNC) - - ExternalSystemUtil.refreshProjects(importSpecBuilder) + super.postCreationAction(settings, contentRoot, rootModel, sourceCreatingProject, indicator) } } From 90540fd61837fd58be32be94022e97afaca438ae Mon Sep 17 00:00:00 2001 From: Hunter Werlla Date: Thu, 27 Aug 2020 10:30:44 -0700 Subject: [PATCH 0047/1546] Log into DockerHub for UI and Integration tests (#2026) --- buildspec/linuxIntegrationTests.yml | 4 ++++ buildspec/linuxUiTests.yml | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/buildspec/linuxIntegrationTests.yml b/buildspec/linuxIntegrationTests.yml index 95ea463324..92d8ad7eca 100644 --- a/buildspec/linuxIntegrationTests.yml +++ b/buildspec/linuxIntegrationTests.yml @@ -27,6 +27,10 @@ phases: - export TOKEN=`jq -r '.Credentials.SessionToken' creds.json` - pip3 install --user --upgrade aws-sam-cli - pip3 install --upgrade awscli + # login to DockerHub so we don't get throttled + - export DOCKER_USERNAME=`echo $DOCKER_HUB_TOKEN | jq -r '.username'` + - export DOCKER_PASSWORD=`echo $DOCKER_HUB_TOKEN | jq -r '.password'` + - docker login --username $DOCKER_USERNAME --password $DOCKER_PASSWORD || true build: commands: diff --git a/buildspec/linuxUiTests.yml b/buildspec/linuxUiTests.yml index ed4d130f87..6db4599a22 100644 --- a/buildspec/linuxUiTests.yml +++ b/buildspec/linuxUiTests.yml @@ -33,6 +33,10 @@ phases: aws_secret_access_key=$SECRET aws_session_token=$TOKEN" - pip3 install --user --upgrade aws-sam-cli + # login to DockerHub so we don't get throttled + - export DOCKER_USERNAME=`echo $DOCKER_HUB_TOKEN | jq -r '.username'` + - export DOCKER_PASSWORD=`echo $DOCKER_HUB_TOKEN | jq -r '.password'` + - docker login --username $DOCKER_USERNAME --password $DOCKER_PASSWORD || true build: commands: From d71f96cc0bce9814a7d4b1f2d00ed463da6ca3e8 Mon Sep 17 00:00:00 2001 From: Christopher Christou <39839589+awschristou@users.noreply.github.com> Date: Fri, 28 Aug 2020 14:44:05 -0700 Subject: [PATCH 0048/1546] Fix credential management diagram (#2028) --- .../images/classDiagram.svg | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/designs/credentialManagement/images/classDiagram.svg b/designs/credentialManagement/images/classDiagram.svg index 58b3d9df14..7acc439c49 100644 --- a/designs/credentialManagement/images/classDiagram.svg +++ b/designs/credentialManagement/images/classDiagram.svg @@ -1,18 +1,18 @@ -AwsRegionString partitionIdString nameString idCredentialIdentifierString factoryIdString displayNameString idCredentialsChangeEventadded: List<CredentialIdentifier>,modified: List<CredentialIdentifier>,removed: List<CredentialIdentifier>CredentialsChangeListeneronChange(CredentialsChangeEventCredentialManagerList<CredentialIdentifier> getCredentialIdentifiers()ToolkitCredentialsProvider getCredentialProvider(String id)ToolkitCredentialsProvider getAwsCredentialProvider(CredentialIdentifier, AwsRegion)CredentialProviderFactoryString idvoid setUp(callback CredentialsChangeListener)ToolkitCredentialsProvider createAwsCredentialProvider(CredentialIdentifier, AwsRegion, Suppier<SdkHttpClient>)ToolkitCredentialsProviderString displayNameString idAwsCredentialsProviderAwsCredentials resolveCredentials()InheritanceCreatesmany1Ownsmany1AwsRegionString partitionIdString nameString idCredentialIdentifierString factoryIdString displayNameString idCredentialsChangeEventadded: List<CredentialIdentifier>,modified: List<CredentialIdentifier>,removed: List<CredentialIdentifier>CredentialsChangeListeneronChange(CredentialsChangeEventCredentialManagerList<CredentialIdentifier> getCredentialIdentifiers()CredentialIdentifier getCredentialIdentifierById(String id)ToolkitCredentialsProvider getAwsCredentialProvider(CredentialIdentifier, AwsRegion)CredentialProviderFactoryString idvoid setUp(callback CredentialsChangeListener)ToolkitCredentialsProvider createAwsCredentialProvider(CredentialIdentifier, AwsRegion, Suppier<SdkHttpClient>)ToolkitCredentialsProviderString displayNameString idAwsCredentialsProviderAwsCredentials resolveCredentials()InheritanceCreatesmany1Ownsmany1 +--> \ No newline at end of file From 5b3a61e8611dbcccba1954be27b69e929532fc23 Mon Sep 17 00:00:00 2001 From: Austin Brooks Date: Mon, 31 Aug 2020 08:54:03 -0700 Subject: [PATCH 0049/1546] Render the release into a per-release changelog (#2029) --- .../gradle/changelog/ChangeLogGenerator.kt | 4 ++-- .../gradle/changelog/tasks/CreateRelease.kt | 20 ++++++++++++++++++- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/buildSrc/src/software/aws/toolkits/gradle/changelog/ChangeLogGenerator.kt b/buildSrc/src/software/aws/toolkits/gradle/changelog/ChangeLogGenerator.kt index 27a6371f05..46de61481c 100644 --- a/buildSrc/src/software/aws/toolkits/gradle/changelog/ChangeLogGenerator.kt +++ b/buildSrc/src/software/aws/toolkits/gradle/changelog/ChangeLogGenerator.kt @@ -13,7 +13,7 @@ import kotlin.streams.toList /** * Generates a combined change log file based in Markdown syntax */ -class ChangeLogGenerator(private val writers: List, private val logger: Logger) { +class ChangeLogGenerator(private val writers: List, private val logger: Logger) : AutoCloseable { fun addUnreleasedChanges(unreleasedFiles: List) { val entries = unreleasedFiles.parallelStream() .map { readFile(it.toFile()) } @@ -50,7 +50,7 @@ class ChangeLogGenerator(private val writers: List, private val } } - fun close() { + override fun close() { writers.forEach { it.close() } } diff --git a/buildSrc/src/software/aws/toolkits/gradle/changelog/tasks/CreateRelease.kt b/buildSrc/src/software/aws/toolkits/gradle/changelog/tasks/CreateRelease.kt index bff48d79d8..dfe351caa2 100644 --- a/buildSrc/src/software/aws/toolkits/gradle/changelog/tasks/CreateRelease.kt +++ b/buildSrc/src/software/aws/toolkits/gradle/changelog/tasks/CreateRelease.kt @@ -3,16 +3,22 @@ package software.aws.toolkits.gradle.changelog.tasks +import org.gradle.api.file.ProjectLayout import org.gradle.api.file.RegularFileProperty import org.gradle.api.provider.Property +import org.gradle.api.provider.Provider import org.gradle.api.tasks.Input +import org.gradle.api.tasks.Optional import org.gradle.api.tasks.OutputFile import org.gradle.api.tasks.TaskAction +import software.aws.toolkits.gradle.changelog.ChangeLogGenerator +import software.aws.toolkits.gradle.changelog.GithubWriter import software.aws.toolkits.gradle.changelog.ReleaseCreator import java.time.LocalDate import java.time.format.DateTimeFormatter +import javax.inject.Inject -open class CreateRelease : ChangeLogTask() { +open class CreateRelease @Inject constructor(projectLayout: ProjectLayout) : ChangeLogTask() { @Input val releaseDate: Property = project.objects.property(String::class.java).convention(DateTimeFormatter.ISO_DATE.format(LocalDate.now())) @@ -21,9 +27,16 @@ open class CreateRelease : ChangeLogTask() { (project.version as String).substringBeforeLast('-') }) + @Input + @Optional + val issuesUrl: Provider = project.objects.property(String::class.java).convention("https://github.com/aws/aws-toolkit-jetbrains/issues") + @OutputFile val releaseFile: RegularFileProperty = project.objects.fileProperty().convention(changesDirectory.file(releaseVersion.map { "$it.json" })) + @OutputFile + val changeLogFile: RegularFileProperty = project.objects.fileProperty().convention(projectLayout.buildDirectory.file("releaseChangeLog.md")) + @TaskAction fun create() { val releaseDate = DateTimeFormatter.ISO_DATE.parse(releaseDate.get()).let { @@ -38,5 +51,10 @@ open class CreateRelease : ChangeLogTask() { git.stage(releaseFile.get().asFile.absoluteFile) git.stage(nextReleaseDirectory.get().asFile.absoluteFile) } + + val generator = ChangeLogGenerator(listOf(GithubWriter(changeLogFile.get().asFile.toPath(), issuesUrl.get())), logger) + generator.use { + generator.addReleasedChanges(listOf(releaseFile.get().asFile.toPath())) + } } } From 8f14b9ce1cecd5062935a338e9adad87dbca52fd Mon Sep 17 00:00:00 2001 From: Richard Li <742829+rli@users.noreply.github.com> Date: Mon, 31 Aug 2020 09:27:42 -0700 Subject: [PATCH 0050/1546] Use byte array instead of intermediate file when resolving the cloud-debug executable (#2030) --- .../software/aws/toolkits/jetbrains/core/HttpUtils.kt | 3 +++ .../services/clouddebug/CloudDebugResolver.kt | 10 ++++------ .../aws/toolkits/jetbrains/utils/ZipDecompressor.kt | 5 +++-- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/core/HttpUtils.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/core/HttpUtils.kt index 9ce15a73b4..9e435250e8 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/core/HttpUtils.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/core/HttpUtils.kt @@ -10,5 +10,8 @@ import java.nio.file.Path fun saveFileFromUrl(url: String, path: Path, indicator: ProgressIndicator? = null) = HttpRequests.request(url).userAgent(AwsClientManager.userAgent).saveToFile(path.toFile(), indicator) +fun readBytesFromUrl(url: String, indicator: ProgressIndicator? = null) = + HttpRequests.request(url).userAgent(AwsClientManager.userAgent).readBytes(indicator) + fun getTextFromUrl(url: String): String = HttpRequests.request(url).userAgent(AwsClientManager.userAgent).readString() diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/clouddebug/CloudDebugResolver.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/clouddebug/CloudDebugResolver.kt index 302ce08564..14439d70f7 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/clouddebug/CloudDebugResolver.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/clouddebug/CloudDebugResolver.kt @@ -14,7 +14,7 @@ import software.aws.toolkits.jetbrains.core.executables.ExecutableInstance import software.aws.toolkits.jetbrains.core.executables.ExecutableManager import software.aws.toolkits.jetbrains.core.executables.ExecutableType import software.aws.toolkits.jetbrains.core.getTextFromUrl -import software.aws.toolkits.jetbrains.core.saveFileFromUrl +import software.aws.toolkits.jetbrains.core.readBytesFromUrl import software.aws.toolkits.jetbrains.services.clouddebug.execution.Context import software.aws.toolkits.jetbrains.services.clouddebug.execution.MessageEmitter import software.aws.toolkits.jetbrains.services.clouddebug.execution.steps.CloudDebugCliValidate @@ -24,7 +24,6 @@ import software.aws.toolkits.resources.message import software.aws.toolkits.telemetry.ClouddebugTelemetry import software.aws.toolkits.telemetry.Result import java.net.URL -import java.nio.file.Files import java.time.Duration import java.time.Instant @@ -123,18 +122,17 @@ object CloudDebugResolver { val startTime = Instant.now() try { // TODO: Consider a way to handle input stream directly? HttpRequests/Decompressor can do this if we have a tar.gz - val zipFile = Files.createTempFile("cloud-debug", ".zip") // TODO: add progress indicator, preferably to this implementation as this is synchronous. - saveFileFromUrl(manifest.location, zipFile, null) + val bytes = readBytesFromUrl(manifest.location, null) // checksum checker - val zipSignature = DigestUtils.sha256Hex(Files.readAllBytes(zipFile)) + val zipSignature = DigestUtils.sha256Hex(bytes) if (zipSignature != manifest.checksum) { throw RuntimeException(message("cloud_debug.step.clouddebug.checksum.fail")) } val directory = ExecutableType.EXECUTABLE_DIRECTORY.toFile() - ZipDecompressor(zipFile.toFile()).use { + ZipDecompressor(bytes).use { it.extract(directory) } emitInstallMetric( diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/utils/ZipDecompressor.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/utils/ZipDecompressor.kt index 3f2c239ce9..2e1858dc7e 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/utils/ZipDecompressor.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/utils/ZipDecompressor.kt @@ -7,6 +7,7 @@ import com.intellij.openapi.util.SystemInfo import com.intellij.openapi.util.io.FileUtil import org.apache.commons.compress.archivers.zip.ZipArchiveEntry import org.apache.commons.compress.archivers.zip.ZipFile +import org.apache.commons.compress.utils.SeekableInMemoryByteChannel import java.io.File import java.io.FileOutputStream import java.io.IOException @@ -14,8 +15,8 @@ import java.nio.file.Files import java.nio.file.attribute.PosixFilePermission // TODO: Write tests -class ZipDecompressor(sourceFile: File) : AutoCloseable { - private val zipFile = ZipFile(sourceFile) +class ZipDecompressor(sourceBytes: ByteArray) : AutoCloseable { + private val zipFile = ZipFile(SeekableInMemoryByteChannel(sourceBytes)) private val zipEntries = zipFile.entries.toList() private val directorySplitRegex = Regex.fromLiteral("""[/\\]""") From bb247e9a43863a8bc21ccdefe8efc76390013ad4 Mon Sep 17 00:00:00 2001 From: Richard Li <742829+rli@users.noreply.github.com> Date: Mon, 31 Aug 2020 15:57:34 -0700 Subject: [PATCH 0051/1546] Update telemetry client (#2033) --- .../aws/toolkits/core/telemetry/MetricEvent.kt | 11 +++++++++++ gradle.properties | 2 +- .../services/telemetry/DefaultTelemetryPublisher.kt | 1 + telemetry-client/telemetryC2J/service-2.json | 4 +++- 4 files changed, 16 insertions(+), 2 deletions(-) diff --git a/core/src/software/aws/toolkits/core/telemetry/MetricEvent.kt b/core/src/software/aws/toolkits/core/telemetry/MetricEvent.kt index 2f85fa51f9..2fe3bf4592 100644 --- a/core/src/software/aws/toolkits/core/telemetry/MetricEvent.kt +++ b/core/src/software/aws/toolkits/core/telemetry/MetricEvent.kt @@ -31,6 +31,7 @@ interface MetricEvent { val name: String val value: Double val unit: MetricUnit + val passive: Boolean val metadata: Map interface Builder { @@ -40,6 +41,8 @@ interface MetricEvent { fun unit(unit: MetricUnit): Builder + fun passive(value: Boolean): Builder + fun metadata(key: String, value: String): Builder fun metadata(key: String, value: Boolean): Builder = metadata(key, value.toString()) @@ -113,11 +116,13 @@ class DefaultMetricEvent internal constructor( override val name: String, override val value: Double, override val unit: MetricUnit, + override val passive: Boolean, override val metadata: Map ) : MetricEvent.Datum { class BuilderImpl(private var name: String) : MetricEvent.Datum.Builder { private var value: Double = 0.0 private var unit: MetricUnit = MetricUnit.NONE + private var passive: Boolean = false private val metadata: MutableMap = HashMap() override fun name(name: String): MetricEvent.Datum.Builder { @@ -135,6 +140,11 @@ class DefaultMetricEvent internal constructor( return this } + override fun passive(value: Boolean): MetricEvent.Datum.Builder { + this.passive = value + return this + } + override fun metadata(key: String, value: String): MetricEvent.Datum.Builder { if (metadata.containsKey(key)) { LOG.warn { "Attempted to add multiple pieces of metadata with the same key" } @@ -154,6 +164,7 @@ class DefaultMetricEvent internal constructor( name.replaceIllegal(), this.value, this.unit, + this.passive, this.metadata ) } diff --git a/gradle.properties b/gradle.properties index 3caa5dc8fe..57951dfeae 100644 --- a/gradle.properties +++ b/gradle.properties @@ -16,7 +16,7 @@ coroutinesVersion=1.3.3 ideaPluginVersion=0.4.20 ktlintVersion=0.36.0 jacksonVersion=2.9.8 -telemetryVersion=0.0.39 +telemetryVersion=0.0.43 assertjVersion=3.15.0 junitVersion=4.12 diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/telemetry/DefaultTelemetryPublisher.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/telemetry/DefaultTelemetryPublisher.kt index e6f772cb69..74f753054b 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/telemetry/DefaultTelemetryPublisher.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/telemetry/DefaultTelemetryPublisher.kt @@ -62,6 +62,7 @@ class DefaultTelemetryPublisher( .metricName(metricName) .unit(datum.unit) .value(datum.value) + .passive(datum.passive) .metadata( datum.metadata.entries.stream().map { MetadataEntry.builder() diff --git a/telemetry-client/telemetryC2J/service-2.json b/telemetry-client/telemetryC2J/service-2.json index 328facd49e..9a8f72a5f6 100644 --- a/telemetry-client/telemetryC2J/service-2.json +++ b/telemetry-client/telemetryC2J/service-2.json @@ -130,13 +130,15 @@ "EpochTimestamp":{"shape":"EpochTimestamp"}, "Unit":{"shape":"Unit"}, "Value":{"shape":"Datapoint"}, - "Metadata":{"shape":"Metadata"} + "Metadata":{"shape":"Metadata"}, + "Passive":{"shape":"Passive"} } }, "MetricName":{ "type":"string", "pattern":"^[\\w+-.:]{1,255}$" }, + "Passive":{"type":"boolean"}, "PostErrorReportRequest":{ "type":"structure", "required":[ From 2532f42f6cdbb09ebb655a1167106fb07035e7d8 Mon Sep 17 00:00:00 2001 From: Richard Li <742829+rli@users.noreply.github.com> Date: Tue, 1 Sep 2020 09:44:43 -0700 Subject: [PATCH 0052/1546] Fix issue launching remote terminal on 2020.2 (#2034) --- .../bugfix-44f91df3-3dcf-48dd-af38-95acb6c8236f.json | 4 ++++ .../clouddebug/actions/StartRemoteShellAction.kt | 12 ++++++++---- 2 files changed, 12 insertions(+), 4 deletions(-) create mode 100644 .changes/next-release/bugfix-44f91df3-3dcf-48dd-af38-95acb6c8236f.json diff --git a/.changes/next-release/bugfix-44f91df3-3dcf-48dd-af38-95acb6c8236f.json b/.changes/next-release/bugfix-44f91df3-3dcf-48dd-af38-95acb6c8236f.json new file mode 100644 index 0000000000..fea62290f7 --- /dev/null +++ b/.changes/next-release/bugfix-44f91df3-3dcf-48dd-af38-95acb6c8236f.json @@ -0,0 +1,4 @@ +{ + "type" : "bugfix", + "description" : "Fix remote terminal start issue on 2020.2" +} \ No newline at end of file diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/clouddebug/actions/StartRemoteShellAction.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/clouddebug/actions/StartRemoteShellAction.kt index 6556c83526..35cda01037 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/clouddebug/actions/StartRemoteShellAction.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/clouddebug/actions/StartRemoteShellAction.kt @@ -11,10 +11,12 @@ import com.intellij.openapi.progress.ProgressIndicator import com.intellij.openapi.progress.ProgressManager import com.intellij.openapi.progress.Task import com.intellij.openapi.project.Project +import com.pty4j.PtyProcess import icons.TerminalIcons -import org.jetbrains.plugins.terminal.LocalTerminalDirectRunner import org.jetbrains.plugins.terminal.TerminalTabState import org.jetbrains.plugins.terminal.TerminalView +import org.jetbrains.plugins.terminal.cloud.CloudTerminalProcess +import org.jetbrains.plugins.terminal.cloud.CloudTerminalRunner import software.aws.toolkits.jetbrains.core.credentials.AwsConnectionManager import software.aws.toolkits.jetbrains.core.credentials.activeCredentialProvider import software.aws.toolkits.jetbrains.core.credentials.activeRegion @@ -108,9 +110,11 @@ class StartRemoteShellAction(private val project: Project, private val container .withParameters("--tty") .withParameters("/aws/cloud-debug/common/busybox", "sh", "-i") - val runner = object : LocalTerminalDirectRunner(project) { - override fun getCommand(envs: MutableMap?) = cmdLine.getCommandLineList(null).toTypedArray() - } + val cmdList = cmdLine.getCommandLineList(null).toTypedArray() + val env = cmdLine.effectiveEnvironment + val ptyProcess = PtyProcess.exec(cmdList, env, null) + val process = CloudTerminalProcess(ptyProcess.outputStream, ptyProcess.inputStream) + val runner = CloudTerminalRunner(project, containerName, process) runInEdt { TerminalView.getInstance(project).createNewSession(runner, TerminalTabState().also { it.myTabName = containerName }) From 8e99c4522ced6b13f9fe5845fc3a0bee8f010adf Mon Sep 17 00:00:00 2001 From: Austin Brooks Date: Wed, 2 Sep 2020 08:16:35 -0700 Subject: [PATCH 0053/1546] Upgrade ktlint (#2035) --- build.gradle.kts | 2 +- .../aws/toolkits/gradle/SourcesUtils.kt | 8 +- .../gradle/changelog/ChangeLogGenerator.kt | 1 - .../gradle/changelog/ChangeLogWriter.kt | 2 +- .../gradle/changelog/JetBrainsWriter.kt | 16 ++- .../gradle/changelog/tasks/CreateRelease.kt | 8 +- .../changelog/tasks/GenerateChangeLog.kt | 1 - .../gradle/changelog/tasks/NewChange.kt | 9 +- .../aws/toolkits/gradle/sdk/GenerateSdk.kt | 1 - .../gradle/changelog/GithubWriterTest.kt | 4 +- .../gradle/changelog/JetBrainsWriterTest.kt | 8 +- .../gradle/changelog/ReleaseCreatorTest.kt | 6 +- .../core/credentials/sso/DiskCacheTest.kt | 2 + .../lambda/LambdaSampleEventProviderTest.kt | 14 +- .../core/rules/EnvironmentVariableHelper.kt | 8 +- gradle.properties | 2 +- .../clouddebug/java/JavaDebugEndToEndTest.kt | 37 +++-- .../python/PythonDebugEndToEndTest.kt | 40 +++--- .../services/lambda/deploy/SamDeployTest.kt | 2 +- ...alLambdaRunConfigurationIntegrationTest.kt | 10 +- .../toolkits/jetbrains/utils/JavaTestUtils.kt | 11 +- .../aws/toolkits/jetbrains/utils/TestUtils.kt | 67 +++++----- .../jetbrains/core/AwsClientManager.kt | 11 +- .../core/credentials/AwsConnectionManager.kt | 21 +-- .../core/credentials/AwsSettingsPanel.kt | 3 +- .../ChangeConnectionSettingsMenu.kt | 3 +- .../CorrectThreadCredentialsProvider.kt | 4 +- .../core/credentials/CredentialManager.kt | 4 +- .../core/credentials/CredentialWriter.kt | 5 +- .../DefaultAwsConnectionManager.kt | 3 +- .../ProfileCredentialProviderFactory.kt | 28 ++-- .../core/explorer/ExplorerToolWindow.kt | 56 ++++---- .../explorer/actions/DeleteResourceAction.kt | 3 +- .../core/notification/NoticeManager.kt | 3 +- .../clouddebug/CloudDebugVersionCache.kt | 11 +- ...dDebuggingExplorerTreeStructureProvider.kt | 35 ++--- .../clouddebug/actions/PseCliAction.kt | 54 ++++---- .../actions/StartRemoteShellAction.kt | 126 +++++++++--------- .../execution/CloudDebugRunState.kt | 13 +- .../execution/steps/AttachDebuggers.kt | 19 +-- .../execution/steps/PortForwarding.kt | 2 +- .../python/PythonDebuggerSupport.kt | 9 +- .../resources/CloudDebuggingResources.kt | 10 +- .../CloudFormationExplorerNodes.kt | 8 +- .../CloudFormationTemplateIndex.kt | 28 ++-- .../cloudformation/IndexedResources.kt | 32 ++--- .../cloudformation/stack/EventsFetcher.kt | 64 ++++----- .../services/cloudformation/stack/Stack.kt | 84 ++++++------ .../cloudwatch/logs/CloudWatchLogWindow.kt | 3 +- .../logs/editor/CloudWatchLogGroup.kt | 12 +- .../logs/editor/CloudWatchLogStream.kt | 21 +-- .../cloudwatch/logs/editor/LocationCrumbs.kt | 26 ++-- .../cloudwatch/logs/editor/LogGroupTable.kt | 29 ++-- .../toolkits/jetbrains/services/ecs/Ecs.kt | 8 +- .../ecs/execution/PortMappingsTable.kt | 6 +- .../services/iam/CreateIamRoleDialog.kt | 9 +- .../services/lambda/LambdaExplorerNodes.kt | 3 +- .../local/LocalLambdaRunSettingsEditor.kt | 6 +- .../lambda/execution/local/SamDebugger.kt | 18 +-- .../lambda/java/JavaLambdaHandlerResolver.kt | 34 ++--- .../services/lambda/sam/SamCommon.kt | 8 +- .../services/lambda/sam/SamExecutable.kt | 24 ++-- .../SamSchemaDownloadPostCreationAction.kt | 5 +- .../services/lambda/sam/SamTemplateUtils.kt | 34 ++--- .../lambda/upload/EditFunctionDialog.kt | 20 +-- .../services/lambda/upload/LambdaPolicies.kt | 6 +- .../services/s3/CreateS3BucketDialog.kt | 9 +- .../jetbrains/services/s3/TransferUtils.kt | 48 ++++--- .../services/s3/editor/S3TreeNode.kt | 8 +- .../services/s3/editor/S3TreeTable.kt | 19 +-- .../services/s3/editor/S3ViewerPanel.kt | 8 +- .../s3/objectActions/DeleteObjectAction.kt | 3 +- .../services/schemas/SchemasExplorerNodes.kt | 3 +- .../code/DownloadCodeForSchemaDialog.kt | 44 +++--- .../schemas/code/SchemaCodeDownloader.kt | 26 ++-- .../schemas/search/SchemaSearchDialog.kt | 10 +- .../schemas/search/SchemasSearchDialogBase.kt | 57 ++++---- .../jetbrains/settings/DeploySettings.kt | 6 +- .../aws/toolkits/jetbrains/ui/HandlerPanel.kt | 24 ++-- .../toolkits/jetbrains/ui/ResourceSelector.kt | 20 ++- .../ui/wizard/PyCharmSdkSelectionPanel.kt | 23 ++-- .../ui/wizard/SamInitProjectBuilderCommon.kt | 25 ++-- .../ui/wizard/SamProjectGenerator.kt | 12 +- .../SamProjectGeneratorIntelliJShims.kt | 28 ++-- .../jetbrains/utils/NotificationUtils.kt | 3 +- .../jetbrains/utils/ui/ScrollBarUtils.kt | 44 +++--- .../jetbrains/utils/ui/SearchFieldUtils.kt | 44 +++--- .../jetbrains/core/AwsClientManagerTest.kt | 13 +- .../DefaultAwsConnectionManagerTest.kt | 43 +++--- .../ProfileCredentialProviderFactoryTest.kt | 10 +- .../core/credentials/ProfileReaderTest.kt | 6 +- .../core/credentials/ProfileWatcherTest.kt | 15 ++- .../RefreshConnectionActionTest.kt | 11 +- .../explorer/AwsExplorerNodeProcessorTest.kt | 12 +- .../jetbrains/services/RoleValidationTest.kt | 30 +++-- .../CloudFormationParametersTest.kt | 6 +- .../CloudFormationServiceNodeTest.kt | 3 +- .../CloudFormationTemplateCanDeployTest.kt | 58 +++++--- .../CloudFormationTemplateIndexTest.kt | 91 +++++++++---- .../cloudformation/DeleteWaiterTest.kt | 84 +++++++----- .../services/cloudformation/stack/Mocks.kt | 19 +-- .../yaml/YamlCloudFormationTemplateTest.kt | 27 ++-- .../logs/CloudWatchLogsServiceNodeTest.kt | 9 +- .../cloudwatch/logs/LogStreamListActorTest.kt | 16 ++- .../logs/OpenLogStreamInEditorActionTest.kt | 18 +-- .../ecs/execution/DockerfileParserTest.kt | 13 +- .../EcsCloudDebugRunConfigurationTest.kt | 8 +- .../services/lambda/LambdaServiceNodeTest.kt | 12 +- .../DeploySamApplicationValidatorTest.kt | 14 +- ...LocalLambdaRunConfigurationProducerTest.kt | 10 +- .../local/LocalLambdaRunConfigurationTest.kt | 23 ++-- .../execution/local/SamInvokeRunnerTest.kt | 8 +- .../remote/RemoteLambdaExecutionTest.kt | 12 +- .../LambdaRunLineMarkerContributorTest.kt | 21 ++- .../services/lambda/sam/SamCommonTest.kt | 8 +- .../lambda/sam/SamTemplateUtilsTest.kt | 10 +- .../lambda/upload/CreateLambdaFunctionTest.kt | 3 +- .../services/s3/S3ServiceNodeTest.kt | 9 +- .../services/s3/S3VirtualBucketTest.kt | 11 +- .../schemas/SchemaRegistryNodeTest.kt | 3 +- .../schemas/SchemasServiceNodeTest.kt | 3 +- .../code/DownloadCodeForSchemaDialogTest.kt | 18 ++- .../schemas/code/SchemaCodeDownloaderTest.kt | 2 +- .../DefaultTelemetryPublisherTest.kt | 52 ++++---- .../toolkits/jetbrains/utils/TextUtilsTest.kt | 10 +- .../jetbrains/utils/YamlWriterTest.kt | 6 +- .../rules/JavaCodeInsightTestFixtureRule.kt | 2 +- .../clouddebug/DotNetDebuggerSupport.kt | 23 ++-- .../clouddebug/DotNetStartupCommand.kt | 6 +- .../dotnet/DotNetLambdaHandlerResolver.kt | 3 +- .../lambda/dotnet/DotNetSamDebugSupport.kt | 17 ++- .../RiderLambdaHandlerFakePsiElement.kt | 6 +- .../ui/wizard/DotNetSamProjectGenerator.kt | 28 ++-- .../jetbrains/utils/DotNetRuntimeUtils.kt | 3 +- .../tst/base/RiderTestFrameworkUtils.kt | 3 +- ...DotNetEcsCloudDebugRunConfigurationTest.kt | 8 +- .../DotNetHandlerCompletionProviderTest.kt | 6 +- .../dotnet/DotNetLambdaHandlerResolverTest.kt | 22 +-- .../nodejs/NodeJsDebugEndToEndTest.kt | 26 ++-- .../lambda/nodejs/NodeJsLambdaBuilderTest.kt | 8 +- ...alLambdaRunConfigurationIntegrationTest.kt | 5 +- .../jetbrains/services/rds/RdsResources.kt | 6 +- .../services/redshift/RedshiftUtils.kt | 6 +- .../toolkits/jetbrains/ui/AwsAuthWidget.kt | 6 +- .../nodejs/NodeJsDebuggerSupport.kt | 3 +- .../lambda/nodejs/NodeJsSamDebugSupport.kt | 10 +- .../services/rds/RdsExplorerNodeTest.kt | 12 +- .../actions/CreateConfigurationActionTest.kt | 9 +- .../redshift/RedshiftExplorerNodeTest.kt | 3 +- .../actions/CreateDataSourceActionTest.kt | 3 +- .../nodejs/NodeJsLambdaHandlerResolverTest.kt | 85 +++++++----- .../rules/NodeJsCodeInsightTestFixtureRule.kt | 10 +- .../ktlint/rules/BannedPatternRule.kt | 18 ++- .../ktlint/rules/BannedPatternRuleTest.kt | 25 +++- .../ktlint/rules/CopyrightHeaderRuleTest.kt | 4 +- .../ktlint/rules/DialogModalityRuleTest.kt | 24 ++-- .../ktlint/rules/ExpressionBodyRuleTest.kt | 28 ++-- .../toolkits/ktlint/rules/LazyLogRuleTest.kt | 8 +- .../uitests/extensions/IdeExtension.kt | 24 ++-- 159 files changed, 1653 insertions(+), 1158 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 89900757a4..9e91c789ea 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -299,7 +299,7 @@ tasks.register("generateChangeLog") { val ktlintTask = tasks.register("ktlint") { description = "Check Kotlin code style." - classpath = configurations.getByName("ktlint") + classpath = ktlint group = "verification" main = "com.pinterest.ktlint.Main" diff --git a/buildSrc/src/software/aws/toolkits/gradle/SourcesUtils.kt b/buildSrc/src/software/aws/toolkits/gradle/SourcesUtils.kt index 203edaa8b4..6d92cd24f5 100644 --- a/buildSrc/src/software/aws/toolkits/gradle/SourcesUtils.kt +++ b/buildSrc/src/software/aws/toolkits/gradle/SourcesUtils.kt @@ -14,9 +14,11 @@ import java.io.FileFilter * [type] is the type of the source folder (e.g. 'src', 'tst', 'resources') * [ideProfile] is the IDE [Profile] currently configured in the project */ -fun findFolders(project: Project, type: String, ideProfile: Profile): Set = project.projectDir.listFiles(FileFilter { - it.isDirectory && includeFolder(type, ideProfile.shortName, it.name) -})?.map { File(it.name) }?.toSet() ?: setOf() +fun findFolders(project: Project, type: String, ideProfile: Profile): Set = project.projectDir.listFiles( + FileFilter { + it.isDirectory && includeFolder(type, ideProfile.shortName, it.name) + } +)?.map { File(it.name) }?.toSet() ?: setOf() /** * Determines if a folder should be included based on the ideVersion being targeted diff --git a/buildSrc/src/software/aws/toolkits/gradle/changelog/ChangeLogGenerator.kt b/buildSrc/src/software/aws/toolkits/gradle/changelog/ChangeLogGenerator.kt index 46de61481c..570cd6f1c1 100644 --- a/buildSrc/src/software/aws/toolkits/gradle/changelog/ChangeLogGenerator.kt +++ b/buildSrc/src/software/aws/toolkits/gradle/changelog/ChangeLogGenerator.kt @@ -9,7 +9,6 @@ import java.time.LocalDate import java.time.format.DateTimeFormatter import kotlin.streams.toList -/* ktlint-disable custom-ktlint-rules:log-not-lazy */ /** * Generates a combined change log file based in Markdown syntax */ diff --git a/buildSrc/src/software/aws/toolkits/gradle/changelog/ChangeLogWriter.kt b/buildSrc/src/software/aws/toolkits/gradle/changelog/ChangeLogWriter.kt index 9ab962da05..7ef459f3e4 100644 --- a/buildSrc/src/software/aws/toolkits/gradle/changelog/ChangeLogWriter.kt +++ b/buildSrc/src/software/aws/toolkits/gradle/changelog/ChangeLogWriter.kt @@ -21,7 +21,7 @@ abstract class ChangeLogWriter(issueUrl: String? = null) { return entry } - val regex = """#(\d+)""".toRegex() + val regex = "#(\\d+)".toRegex() return regex.replace(entry) { val issue = it.groups[1]?.value ?: return@replace it.value "[#$issue]($issueUrl$issue)" diff --git a/buildSrc/src/software/aws/toolkits/gradle/changelog/JetBrainsWriter.kt b/buildSrc/src/software/aws/toolkits/gradle/changelog/JetBrainsWriter.kt index 1d94c61c0e..c47404b09e 100644 --- a/buildSrc/src/software/aws/toolkits/gradle/changelog/JetBrainsWriter.kt +++ b/buildSrc/src/software/aws/toolkits/gradle/changelog/JetBrainsWriter.kt @@ -24,18 +24,21 @@ class JetBrainsWriter(private val changeNotesFile: File, issueUrl: String? = nul .build() val parser = Parser.builder() .postProcessor { - it.accept(object : AbstractVisitor() { - override fun visit(heading: Heading) { - heading.level = max(1, min(heading.level + 2, 6)) + it.accept( + object : AbstractVisitor() { + override fun visit(heading: Heading) { + heading.level = max(1, min(heading.level + 2, 6)) + } } - }) + ) it } .build() val htmlVersionError = renderer.render(parser.parse(sb.toString())) - changeNotesFile.writeText(""" + changeNotesFile.writeText( + """ - """.trimIndent()) + """.trimIndent() + ) } override fun toString(): String = "JetBrainsWriter(file=$changeNotesFile)" diff --git a/buildSrc/src/software/aws/toolkits/gradle/changelog/tasks/CreateRelease.kt b/buildSrc/src/software/aws/toolkits/gradle/changelog/tasks/CreateRelease.kt index dfe351caa2..68c9cbc35f 100644 --- a/buildSrc/src/software/aws/toolkits/gradle/changelog/tasks/CreateRelease.kt +++ b/buildSrc/src/software/aws/toolkits/gradle/changelog/tasks/CreateRelease.kt @@ -23,9 +23,11 @@ open class CreateRelease @Inject constructor(projectLayout: ProjectLayout) : Cha val releaseDate: Property = project.objects.property(String::class.java).convention(DateTimeFormatter.ISO_DATE.format(LocalDate.now())) @Input - val releaseVersion: Property = project.objects.property(String::class.java).convention(project.provider { - (project.version as String).substringBeforeLast('-') - }) + val releaseVersion: Property = project.objects.property(String::class.java).convention( + project.provider { + (project.version as String).substringBeforeLast('-') + } + ) @Input @Optional diff --git a/buildSrc/src/software/aws/toolkits/gradle/changelog/tasks/GenerateChangeLog.kt b/buildSrc/src/software/aws/toolkits/gradle/changelog/tasks/GenerateChangeLog.kt index 6bf4addec9..47e24670a4 100644 --- a/buildSrc/src/software/aws/toolkits/gradle/changelog/tasks/GenerateChangeLog.kt +++ b/buildSrc/src/software/aws/toolkits/gradle/changelog/tasks/GenerateChangeLog.kt @@ -15,7 +15,6 @@ import software.aws.toolkits.gradle.changelog.ChangeLogWriter import software.aws.toolkits.gradle.changelog.GithubWriter import software.aws.toolkits.gradle.changelog.JetBrainsWriter -/* ktlint-disable custom-ktlint-rules:log-not-lazy */ abstract class GenerateChangeLog(private val shouldStage: Boolean) : ChangeLogTask() { @Input @Optional diff --git a/buildSrc/src/software/aws/toolkits/gradle/changelog/tasks/NewChange.kt b/buildSrc/src/software/aws/toolkits/gradle/changelog/tasks/NewChange.kt index a99078b7da..c52dcc8cab 100644 --- a/buildSrc/src/software/aws/toolkits/gradle/changelog/tasks/NewChange.kt +++ b/buildSrc/src/software/aws/toolkits/gradle/changelog/tasks/NewChange.kt @@ -59,13 +59,14 @@ open class NewChange : ChangeLogTask() { } private fun createChange(changeType: ChangeType, description: String) = newFile(changeType).apply { - MAPPER.writerWithDefaultPrettyPrinter().writeValue(this, + MAPPER.writerWithDefaultPrettyPrinter().writeValue( + this, Entry(changeType, description) ) } private fun newFile(changeType: ChangeType) = nextReleaseDirectory.file("${changeType.name.toLowerCase()}-${UUID.randomUUID()}.json").get().asFile.apply { - parentFile?.mkdirs() - createNewFile() - } + parentFile?.mkdirs() + createNewFile() + } } diff --git a/buildSrc/src/software/aws/toolkits/gradle/sdk/GenerateSdk.kt b/buildSrc/src/software/aws/toolkits/gradle/sdk/GenerateSdk.kt index 77ec1a439a..7fdff84d2f 100644 --- a/buildSrc/src/software/aws/toolkits/gradle/sdk/GenerateSdk.kt +++ b/buildSrc/src/software/aws/toolkits/gradle/sdk/GenerateSdk.kt @@ -15,7 +15,6 @@ import software.amazon.awssdk.codegen.model.service.ServiceModel import software.amazon.awssdk.codegen.utils.ModelLoaderUtils import java.io.File -/* ktlint-disable custom-ktlint-rules:log-not-lazy */ open class GenerateSdk : DefaultTask() { @InputDirectory lateinit var c2jFolder: File diff --git a/buildSrc/tst/software/aws/toolkits/gradle/changelog/GithubWriterTest.kt b/buildSrc/tst/software/aws/toolkits/gradle/changelog/GithubWriterTest.kt index 5c0cd13c4b..79d566c8b2 100644 --- a/buildSrc/tst/software/aws/toolkits/gradle/changelog/GithubWriterTest.kt +++ b/buildSrc/tst/software/aws/toolkits/gradle/changelog/GithubWriterTest.kt @@ -64,7 +64,9 @@ class GithubWriterTest { sut.writeLine( renderEntry( ReleaseEntry( - LocalDate.of(2017, 2, 1), "2.0.0-preview-3", listOf( + LocalDate.of(2017, 2, 1), + "2.0.0-preview-3", + listOf( Entry( ChangeType.FEATURE, "A feature with some an issue link #45 or (#12) but not regular #hash" diff --git a/buildSrc/tst/software/aws/toolkits/gradle/changelog/JetBrainsWriterTest.kt b/buildSrc/tst/software/aws/toolkits/gradle/changelog/JetBrainsWriterTest.kt index 888a8f2fc0..47d2751712 100644 --- a/buildSrc/tst/software/aws/toolkits/gradle/changelog/JetBrainsWriterTest.kt +++ b/buildSrc/tst/software/aws/toolkits/gradle/changelog/JetBrainsWriterTest.kt @@ -103,7 +103,9 @@ class JetBrainsWriterTest { sut.writeLine( renderEntry( ReleaseEntry( - LocalDate.of(2017, 2, 1), "2.0.0-preview-3", listOf( + LocalDate.of(2017, 2, 1), + "2.0.0-preview-3", + listOf( Entry( ChangeType.FEATURE, "A feature with some *code* sample\n```java\nhello();\n```" @@ -136,7 +138,9 @@ class JetBrainsWriterTest { sut.writeLine( renderEntry( ReleaseEntry( - LocalDate.of(2017, 2, 1), "2.0.0-preview-3", listOf( + LocalDate.of(2017, 2, 1), + "2.0.0-preview-3", + listOf( Entry( ChangeType.FEATURE, "A feature with some an issue link #45 or (#12) but not regular #hash" diff --git a/buildSrc/tst/software/aws/toolkits/gradle/changelog/ReleaseCreatorTest.kt b/buildSrc/tst/software/aws/toolkits/gradle/changelog/ReleaseCreatorTest.kt index 17c6edc904..d952cd839c 100644 --- a/buildSrc/tst/software/aws/toolkits/gradle/changelog/ReleaseCreatorTest.kt +++ b/buildSrc/tst/software/aws/toolkits/gradle/changelog/ReleaseCreatorTest.kt @@ -30,7 +30,7 @@ class ReleaseCreatorTest { "type": "bugfix", "description": "Some bugfix" } - """.trimIndent() + """.trimIndent() ) } @@ -41,7 +41,7 @@ class ReleaseCreatorTest { "type": "feature", "description": "Some feature" } - """.trimIndent() + """.trimIndent() ) } @@ -62,7 +62,7 @@ class ReleaseCreatorTest { "description" : "Some bugfix" } ] } - """.trimIndent() + """.trimIndent() ) assertThat(firstFile).doesNotExist() diff --git a/core/tst/software/aws/toolkits/core/credentials/sso/DiskCacheTest.kt b/core/tst/software/aws/toolkits/core/credentials/sso/DiskCacheTest.kt index 791d64cc1c..ca202ba62f 100644 --- a/core/tst/software/aws/toolkits/core/credentials/sso/DiskCacheTest.kt +++ b/core/tst/software/aws/toolkits/core/credentials/sso/DiskCacheTest.kt @@ -21,6 +21,8 @@ import java.time.ZonedDateTime import java.time.format.DateTimeFormatter import java.time.temporal.ChronoUnit +/* ktlint-disable parameter-list-wrapping */ +// TODO bugs in parameter list wrapping in our multiline strings, https://github.com/pinterest/ktlint/issues/859 class DiskCacheTest { @Rule @JvmField diff --git a/core/tst/software/aws/toolkits/core/lambda/LambdaSampleEventProviderTest.kt b/core/tst/software/aws/toolkits/core/lambda/LambdaSampleEventProviderTest.kt index ec83119a0e..8bed8d4cb3 100644 --- a/core/tst/software/aws/toolkits/core/lambda/LambdaSampleEventProviderTest.kt +++ b/core/tst/software/aws/toolkits/core/lambda/LambdaSampleEventProviderTest.kt @@ -37,20 +37,22 @@ class LambdaSampleEventProviderTest { second.json - """.trimIndent() + """.trimIndent() ) - val firstContent = """ + val firstContent = + """ { "hello": "world" } - """.trimIndent() + """.trimIndent() firstFile.writeText(firstContent) - val secondContent = """ + val secondContent = + """ ["hello"] - """.trimIndent() + """.trimIndent() secondFile.writeText(secondContent) @@ -86,7 +88,7 @@ class LambdaSampleEventProviderTest { first.json - """.trimIndent() + """.trimIndent() ) val resourceResolver = mock { diff --git a/core/tst/software/aws/toolkits/core/rules/EnvironmentVariableHelper.kt b/core/tst/software/aws/toolkits/core/rules/EnvironmentVariableHelper.kt index a339a2674c..f1fd7b5b85 100644 --- a/core/tst/software/aws/toolkits/core/rules/EnvironmentVariableHelper.kt +++ b/core/tst/software/aws/toolkits/core/rules/EnvironmentVariableHelper.kt @@ -36,9 +36,11 @@ class EnvironmentVariableHelper : ExternalResource() { private fun getField(processEnvironment: Class<*>, obj: Any?, fieldName: String): MutableMap? = try { val declaredField = processEnvironment.getDeclaredField(fieldName) - AccessController.doPrivileged(PrivilegedAction { - declaredField.isAccessible = true - }) + AccessController.doPrivileged( + PrivilegedAction { + declaredField.isAccessible = true + } + ) @Suppress("UNCHECKED_CAST") declaredField.get(obj) as MutableMap } catch (_: NoSuchFieldException) { diff --git a/gradle.properties b/gradle.properties index 57951dfeae..a35320ca24 100644 --- a/gradle.properties +++ b/gradle.properties @@ -14,7 +14,7 @@ kotlinVersion=1.3.70 awsSdkVersion=2.13.74 coroutinesVersion=1.3.3 ideaPluginVersion=0.4.20 -ktlintVersion=0.36.0 +ktlintVersion=0.38.1 jacksonVersion=2.9.8 telemetryVersion=0.0.43 diff --git a/jetbrains-core/it/software/aws/toolkits/jetbrains/services/clouddebug/java/JavaDebugEndToEndTest.kt b/jetbrains-core/it/software/aws/toolkits/jetbrains/services/clouddebug/java/JavaDebugEndToEndTest.kt index 630d5794fd..22b2af9ced 100644 --- a/jetbrains-core/it/software/aws/toolkits/jetbrains/services/clouddebug/java/JavaDebugEndToEndTest.kt +++ b/jetbrains-core/it/software/aws/toolkits/jetbrains/services/clouddebug/java/JavaDebugEndToEndTest.kt @@ -57,7 +57,11 @@ class JavaDebugEndToEndTest : CloudDebugTestCase("CloudDebugTestECSClusterTaskDe } val future = CompletableFuture() - ExternalSystemUtil.runTask(buildSettings, DefaultRunExecutor.EXECUTOR_ID, projectRule.project, GradleConstants.SYSTEM_ID, + ExternalSystemUtil.runTask( + buildSettings, + DefaultRunExecutor.EXECUTOR_ID, + projectRule.project, + GradleConstants.SYSTEM_ID, object : TaskCallback { override fun onSuccess() { future.complete(null) @@ -66,7 +70,10 @@ class JavaDebugEndToEndTest : CloudDebugTestCase("CloudDebugTestECSClusterTaskDe override fun onFailure() { future.completeExceptionally(RuntimeException("Jar task failed")) } - }, ProgressExecutionMode.IN_BACKGROUND_ASYNC, false) + }, + ProgressExecutionMode.IN_BACKGROUND_ASYNC, + false + ) future.join() // set breakpoint @@ -83,16 +90,22 @@ class JavaDebugEndToEndTest : CloudDebugTestCase("CloudDebugTestECSClusterTaskDe beforeRunTasks = mutableListOf(Mockito.mock(BeforeRunTask::class.java)) clusterArn(service.clusterArn()) // TODO: remove this once we fix the UX around which service is debugged - serviceArn(service.serviceArn().let { - // replace service name with instrumented service name - val instrumentedServiceName = "cloud-debug-${EcsUtils.serviceArnToName(service.serviceArn())}" - it.replace(EcsUtils.serviceArnToName(it), instrumentedServiceName) - }) - containerOptions(mapOf("ContainerName" to ContainerOptions().apply { - platform = CloudDebuggingPlatform.JVM - startCommand = "java -cp /main.jar Main" - artifactMappings = listOf(ArtifactMapping(jarFile.toString(), "/main.jar")) - })) + serviceArn( + service.serviceArn().let { + // replace service name with instrumented service name + val instrumentedServiceName = "cloud-debug-${EcsUtils.serviceArnToName(service.serviceArn())}" + it.replace(EcsUtils.serviceArnToName(it), instrumentedServiceName) + } + ) + containerOptions( + mapOf( + "ContainerName" to ContainerOptions().apply { + platform = CloudDebuggingPlatform.JVM + startCommand = "java -cp /main.jar Main" + artifactMappings = listOf(ArtifactMapping(jarFile.toString(), "/main.jar")) + } + ) + ) } runUnderRealCredentials(projectRule.project) { diff --git a/jetbrains-core/it/software/aws/toolkits/jetbrains/services/clouddebug/python/PythonDebugEndToEndTest.kt b/jetbrains-core/it/software/aws/toolkits/jetbrains/services/clouddebug/python/PythonDebugEndToEndTest.kt index 5af53d9f48..0e1f27e6e6 100644 --- a/jetbrains-core/it/software/aws/toolkits/jetbrains/services/clouddebug/python/PythonDebugEndToEndTest.kt +++ b/jetbrains-core/it/software/aws/toolkits/jetbrains/services/clouddebug/python/PythonDebugEndToEndTest.kt @@ -36,12 +36,14 @@ class PythonDebugEndToEndTest : CloudDebugTestCase("CloudDebugTestECSClusterTask @Test fun testEndToEnd() { // setup project workspace - val testScript = addPythonFile("test.py", + val testScript = addPythonFile( + "test.py", """ import hello import folderNoTrailingSlash.hello import folderTrailingSlash.hello - """.trimIndent()) + """.trimIndent() + ) val file = addPythonFile("hello.py") projectRule.addBreakpoint() @@ -68,21 +70,27 @@ class PythonDebugEndToEndTest : CloudDebugTestCase("CloudDebugTestECSClusterTask ).apply { clusterArn(service.clusterArn()) // TODO: remove this once we fix the UX around which service is debugged - serviceArn(service.serviceArn().let { - // replace service name with instrumented service name - val instrumentedServiceName = "cloud-debug-${EcsUtils.serviceArnToName(service.serviceArn())}" - it.replace(EcsUtils.serviceArnToName(it), instrumentedServiceName) - }) - containerOptions(mapOf("ContainerName" to ContainerOptions().apply { - platform = CloudDebuggingPlatform.PYTHON - startCommand = "python /${testScript.fileName}" - artifactMappings = listOf( - ArtifactMapping(testScript.toString(), "/test.py"), - ArtifactMapping(file.toString(), "/hello.py"), - ArtifactMapping(folderNoTrailingSlash.parent.toString().trimEnd('/'), "/"), - ArtifactMapping(folderTrailingSlash.parent.toString().trimEnd('/') + '/', "/folderTrailingSlash") + serviceArn( + service.serviceArn().let { + // replace service name with instrumented service name + val instrumentedServiceName = "cloud-debug-${EcsUtils.serviceArnToName(service.serviceArn())}" + it.replace(EcsUtils.serviceArnToName(it), instrumentedServiceName) + } + ) + containerOptions( + mapOf( + "ContainerName" to ContainerOptions().apply { + platform = CloudDebuggingPlatform.PYTHON + startCommand = "python /${testScript.fileName}" + artifactMappings = listOf( + ArtifactMapping(testScript.toString(), "/test.py"), + ArtifactMapping(file.toString(), "/hello.py"), + ArtifactMapping(folderNoTrailingSlash.parent.toString().trimEnd('/'), "/"), + ArtifactMapping(folderTrailingSlash.parent.toString().trimEnd('/') + '/', "/folderTrailingSlash") + ) + } ) - })) + ) } runUnderRealCredentials(projectRule.project) { diff --git a/jetbrains-core/it/software/aws/toolkits/jetbrains/services/lambda/deploy/SamDeployTest.kt b/jetbrains-core/it/software/aws/toolkits/jetbrains/services/lambda/deploy/SamDeployTest.kt index 83f2fb3498..0be4f5b4b4 100644 --- a/jetbrains-core/it/software/aws/toolkits/jetbrains/services/lambda/deploy/SamDeployTest.kt +++ b/jetbrains-core/it/software/aws/toolkits/jetbrains/services/lambda/deploy/SamDeployTest.kt @@ -135,7 +135,7 @@ class SamDeployTest { """ def lambda_handler(event, context): return "Hello world" - """.trimIndent() + """.trimIndent() ) projectRule.fixture.addFileToProject( diff --git a/jetbrains-core/it/software/aws/toolkits/jetbrains/services/lambda/java/JavaLocalLambdaRunConfigurationIntegrationTest.kt b/jetbrains-core/it/software/aws/toolkits/jetbrains/services/lambda/java/JavaLocalLambdaRunConfigurationIntegrationTest.kt index 33adc1aeaf..92c5f4653c 100644 --- a/jetbrains-core/it/software/aws/toolkits/jetbrains/services/lambda/java/JavaLocalLambdaRunConfigurationIntegrationTest.kt +++ b/jetbrains-core/it/software/aws/toolkits/jetbrains/services/lambda/java/JavaLocalLambdaRunConfigurationIntegrationTest.kt @@ -113,7 +113,8 @@ class JavaLocalLambdaRunConfigurationIntegrationTest(private val runtime: Runtim @Test fun samIsExecutedWhenRunWithATemplateServerless() { val templateFile = projectRule.fixture.addFileToProject( - "template.yaml", """ + "template.yaml", + """ Resources: SomeFunction: Type: AWS::Serverless::Function @@ -122,7 +123,7 @@ class JavaLocalLambdaRunConfigurationIntegrationTest(private val runtime: Runtim CodeUri: main Runtime: $runtime Timeout: 900 - """.trimIndent() + """.trimIndent() ) val runConfiguration = createTemplateRunConfiguration( @@ -144,7 +145,8 @@ class JavaLocalLambdaRunConfigurationIntegrationTest(private val runtime: Runtim @Test fun samIsExecutedWhenRunWithATemplateLambda() { val templateFile = projectRule.fixture.addFileToProject( - "template.yaml", """ + "template.yaml", + """ Resources: SomeFunction: Type: AWS::Lambda::Function @@ -153,7 +155,7 @@ class JavaLocalLambdaRunConfigurationIntegrationTest(private val runtime: Runtim Code: main Runtime: $runtime Timeout: 900 - """.trimIndent() + """.trimIndent() ) val runConfiguration = createTemplateRunConfiguration( diff --git a/jetbrains-core/it/software/aws/toolkits/jetbrains/utils/JavaTestUtils.kt b/jetbrains-core/it/software/aws/toolkits/jetbrains/utils/JavaTestUtils.kt index 906615f3d6..83ed84dcb6 100644 --- a/jetbrains-core/it/software/aws/toolkits/jetbrains/utils/JavaTestUtils.kt +++ b/jetbrains-core/it/software/aws/toolkits/jetbrains/utils/JavaTestUtils.kt @@ -87,7 +87,7 @@ fun HeavyJavaCodeInsightTestFixtureRule.setUpGradleProject(compatibility: String compile 'com.amazonaws:aws-lambda-java-core:1.2.0' testImplementation 'junit:junit:4.12' } - """.trimIndent() + """.trimIndent() ).virtualFile // Use our project's own Gradle version @@ -102,7 +102,7 @@ fun HeavyJavaCodeInsightTestFixtureRule.setUpGradleProject(compatibility: String return input.toUpperCase(); } } - """.trimIndent() + """.trimIndent() ) val jdkName = "Gradle JDK" @@ -116,7 +116,8 @@ fun HeavyJavaCodeInsightTestFixtureRule.setUpGradleProject(compatibility: String super.onProjectsLinked(settings) settings.first().gradleJvm = jdkName } - }) + } + ) val gradleProjectSettings = GradleProjectSettings().apply { withQualifiedModuleNames() @@ -252,7 +253,7 @@ internal fun HeavyJavaCodeInsightTestFixtureRule.setUpMavenProject(): PsiClass {
- """.trimIndent() + """.trimIndent() ).virtualFile val lambdaClass = fixture.addClass( @@ -264,7 +265,7 @@ internal fun HeavyJavaCodeInsightTestFixtureRule.setUpMavenProject(): PsiClass { return input.toUpperCase(); } } - """.trimIndent() + """.trimIndent() ) val projectsManager = MavenProjectsManager.getInstance(project) diff --git a/jetbrains-core/it/software/aws/toolkits/jetbrains/utils/TestUtils.kt b/jetbrains-core/it/software/aws/toolkits/jetbrains/utils/TestUtils.kt index cb84220d45..fcf11fc0d2 100644 --- a/jetbrains-core/it/software/aws/toolkits/jetbrains/utils/TestUtils.kt +++ b/jetbrains-core/it/software/aws/toolkits/jetbrains/utils/TestUtils.kt @@ -35,22 +35,24 @@ fun executeRunConfiguration( val executionEnvironment = ExecutionEnvironmentBuilder.create(executor, runConfiguration).build() try { executionEnvironment.runner.execute(executionEnvironment) { - it.processHandler?.addProcessListener(object : OutputListener() { - override fun onTextAvailable(event: ProcessEvent, outputType: Key<*>) { - // Ansi codes throw off the default logic, so remap it to check the base type - val processOutputType = outputType as? ProcessOutputType - val baseType = processOutputType?.baseOutputType ?: outputType + it.processHandler?.addProcessListener( + object : OutputListener() { + override fun onTextAvailable(event: ProcessEvent, outputType: Key<*>) { + // Ansi codes throw off the default logic, so remap it to check the base type + val processOutputType = outputType as? ProcessOutputType + val baseType = processOutputType?.baseOutputType ?: outputType - super.onTextAvailable(event, baseType) + super.onTextAvailable(event, baseType) - println("[${if (baseType == ProcessOutputTypes.STDOUT) "stdout" else "stderr"}]: ${event.text}") - } + println("[${if (baseType == ProcessOutputTypes.STDOUT) "stdout" else "stderr"}]: ${event.text}") + } - override fun processTerminated(event: ProcessEvent) { - super.processTerminated(event) - executionFuture.complete(this.output) + override fun processTerminated(event: ProcessEvent) { + super.processTerminated(event) + executionFuture.complete(this.output) + } } - }) + ) } } catch (e: Exception) { executionFuture.completeExceptionally(e) @@ -64,27 +66,32 @@ fun checkBreakPointHit(project: Project, callback: () -> Unit = {}): Ref by lazy { EP_NAME.extensionList.associateBy { - it.id - } + it.id + } } init { diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/core/credentials/CredentialWriter.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/core/credentials/CredentialWriter.kt index b446d18f06..401784cffc 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/core/credentials/CredentialWriter.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/core/credentials/CredentialWriter.kt @@ -92,7 +92,8 @@ interface ConfigFileWriter { } object DefaultConfigFileWriter : ConfigFileWriter { - val TEMPLATE = """ + val TEMPLATE = + """ # Amazon Web Services Config File used by AWS CLI, SDKs, and tools # This file was created by the AWS Toolkit for JetBrains plugin. # @@ -120,7 +121,7 @@ object DefaultConfigFileWriter : ConfigFileWriter { # [profile user1] # aws_access_key_id = [accessKey1] # aws_secret_access_key = [secretKey1] - """.trimIndent() + """.trimIndent() override fun createFile(file: File) { val parent = file.parentFile diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/core/credentials/DefaultAwsConnectionManager.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/core/credentials/DefaultAwsConnectionManager.kt index 6322ab1ab2..ee781e4633 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/core/credentials/DefaultAwsConnectionManager.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/core/credentials/DefaultAwsConnectionManager.kt @@ -22,7 +22,8 @@ data class ConnectionSettingsState( ) @State(name = "accountSettings", storages = [Storage("aws.xml")]) -class DefaultAwsConnectionManager(private val project: Project) : AwsConnectionManager(project), +class DefaultAwsConnectionManager(private val project: Project) : + AwsConnectionManager(project), PersistentStateComponent { override fun getState(): ConnectionSettingsState = ConnectionSettingsState( activeProfile = selectedCredentialIdentifier?.id, diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/core/credentials/profiles/ProfileCredentialProviderFactory.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/core/credentials/profiles/ProfileCredentialProviderFactory.kt index 9a119e3a14..fbe9bb5ee3 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/core/credentials/profiles/ProfileCredentialProviderFactory.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/core/credentials/profiles/ProfileCredentialProviderFactory.kt @@ -260,15 +260,17 @@ class ProfileCredentialProviderFactory : CredentialProviderFactory { val assumeRoleCredentialsProvider = StsAssumeRoleCredentialsProvider.builder() .stsClient(stsClient) - .refreshRequest(Supplier { - createAssumeRoleRequest( - profile.name(), - mfaSerial, - roleArn, - roleSessionName, - externalId - ) - }) + .refreshRequest( + Supplier { + createAssumeRoleRequest( + profile.name(), + mfaSerial, + roleArn, + roleSessionName, + externalId + ) + } + ) .build() // TODO: Do we still need this wrapper? @@ -320,8 +322,12 @@ class ProfileCredentialProviderFactory : CredentialProviderFactory { return when { this.requiresMfa(profiles) -> ProfileCredentialsIdentifierMfa(name, defaultRegion) - this.requiresSso(profiles) -> ProfileCredentialsIdentifierSso(name, defaultRegion, - diskCache, this.requiredProperty(SSO_URL)) + this.requiresSso(profiles) -> ProfileCredentialsIdentifierSso( + name, + defaultRegion, + diskCache, + this.requiredProperty(SSO_URL) + ) else -> ProfileCredentialsIdentifier(name, defaultRegion) } } diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/core/explorer/ExplorerToolWindow.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/core/explorer/ExplorerToolWindow.kt index 2a336be8d6..1a93d0fc3e 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/core/explorer/ExplorerToolWindow.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/core/explorer/ExplorerToolWindow.kt @@ -132,12 +132,14 @@ class ExplorerToolWindow(project: Project) : SimpleToolWindowPanel(true, true), private fun createActionLabel(action: AnAction): HyperlinkLabel { val label = HyperlinkLabel(action.templateText ?: "BUG: $action lacks a text description") - label.addHyperlinkListener(object : HyperlinkAdapter() { - override fun hyperlinkActivated(e: HyperlinkEvent) { - val event = AnActionEvent.createFromAnAction(action, e.inputEvent, ActionPlaces.UNKNOWN, DataManager.getInstance().getDataContext(label)) - action.actionPerformed(event) + label.addHyperlinkListener( + object : HyperlinkAdapter() { + override fun hyperlinkActivated(e: HyperlinkEvent) { + val event = AnActionEvent.createFromAnAction(action, e.inputEvent, ActionPlaces.UNKNOWN, DataManager.getInstance().getDataContext(label)) + action.actionPerformed(event) + } } - }) + ) return label } @@ -196,34 +198,36 @@ class ExplorerToolWindow(project: Project) : SimpleToolWindowPanel(true, true), } }.installOn(awsTree) - awsTree.addMouseListener(object : PopupHandler() { - override fun invokePopup(comp: Component?, x: Int, y: Int) { - // Build a right click menu based on the selected first node - // All nodes must be the same type (e.g. all S3 buckets, or a service node) - val explorerNode = getSelectedNodesSameType>()?.get(0) ?: return - val actionGroupName = (explorerNode as? ResourceActionNode)?.actionGroupName() + awsTree.addMouseListener( + object : PopupHandler() { + override fun invokePopup(comp: Component?, x: Int, y: Int) { + // Build a right click menu based on the selected first node + // All nodes must be the same type (e.g. all S3 buckets, or a service node) + val explorerNode = getSelectedNodesSameType>()?.get(0) ?: return + val actionGroupName = (explorerNode as? ResourceActionNode)?.actionGroupName() - val totalActions = mutableListOf() + val totalActions = mutableListOf() - (actionGroupName?.let { actionManager.getAction(it) } as? ActionGroup)?.let { totalActions.addAll(it.getChildren(null)) } + (actionGroupName?.let { actionManager.getAction(it) } as? ActionGroup)?.let { totalActions.addAll(it.getChildren(null)) } - if (explorerNode is AwsExplorerResourceNode<*>) { - totalActions.add(CopyArnAction()) - } + if (explorerNode is AwsExplorerResourceNode<*>) { + totalActions.add(CopyArnAction()) + } - totalActions.find { it is DeleteResourceAction<*> }?.let { - totalActions.remove(it) - totalActions.add(Separator.create()) - totalActions.add(it) - } + totalActions.find { it is DeleteResourceAction<*> }?.let { + totalActions.remove(it) + totalActions.add(Separator.create()) + totalActions.add(it) + } - val actionGroup = DefaultActionGroup(totalActions) - if (actionGroup.childrenCount > 0) { - val popupMenu = actionManager.createActionPopupMenu("ExplorerToolWindow", actionGroup) - popupMenu.component.show(comp, x, y) + val actionGroup = DefaultActionGroup(totalActions) + if (actionGroup.childrenCount > 0) { + val popupMenu = actionManager.createActionPopupMenu("ExplorerToolWindow", actionGroup) + popupMenu.component.show(comp, x, y) + } } } - }) + ) return awsTree } diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/core/explorer/actions/DeleteResourceAction.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/core/explorer/actions/DeleteResourceAction.kt index ce43a3dd77..6d3438e020 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/core/explorer/actions/DeleteResourceAction.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/core/explorer/actions/DeleteResourceAction.kt @@ -27,7 +27,8 @@ abstract class DeleteResourceAction>(text: Str val resourceType = selected.resourceType() val resourceName = selected.displayName() - val response = Messages.showInputDialog(selected.project, + val response = Messages.showInputDialog( + selected.project, message("delete_resource.message", resourceType, resourceName), message("delete_resource.title", resourceType, resourceName), Messages.getWarningIcon(), diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/core/notification/NoticeManager.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/core/notification/NoticeManager.kt index bbbbae6c0e..b2d10b22b0 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/core/notification/NoticeManager.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/core/notification/NoticeManager.kt @@ -28,7 +28,8 @@ interface NoticeManager { internal const val NOTICE_NOTIFICATION_GROUP_ID = "AWS Toolkit Notices" @State(name = "notices", storages = [Storage("aws.xml")]) -class DefaultNoticeManager : PersistentStateComponent, +class DefaultNoticeManager : + PersistentStateComponent, NoticeManager { private val internalState = mutableMapOf() private val notificationGroup = NotificationGroup(NOTICE_NOTIFICATION_GROUP_ID, NotificationDisplayType.STICKY_BALLOON, true) diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/clouddebug/CloudDebugVersionCache.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/clouddebug/CloudDebugVersionCache.kt index d82e01dedf..f70a4905b0 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/clouddebug/CloudDebugVersionCache.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/clouddebug/CloudDebugVersionCache.kt @@ -30,10 +30,13 @@ object CloudDebugVersionCache : FileInfoCache() { throw IllegalStateException(message("executableCommon.empty_info", executableName)) } return SemVer.parseFromText(output) - ?: throw IllegalStateException(message("executableCommon.version_parse_error", - executableName, - output - )) + ?: throw IllegalStateException( + message( + "executableCommon.version_parse_error", + executableName, + output + ) + ) } } } diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/clouddebug/CloudDebuggingExplorerTreeStructureProvider.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/clouddebug/CloudDebuggingExplorerTreeStructureProvider.kt index 86ee870750..9502c84e99 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/clouddebug/CloudDebuggingExplorerTreeStructureProvider.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/clouddebug/CloudDebuggingExplorerTreeStructureProvider.kt @@ -17,23 +17,26 @@ class CloudDebuggingExplorerTreeStructureProvider : AwsExplorerTreeStructureProv settings: ViewSettings? ): MutableCollection> = when (parent) { - is EcsClusterNode -> children - .sortedWith(Comparator { x, y -> - val service1 = (x as? EcsServiceNode)?.resourceArn()?.toLowerCase() ?: "" - val service2 = (y as? EcsServiceNode)?.resourceArn()?.toLowerCase() ?: "" - val value = EcsUtils.originalServiceName(service1).compareTo(EcsUtils.originalServiceName(service2)) - if (value != 0) { - value - } else { - // Always put the instrumented service first - if (EcsUtils.isInstrumented(service1)) { - -1 - } else { - 1 + is EcsClusterNode -> + children + .sortedWith( + Comparator { x, y -> + val service1 = (x as? EcsServiceNode)?.resourceArn()?.toLowerCase() ?: "" + val service2 = (y as? EcsServiceNode)?.resourceArn()?.toLowerCase() ?: "" + val value = EcsUtils.originalServiceName(service1).compareTo(EcsUtils.originalServiceName(service2)) + if (value != 0) { + value + } else { + // Always put the instrumented service first + if (EcsUtils.isInstrumented(service1)) { + -1 + } else { + 1 + } + } } - } - }) - .toMutableList() + ) + .toMutableList() else -> children } } diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/clouddebug/actions/PseCliAction.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/clouddebug/actions/PseCliAction.kt index bea9249642..c8e653daf5 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/clouddebug/actions/PseCliAction.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/clouddebug/actions/PseCliAction.kt @@ -119,37 +119,39 @@ abstract class PseCliAction(val project: Project, val actionName: String, privat val handler = CapturingProcessHandler(cmd) - handler.addProcessListener(object : ProcessAdapter() { - val cliOutput = AtomicReference() - override fun onTextAvailable(event: ProcessEvent, outputType: Key<*>) { - if (outputType == ProcessOutputTypes.STDOUT) { - cliOutput.set(event.text) - } else { - val (text, level) = event.text.asLogEvent() - @Suppress("DEPRECATION") - messageEmitter.emitMessage(text, level == Level.ERROR) - indicator.text2 = text - // output to the log for diagnostic and integrations tests - LOG.debug { event.text.trim() } + handler.addProcessListener( + object : ProcessAdapter() { + val cliOutput = AtomicReference() + override fun onTextAvailable(event: ProcessEvent, outputType: Key<*>) { + if (outputType == ProcessOutputTypes.STDOUT) { + cliOutput.set(event.text) + } else { + val (text, level) = event.text.asLogEvent() + @Suppress("DEPRECATION") + messageEmitter.emitMessage(text, level == Level.ERROR) + indicator.text2 = text + // output to the log for diagnostic and integrations tests + LOG.debug { event.text.trim() } + } } - } - override fun processTerminated(event: ProcessEvent) { - val result = if (event.exitCode == 0) { - SuccessResultImpl() - } else { - // TODO: really need to refactor this and steps - it's getting a bit crazy - messageEmitter.emitMessage("Error details:\n", true) - cliOutput.get()?.let { CliOutputParser.parseErrorOutput(it) }?.errors?.forEach { - messageEmitter.emitMessage("\t- $it\n", true) - // output to the log for diagnostic and integrations tests - LOG.debug { "Error details:\n $it" } + override fun processTerminated(event: ProcessEvent) { + val result = if (event.exitCode == 0) { + SuccessResultImpl() + } else { + // TODO: really need to refactor this and steps - it's getting a bit crazy + messageEmitter.emitMessage("Error details:\n", true) + cliOutput.get()?.let { CliOutputParser.parseErrorOutput(it) }?.errors?.forEach { + messageEmitter.emitMessage("\t- $it\n", true) + // output to the log for diagnostic and integrations tests + LOG.debug { "Error details:\n $it" } + } + FailureResultImpl() } - FailureResultImpl() + buildViewManager.onEvent(actionName, FinishBuildEventImpl(actionName, null, System.currentTimeMillis(), "", result)) } - buildViewManager.onEvent(actionName, FinishBuildEventImpl(actionName, null, System.currentTimeMillis(), "", result)) } - }) + ) val exit = handler.runProcess().exitCode if (exit == 0) { diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/clouddebug/actions/StartRemoteShellAction.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/clouddebug/actions/StartRemoteShellAction.kt index 35cda01037..59ba7d604b 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/clouddebug/actions/StartRemoteShellAction.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/clouddebug/actions/StartRemoteShellAction.kt @@ -21,7 +21,6 @@ import software.aws.toolkits.jetbrains.core.credentials.AwsConnectionManager import software.aws.toolkits.jetbrains.core.credentials.activeCredentialProvider import software.aws.toolkits.jetbrains.core.credentials.activeRegion import software.aws.toolkits.jetbrains.core.credentials.toEnvironmentVariables -import software.aws.toolkits.jetbrains.services.clouddebug.CloudDebugExecutable import software.aws.toolkits.jetbrains.core.executables.ExecutableInstance import software.aws.toolkits.jetbrains.core.executables.ExecutableManager import software.aws.toolkits.jetbrains.core.executables.getExecutable @@ -29,6 +28,7 @@ import software.aws.toolkits.jetbrains.core.plugins.pluginIsInstalledAndEnabled import software.aws.toolkits.jetbrains.services.clouddebug.CliOutputParser import software.aws.toolkits.jetbrains.services.clouddebug.CloudDebugConstants import software.aws.toolkits.jetbrains.services.clouddebug.CloudDebugConstants.INSTRUMENTED_STATUS +import software.aws.toolkits.jetbrains.services.clouddebug.CloudDebugExecutable import software.aws.toolkits.jetbrains.services.clouddebug.InstrumentResponse import software.aws.toolkits.jetbrains.services.clouddebug.resources.CloudDebuggingResources import software.aws.toolkits.jetbrains.services.ecs.ContainerDetails @@ -69,77 +69,79 @@ class StartRemoteShellAction(private val project: Project, private val container val startTime = Instant.now() ExecutableManager.getInstance().getExecutable().thenAccept { cloudDebugExecutable -> - ProgressManager.getInstance().run(object : Task.Backgroundable(project, title, false) { - override fun run(indicator: ProgressIndicator) { - if (cloudDebugExecutable !is ExecutableInstance.Executable) { - val error = (cloudDebugExecutable as? ExecutableInstance.BadExecutable)?.validationError ?: message("general.unknown_error") - - runInEdt { - notifyError(message("cloud_debug.step.clouddebug.install.fail", error)) + ProgressManager.getInstance().run( + object : Task.Backgroundable(project, title, false) { + override fun run(indicator: ProgressIndicator) { + if (cloudDebugExecutable !is ExecutableInstance.Executable) { + val error = (cloudDebugExecutable as? ExecutableInstance.BadExecutable)?.validationError ?: message("general.unknown_error") + + runInEdt { + notifyError(message("cloud_debug.step.clouddebug.install.fail", error)) + } + throw Exception("cloud debug executable not found") } - throw Exception("cloud debug executable not found") - } - val connectionManager = AwsConnectionManager.getInstance(project) - val credentials = connectionManager.activeCredentialProvider - val region = connectionManager.activeRegion + val connectionManager = AwsConnectionManager.getInstance(project) + val credentials = connectionManager.activeCredentialProvider + val region = connectionManager.activeRegion + + val description = CloudDebuggingResources.describeInstrumentedResource(credentials, region, cluster, service) + if (description == null || description.status != INSTRUMENTED_STATUS || description.taskRole.isEmpty()) { + runInEdt { + notifyError(message("cloud_debug.execution.failed.not_set_up")) + } + throw RuntimeException("Resource somehow became de-instrumented?") + } + val role = description.taskRole + + val target = try { + runInstrument(project, cloudDebugExecutable, cluster, service, role).target + } catch (e: Exception) { + e.notifyError(title) + null + } ?: return + + val cmdLine = buildBaseCmdLine(project, cloudDebugExecutable) + .withParameters("exec") + .withParameters("--target") + .withParameters(target) + /* TODO remove this when the cli conforms to the contract */ + .withParameters("--selector") + .withParameters(containerName) + .withParameters("--tty") + .withParameters("/aws/cloud-debug/common/busybox", "sh", "-i") + + val cmdList = cmdLine.getCommandLineList(null).toTypedArray() + val env = cmdLine.effectiveEnvironment + val ptyProcess = PtyProcess.exec(cmdList, env, null) + val process = CloudTerminalProcess(ptyProcess.outputStream, ptyProcess.inputStream) + val runner = CloudTerminalRunner(project, containerName, process) - val description = CloudDebuggingResources.describeInstrumentedResource(credentials, region, cluster, service) - if (description == null || description.status != INSTRUMENTED_STATUS || description.taskRole.isEmpty()) { runInEdt { - notifyError(message("cloud_debug.execution.failed.not_set_up")) + TerminalView.getInstance(project).createNewSession(runner, TerminalTabState().also { it.myTabName = containerName }) } - throw RuntimeException("Resource somehow became de-instrumented?") } - val role = description.taskRole - - val target = try { - runInstrument(project, cloudDebugExecutable, cluster, service, role).target - } catch (e: Exception) { - e.notifyError(title) - null - } ?: return - - val cmdLine = buildBaseCmdLine(project, cloudDebugExecutable) - .withParameters("exec") - .withParameters("--target") - .withParameters(target) - /* TODO remove this when the cli conforms to the contract */ - .withParameters("--selector") - .withParameters(containerName) - .withParameters("--tty") - .withParameters("/aws/cloud-debug/common/busybox", "sh", "-i") - - val cmdList = cmdLine.getCommandLineList(null).toTypedArray() - val env = cmdLine.effectiveEnvironment - val ptyProcess = PtyProcess.exec(cmdList, env, null) - val process = CloudTerminalProcess(ptyProcess.outputStream, ptyProcess.inputStream) - val runner = CloudTerminalRunner(project, containerName, process) - - runInEdt { - TerminalView.getInstance(project).createNewSession(runner, TerminalTabState().also { it.myTabName = containerName }) - } - } - override fun onSuccess() { - recordTelemetry(Result.Succeeded) - } + override fun onSuccess() { + recordTelemetry(Result.Succeeded) + } - override fun onThrowable(error: Throwable) { - recordTelemetry(Result.Failed) - } + override fun onThrowable(error: Throwable) { + recordTelemetry(Result.Failed) + } - private fun recordTelemetry(result: Result) { - ClouddebugTelemetry.shell( - project, - // TODO clean up with executable manager changes - version = (cloudDebugExecutable as? ExecutableInstance.Executable)?.version, - result = result, - value = Duration.between(startTime, Instant.now()).toMillis().toDouble(), - createTime = startTime - ) + private fun recordTelemetry(result: Result) { + ClouddebugTelemetry.shell( + project, + // TODO clean up with executable manager changes + version = (cloudDebugExecutable as? ExecutableInstance.Executable)?.version, + result = result, + value = Duration.between(startTime, Instant.now()).toMillis().toDouble(), + createTime = startTime + ) + } } - }) + ) } } diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/clouddebug/execution/CloudDebugRunState.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/clouddebug/execution/CloudDebugRunState.kt index 56ed9d8860..bb0ae5b9a7 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/clouddebug/execution/CloudDebugRunState.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/clouddebug/execution/CloudDebugRunState.kt @@ -42,11 +42,16 @@ class CloudDebugRunState( System.currentTimeMillis() ) - val buildView = BuildView(project, descriptor, null, object : ViewManager { - override fun isConsoleEnabledByDefault() = false + val buildView = BuildView( + project, + descriptor, + null, + object : ViewManager { + override fun isConsoleEnabledByDefault() = false - override fun isBuildContentView() = true - }) + override fun isBuildContentView() = true + } + ) val rootStep = RootStep(settings, environment) val context = Context(project) diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/clouddebug/execution/steps/AttachDebuggers.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/clouddebug/execution/steps/AttachDebuggers.kt index 53e682ec03..ebb7ad3670 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/clouddebug/execution/steps/AttachDebuggers.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/clouddebug/execution/steps/AttachDebuggers.kt @@ -62,7 +62,8 @@ class AttachDebugger( val debuggerAttacher = DebuggerSupport.debuggers()[containerOptions.platform] ?: throw IllegalStateException( message( - "cloud_debug.step.attach_debugger.unknown_platform", containerOptions.platform + "cloud_debug.step.attach_debugger.unknown_platform", + containerOptions.platform ) ) @@ -130,15 +131,17 @@ class AttachDebugger( ConsoleViewContentType.NORMAL_OUTPUT } - handler.addProcessListener(object : OutputListener() { - override fun onTextAvailable(event: ProcessEvent, outputType: Key<*>) { - // Skip system messages - if (outputType == ProcessOutputTypes.SYSTEM) { - return + handler.addProcessListener( + object : OutputListener() { + override fun onTextAvailable(event: ProcessEvent, outputType: Key<*>) { + // Skip system messages + if (outputType == ProcessOutputTypes.SYSTEM) { + return + } + console.print(event.text, viewType) } - console.print(event.text, viewType) } - }) + ) handler.startNotify() diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/clouddebug/execution/steps/PortForwarding.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/clouddebug/execution/steps/PortForwarding.kt index 1dafa14e15..5c8e9628f4 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/clouddebug/execution/steps/PortForwarding.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/clouddebug/execution/steps/PortForwarding.kt @@ -122,6 +122,6 @@ class PortForwarder( workflowtoken = context.workflowToken, value = Duration.between(startTime, Instant.now()).toMillis().toDouble(), createTime = startTime - ) + ) } } diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/clouddebug/python/PythonDebuggerSupport.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/clouddebug/python/PythonDebuggerSupport.kt index 97c249b41b..458202b2e1 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/clouddebug/python/PythonDebuggerSupport.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/clouddebug/python/PythonDebuggerSupport.kt @@ -27,7 +27,8 @@ class PythonDebuggerSupport : DebuggerSupport() { // python start command. This is a regex because we can have valid start commands like python3.7 or just python // The space after "python" is intentional: it makes statements like "python.sh" not match - private val pythonStartRegex = """^python((\d+\.\d+)|(\d+))?$""".toRegex() + private val pythonStartRegex = + """^python((\d+\.\d+)|(\d+))?$""".toRegex() override val debuggerPath = object : DebuggerPath { override fun getDebuggerPath(): String = PythonHelper.DEBUGGER.pythonPathEntry @@ -54,7 +55,8 @@ class PythonDebuggerSupport : DebuggerSupport() { runInEdt { try { val descriptor = manager.startSessionAndShowTab( - displayName, null, + displayName, + null, startDebugProcess(containerOptions, environment.project, ports.first()) ).runContentDescriptor future.complete(descriptor) @@ -85,7 +87,8 @@ class PythonDebuggerSupport : DebuggerSupport() { it.positionConverter = PathMapper.PositionConverter( PathMapper( convertArtifactMappingsToPathMappings( - containerOptions.artifactMappings, debuggerPath + containerOptions.artifactMappings, + debuggerPath ).map { pair -> PathMapping(pair.first, pair.second) } diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/clouddebug/resources/CloudDebuggingResources.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/clouddebug/resources/CloudDebuggingResources.kt index 49386536e2..a92338659d 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/clouddebug/resources/CloudDebuggingResources.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/clouddebug/resources/CloudDebuggingResources.kt @@ -98,11 +98,13 @@ object CloudDebuggingResources { val generalCommandLine = shutdownTask.getCommandLine().withParameters("shutdown") try { val handler = CapturingProcessHandler(generalCommandLine) - handler.addProcessListener(object : ProcessAdapter() { - override fun onTextAvailable(event: ProcessEvent, outputType: Key<*>) { - messageEmitter?.emitMessage(event.text, outputType == ProcessOutputTypes.STDERR) + handler.addProcessListener( + object : ProcessAdapter() { + override fun onTextAvailable(event: ProcessEvent, outputType: Key<*>) { + messageEmitter?.emitMessage(event.text, outputType == ProcessOutputTypes.STDERR) + } } - }) + ) handler.runProcess(TimeUnit.SECONDS.toMillis(5).toInt()) } catch (e: Exception) { LOG.warn(e) { "Unable to shutdown the local dispatcher!" } diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudformation/CloudFormationExplorerNodes.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudformation/CloudFormationExplorerNodes.kt index a6ad85ab1c..73e089375f 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudformation/CloudFormationExplorerNodes.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudformation/CloudFormationExplorerNodes.kt @@ -30,10 +30,10 @@ class CloudFormationStackNode( private val stackStatus: StackStatus, val stackId: String ) : AwsExplorerResourceNode( - project, - CloudFormationClient.SERVICE_NAME, - stackName, - AwsIcons.Resources.CLOUDFORMATION_STACK + project, + CloudFormationClient.SERVICE_NAME, + stackName, + AwsIcons.Resources.CLOUDFORMATION_STACK ) { override fun resourceType() = "stack" diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudformation/CloudFormationTemplateIndex.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudformation/CloudFormationTemplateIndex.kt index 6da996aae6..1b32e271bc 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudformation/CloudFormationTemplateIndex.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudformation/CloudFormationTemplateIndex.kt @@ -54,22 +54,24 @@ class CloudFormationTemplateIndex : FileBasedIndexExtension, FileContent> = DataIndexer { fileContent -> val indexedResources = mutableMapOf>() - fileContent.psiFile.acceptNode(object : PsiElementVisitor() { - override fun visitElement(element: PsiElement) { - super.visitElement(element) - // element is nullable in versions prior to 2020.1 FIX_WHEN_MIN_IS_201 - element?.run { - val parent = element.parent as? YAMLKeyValue ?: return - if (parent.value != this) return - - val resource = YamlCloudFormationTemplate.convertPsiToResource(parent) ?: return - val resourceType = resource.type() ?: return - IndexedResource.from(resource)?.let { - indexedResources.computeIfAbsent(resourceType) { mutableListOf() }.add(it) + fileContent.psiFile.acceptNode( + object : PsiElementVisitor() { + override fun visitElement(element: PsiElement) { + super.visitElement(element) + // element is nullable in versions prior to 2020.1 FIX_WHEN_MIN_IS_201 + element?.run { + val parent = element.parent as? YAMLKeyValue ?: return + if (parent.value != this) return + + val resource = YamlCloudFormationTemplate.convertPsiToResource(parent) ?: return + val resourceType = resource.type() ?: return + IndexedResource.from(resource)?.let { + indexedResources.computeIfAbsent(resourceType) { mutableListOf() }.add(it) + } } } } - }) + ) indexedResources } diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudformation/IndexedResources.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudformation/IndexedResources.kt index 9a5b9433e0..417abee418 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudformation/IndexedResources.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudformation/IndexedResources.kt @@ -15,20 +15,20 @@ import java.io.DataOutput open class IndexedResource protected constructor(val type: String, val indexedProperties: Map) { protected constructor(resource: Resource, indexProperties: List) : - this( - resource.type() ?: throw RuntimeException(message("cloudformation.template_index.missing_type")), - indexProperties - .asSequence() - .map { - it to try { - resource.getScalarProperty(it) - } catch (e: Exception) { - null - } + this( + resource.type() ?: throw RuntimeException(message("cloudformation.template_index.missing_type")), + indexProperties + .asSequence() + .map { + it to try { + resource.getScalarProperty(it) + } catch (e: Exception) { + null } - .mapNotNull { (key, value) -> value?.let { key to it } } - .toMap() - ) + } + .mapNotNull { (key, value) -> value?.let { key to it } } + .toMap() + ) fun save(dataOutput: DataOutput) { dataOutput.writeUTF(type) @@ -58,7 +58,7 @@ open class IndexedResource protected constructor(val type: String, val indexedPr } fun from(type: String, indexedProperties: Map) = - INDEXED_RESOURCE_MAPPINGS[type]?.first?.invoke(type, indexedProperties) ?: IndexedResource(type, indexedProperties) + INDEXED_RESOURCE_MAPPINGS[type]?.first?.invoke(type, indexedProperties) ?: IndexedResource(type, indexedProperties) fun from(resource: Resource): IndexedResource? = resource.type()?.let { INDEXED_RESOURCE_MAPPINGS[it]?.second?.invoke(resource) ?: IndexedResource(resource, listOf()) @@ -80,6 +80,6 @@ class IndexedFunction : IndexedResource { } internal val INDEXED_RESOURCE_MAPPINGS = mapOf) -> IndexedResource, (Resource) -> IndexedResource>>( - LAMBDA_FUNCTION_TYPE to Pair(::IndexedFunction, ::IndexedFunction), - SERVERLESS_FUNCTION_TYPE to Pair(::IndexedFunction, ::IndexedFunction) + LAMBDA_FUNCTION_TYPE to Pair(::IndexedFunction, ::IndexedFunction), + SERVERLESS_FUNCTION_TYPE to Pair(::IndexedFunction, ::IndexedFunction) ) diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudformation/stack/EventsFetcher.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudformation/stack/EventsFetcher.kt index 7a9eca0c84..aa866b456d 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudformation/stack/EventsFetcher.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudformation/stack/EventsFetcher.kt @@ -29,46 +29,46 @@ class EventsFetcher(private val stackName: String) { */ fun fetchEvents(client: CloudFormationClient, pageToSwitchTo: Page?): Pair, Set> { - assert(!SwingUtilities.isEventDispatchThread()) + assert(!SwingUtilities.isEventDispatchThread()) - val pageToFetch: String? = when (pageToSwitchTo) { - Page.NEXT -> nextPage - Page.PREVIOUS -> previousPages.lastOrNull() - else -> currentPage - } + val pageToFetch: String? = when (pageToSwitchTo) { + Page.NEXT -> nextPage + Page.PREVIOUS -> previousPages.lastOrNull() + else -> currentPage + } - val request = DescribeStackEventsRequest.builder().stackName(stackName).nextToken(pageToFetch).build() - val response = client.describeStackEvents(request) + val request = DescribeStackEventsRequest.builder().stackName(stackName).nextToken(pageToFetch).build() + val response = client.describeStackEvents(request) - when (pageToSwitchTo) { - Page.NEXT -> currentPage?.let { previousPages.add(it) } // Store current as prev - Page.PREVIOUS -> if (previousPages.isNotEmpty()) previousPages.removeAt(previousPages.size - 1) - } - nextPage = response.nextToken() - currentPage = pageToFetch + when (pageToSwitchTo) { + Page.NEXT -> currentPage?.let { previousPages.add(it) } // Store current as prev + Page.PREVIOUS -> if (previousPages.isNotEmpty()) previousPages.removeAt(previousPages.size - 1) + } + nextPage = response.nextToken() + currentPage = pageToFetch - if (pageToSwitchTo != null) { // page changed, last event is not valid - lastEventIdOfCurrentPage = null - } + if (pageToSwitchTo != null) { // page changed, last event is not valid + lastEventIdOfCurrentPage = null + } - val eventsUnprocessed = response.stackEvents() - val eventsProcessed = when (lastEventIdOfCurrentPage) { - null -> eventsUnprocessed - else -> eventsUnprocessed.takeWhile { it.id != lastEventIdOfCurrentPage } - } - eventsProcessed.firstOrNull()?.let { lastEventIdOfCurrentPage = it.id } + val eventsUnprocessed = response.stackEvents() + val eventsProcessed = when (lastEventIdOfCurrentPage) { + null -> eventsUnprocessed + else -> eventsUnprocessed.takeWhile { it.id != lastEventIdOfCurrentPage } + } + eventsProcessed.firstOrNull()?.let { lastEventIdOfCurrentPage = it.id } - val availablePages = mutableSetOf() + val availablePages = mutableSetOf() - if (currentPage != null) { // We only can go prev. if current page is not first (not null) - availablePages.add(Page.PREVIOUS) - } - if (nextPage != null) { - availablePages.add(Page.NEXT) - } + if (currentPage != null) { // We only can go prev. if current page is not first (not null) + availablePages.add(Page.PREVIOUS) + } + if (nextPage != null) { + availablePages.add(Page.NEXT) + } - return Pair(eventsProcessed, availablePages) - } + return Pair(eventsProcessed, availablePages) + } private val StackEvent.id: String get() = eventId() } diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudformation/stack/Stack.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudformation/stack/Stack.kt index 1a018fd8e5..14febc6ed1 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudformation/stack/Stack.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudformation/stack/Stack.kt @@ -79,47 +79,53 @@ private class StackUI(private val project: Project, private val stackName: Strin val mainPanel = OnePixelSplitter(false, TREE_TABLE_INITIAL_PROPORTION).apply { firstComponent = tree.component secondComponent = JBTabbedPane().apply { - this.add(message("cloudformation.stack.tab_labels.events"), JPanel(GridLayoutManager(2, 1)).apply { - add( - eventsTable.component, - GridConstraints( - 0, - 0, - 1, - 1, - 0, - GridConstraints.FILL_BOTH, - GridConstraints.SIZEPOLICY_CAN_GROW or GridConstraints.SIZEPOLICY_WANT_GROW or GridConstraints.SIZEPOLICY_CAN_SHRINK, - GridConstraints.SIZEPOLICY_CAN_GROW or GridConstraints.SIZEPOLICY_WANT_GROW or GridConstraints.SIZEPOLICY_CAN_SHRINK, - null, - null, - null + this.add( + message("cloudformation.stack.tab_labels.events"), + JPanel(GridLayoutManager(2, 1)).apply { + add( + eventsTable.component, + GridConstraints( + 0, + 0, + 1, + 1, + 0, + GridConstraints.FILL_BOTH, + GridConstraints.SIZEPOLICY_CAN_GROW or GridConstraints.SIZEPOLICY_WANT_GROW or GridConstraints.SIZEPOLICY_CAN_SHRINK, + GridConstraints.SIZEPOLICY_CAN_GROW or GridConstraints.SIZEPOLICY_WANT_GROW or GridConstraints.SIZEPOLICY_CAN_SHRINK, + null, + null, + null + ) ) - ) - add( - pageButtons.component, - GridConstraints( - 1, - 0, - 1, - 1, - 0, - GridConstraints.FILL_HORIZONTAL, - GridConstraints.SIZEPOLICY_CAN_GROW or GridConstraints.SIZEPOLICY_WANT_GROW or GridConstraints.SIZEPOLICY_CAN_SHRINK, - GridConstraints.SIZEPOLICY_CAN_GROW or GridConstraints.SIZEPOLICY_CAN_SHRINK, - null, - null, - null + add( + pageButtons.component, + GridConstraints( + 1, + 0, + 1, + 1, + 0, + GridConstraints.FILL_HORIZONTAL, + GridConstraints.SIZEPOLICY_CAN_GROW or GridConstraints.SIZEPOLICY_WANT_GROW or GridConstraints.SIZEPOLICY_CAN_SHRINK, + GridConstraints.SIZEPOLICY_CAN_GROW or GridConstraints.SIZEPOLICY_CAN_SHRINK, + null, + null, + null + ) ) - ) - tabComponentInsets = JBUI.emptyInsets() - border = JBUI.Borders.empty() - }) - - this.add(message("cloudformation.stack.tab_labels.outputs"), JPanel().apply { - layout = BoxLayout(this, BoxLayout.Y_AXIS) - add(outputsTable.component) - }) + tabComponentInsets = JBUI.emptyInsets() + border = JBUI.Borders.empty() + } + ) + + this.add( + message("cloudformation.stack.tab_labels.outputs"), + JPanel().apply { + layout = BoxLayout(this, BoxLayout.Y_AXIS) + add(outputsTable.component) + } + ) } } diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/CloudWatchLogWindow.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/CloudWatchLogWindow.kt index 18ad439a3a..68a3ae6392 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/CloudWatchLogWindow.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/CloudWatchLogWindow.kt @@ -69,7 +69,8 @@ class CloudWatchLogWindow(private val project: Project) : CoroutineScope by Appl } val title = if (previousEvent != null && duration != null) { message( - "cloudwatch.logs.filtered_log_stream_title", logStream, + "cloudwatch.logs.filtered_log_stream_title", + logStream, DateFormatUtil.getDateTimeFormat().format(previousEvent.timestamp - duration.toMillis()), DateFormatUtil.getDateTimeFormat().format(previousEvent.timestamp + duration.toMillis()) ) diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/editor/CloudWatchLogGroup.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/editor/CloudWatchLogGroup.kt index 7f7f01ef2c..9d2f62126b 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/editor/CloudWatchLogGroup.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/editor/CloudWatchLogGroup.kt @@ -97,12 +97,14 @@ class CloudWatchLogGroup( private fun addToolbar() { val actionGroup = DefaultActionGroup() - actionGroup.addAction(object : AnAction(message("general.refresh"), null, AllIcons.Actions.Refresh), DumbAware { - override fun actionPerformed(e: AnActionEvent) { - CloudwatchlogsTelemetry.refreshGroup(project) - refreshTable() + actionGroup.addAction( + object : AnAction(message("general.refresh"), null, AllIcons.Actions.Refresh), DumbAware { + override fun actionPerformed(e: AnActionEvent) { + CloudwatchlogsTelemetry.refreshGroup(project) + refreshTable() + } } - }) + ) tablePanel.toolbar = ActionManager.getInstance().createActionToolbar("CloudWatchLogStream", actionGroup, false).component } diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/editor/CloudWatchLogStream.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/editor/CloudWatchLogStream.kt index 4457b7e368..5992d60513 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/editor/CloudWatchLogStream.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/editor/CloudWatchLogStream.kt @@ -114,15 +114,20 @@ class CloudWatchLogStream( private fun addActionToolbar() { val actionGroup = DefaultActionGroup() - actionGroup.addAction(object : AnAction(message("general.refresh"), null, AllIcons.Actions.Refresh), DumbAware { - override fun actionPerformed(e: AnActionEvent) { - refreshTable() - CloudwatchlogsTelemetry.refreshStream(project) + actionGroup.addAction( + object : AnAction(message("general.refresh"), null, AllIcons.Actions.Refresh), DumbAware { + override fun actionPerformed(e: AnActionEvent) { + refreshTable() + CloudwatchlogsTelemetry.refreshStream(project) + } + }, + Constraints.FIRST + ) + actionGroup.add( + OpenCurrentInEditorAction(project, logStream) { + searchStreamTable?.logsTable?.listTableModel?.items ?: logStreamTable.logsTable.listTableModel.items } - }, Constraints.FIRST) - actionGroup.add(OpenCurrentInEditorAction(project, logStream) { - searchStreamTable?.logsTable?.listTableModel?.items ?: logStreamTable.logsTable.listTableModel.items - }) + ) actionGroup.add(TailLogsAction(project) { searchStreamTable?.channel ?: logStreamTable.channel }) actionGroup.add(WrapLogsAction(project) { searchStreamTable?.logsTable ?: logStreamTable.logsTable }) val toolbar = ActionManager.getInstance().createActionToolbar("CloudWatchLogStream", actionGroup, false) diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/editor/LocationCrumbs.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/editor/LocationCrumbs.kt index 801678c4c0..0326855162 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/editor/LocationCrumbs.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/editor/LocationCrumbs.kt @@ -50,17 +50,27 @@ class LocationCrumbs(project: Project, logGroup: String, logStream: String? = nu val crumbs = listOfNotNull( Crumb.Impl(null, project.activeCredentialProvider().displayName, null, listOf()), Crumb.Impl(null, project.activeRegion().displayName, null, listOf()), - Crumb.Impl(null, logGroup, null, object : AbstractAction(message("cloudwatch.logs.view_log_streams")), DumbAware { - override fun actionPerformed(e: ActionEvent?) { - CloudWatchLogWindow.getInstance(project)?.showLogGroup(logGroup) + Crumb.Impl( + null, + logGroup, + null, + object : AbstractAction(message("cloudwatch.logs.view_log_streams")), DumbAware { + override fun actionPerformed(e: ActionEvent?) { + CloudWatchLogWindow.getInstance(project)?.showLogGroup(logGroup) + } } - }), + ), logStream?.let { - Crumb.Impl(null, it, null, object : AbstractAction(message("cloudwatch.logs.view_log_stream")), DumbAware { - override fun actionPerformed(e: ActionEvent?) { - CloudWatchLogWindow.getInstance(project)?.showLogStream(logGroup, it) + Crumb.Impl( + null, + it, + null, + object : AbstractAction(message("cloudwatch.logs.view_log_stream")), DumbAware { + override fun actionPerformed(e: ActionEvent?) { + CloudWatchLogWindow.getInstance(project)?.showLogStream(logGroup, it) + } } - }) + ) } ) } diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/editor/LogGroupTable.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/editor/LogGroupTable.kt index b2565d0865..da8852f96c 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/editor/LogGroupTable.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/editor/LogGroupTable.kt @@ -90,16 +90,18 @@ class LogGroupTable( } private fun addKeyListener(table: JBTable) { - table.addKeyListener(object : KeyAdapter() { - override fun keyTyped(e: KeyEvent) { - val logStream = table.getSelectedRowLogStream() ?: return - if (!e.isConsumed && e.keyCode == KeyEvent.VK_ENTER) { - e.consume() - val window = CloudWatchLogWindow.getInstance(project) - window.showLogStream(logGroup, logStream) + table.addKeyListener( + object : KeyAdapter() { + override fun keyTyped(e: KeyEvent) { + val logStream = table.getSelectedRowLogStream() ?: return + if (!e.isConsumed && e.keyCode == KeyEvent.VK_ENTER) { + e.consume() + val window = CloudWatchLogWindow.getInstance(project) + window.showLogStream(logGroup, logStream) + } } } - }) + ) } private fun addTableMouseListener(table: JBTable) { @@ -115,10 +117,13 @@ class LogGroupTable( private fun addActions(table: JBTable) { val actionGroup = DefaultActionGroup() - actionGroup.addAction(ExportActionGroup(project, client, logGroup) { - val row = groupTable.selectedRow.takeIf { it >= 0 } ?: return@ExportActionGroup null - table.getValueAt(row, 0) as? String - }, Constraints.FIRST) + actionGroup.addAction( + ExportActionGroup(project, client, logGroup) { + val row = groupTable.selectedRow.takeIf { it >= 0 } ?: return@ExportActionGroup null + table.getValueAt(row, 0) as? String + }, + Constraints.FIRST + ) PopupHandler.installPopupHandler( table, actionGroup, diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/ecs/Ecs.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/ecs/Ecs.kt index d8b6e727ff..1c877b44c0 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/ecs/Ecs.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/ecs/Ecs.kt @@ -25,10 +25,10 @@ fun EcsClient.waitForServicesStable( success = { response -> // return true when there are no non-stable services response.services().size != 0 && - response.services().map { service -> - // service is stable if there is only a single deployment and the running count matches desired - service.deployments().size == 1 && service.runningCount() == service.desiredCount() - }.all { it } + response.services().map { service -> + // service is stable if there is only a single deployment and the running count matches desired + service.deployments().size == 1 && service.runningCount() == service.desiredCount() + }.all { it } }, fail = { it.failures().mapNotNull { failure -> diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/ecs/execution/PortMappingsTable.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/ecs/execution/PortMappingsTable.kt index 5d48a547b3..445856da22 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/ecs/execution/PortMappingsTable.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/ecs/execution/PortMappingsTable.kt @@ -24,11 +24,13 @@ class PortMappingsTable : ContainerMappingTable( NumericColumnInfo( message("cloud_debug.ecs.run_config.container.ports.local"), { it.localPort }, - { mapping, value -> mapping.localPort = value }), + { mapping, value -> mapping.localPort = value } + ), NumericColumnInfo( message("cloud_debug.ecs.run_config.container.ports.remote"), { it.remotePort }, - { mapping, value -> mapping.remotePort = value }) + { mapping, value -> mapping.remotePort = value } + ) ) private inner class NumericColumnInfo( diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/iam/CreateIamRoleDialog.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/iam/CreateIamRoleDialog.kt index 991486558e..93ff90b3cb 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/iam/CreateIamRoleDialog.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/iam/CreateIamRoleDialog.kt @@ -62,9 +62,12 @@ class CreateIamRoleDialog( ApplicationManager.getApplication().executeOnPooledThread { try { createIamRole(roleName(), policyDocument(), assumeRolePolicy()) - ApplicationManager.getApplication().invokeLater({ - close(OK_EXIT_CODE) - }, ModalityState.stateForComponent(view.component)) + ApplicationManager.getApplication().invokeLater( + { + close(OK_EXIT_CODE) + }, + ModalityState.stateForComponent(view.component) + ) } catch (e: Exception) { LOG.warn(e) { "Failed to create IAM role '${roleName()}'" } setErrorText(e.message) diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/LambdaExplorerNodes.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/LambdaExplorerNodes.kt index e4297ac2d9..931360369a 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/LambdaExplorerNodes.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/LambdaExplorerNodes.kt @@ -29,7 +29,8 @@ open class LambdaFunctionNode( LambdaClient.SERVICE_NAME, function, AwsIcons.Resources.LAMBDA_FUNCTION -), ResourceLocationNode { +), + ResourceLocationNode { override fun resourceType() = "function" diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/execution/local/LocalLambdaRunSettingsEditor.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/execution/local/LocalLambdaRunSettingsEditor.kt index 6b5f8ce979..1d0787a226 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/execution/local/LocalLambdaRunSettingsEditor.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/execution/local/LocalLambdaRunSettingsEditor.kt @@ -85,7 +85,8 @@ class LocalLambdaRunSettingsEditor(project: Project) : SettingsEditor diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/java/JavaLambdaHandlerResolver.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/java/JavaLambdaHandlerResolver.kt index 440693720e..e1faf29ea7 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/java/JavaLambdaHandlerResolver.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/java/JavaLambdaHandlerResolver.kt @@ -131,7 +131,8 @@ class JavaLambdaHandlerResolver : LambdaHandlerResolver { private fun findByClass(clz: PsiClass): String? = if (clz.canBeInstantiatedByLambda() && clz.containingFile.virtualFile != null && - clz.implementsLambdaHandlerInterface(clz.containingFile.virtualFile)) { + clz.implementsLambdaHandlerInterface(clz.containingFile.virtualFile) + ) { clz.qualifiedName } else { null @@ -147,11 +148,13 @@ class JavaLambdaHandlerResolver : LambdaHandlerResolver { clz.qualifiedName?.let { handlers.add(it) } } - handlers.addAll(clz.allMethods - .asSequence() - .filter { it.isValidHandler(clz, file) } - .map { "${clz.qualifiedName}::${it.name}" } - .toSet()) + handlers.addAll( + clz.allMethods + .asSequence() + .filter { it.isValidHandler(clz, file) } + .map { "${clz.qualifiedName}::${it.name}" } + .toSet() + ) return handlers } @@ -187,15 +190,16 @@ class JavaLambdaHandlerResolver : LambdaHandlerResolver { !(parentClass.implementsLambdaHandlerInterface(file) && this.name == HANDLER_NAME) private fun PsiMethod.hasRequiredParameters(): Boolean = when (this.parameters.size) { - 1 -> true - 2 -> (this.parameterList.parameters[0].isInputStreamParameter() && - this.parameterList.parameters[1].isOutputStreamParameter()) || - this.parameterList.parameters[1].isContextParameter() - 3 -> this.parameterList.parameters[0].isInputStreamParameter() && - this.parameterList.parameters[1].isOutputStreamParameter() && - this.parameterList.parameters[2].isContextParameter() - else -> false - } + 1 -> true + 2 -> + (this.parameterList.parameters[0].isInputStreamParameter() && this.parameterList.parameters[1].isOutputStreamParameter()) || + this.parameterList.parameters[1].isContextParameter() + 3 -> + this.parameterList.parameters[0].isInputStreamParameter() && + this.parameterList.parameters[1].isOutputStreamParameter() && + this.parameterList.parameters[2].isContextParameter() + else -> false + } private fun PsiParameter.isContextParameter(): Boolean = isClass(LAMBDA_CONTEXT) private fun PsiParameter.isInputStreamParameter(): Boolean = isClass(INPUT_STREAM) diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/sam/SamCommon.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/sam/SamCommon.kt index b2b5123fd9..590cbf51f6 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/sam/SamCommon.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/sam/SamCommon.kt @@ -34,9 +34,11 @@ class SamCommon { fun getTemplateFromDirectory(projectRoot: VirtualFile): VirtualFile? { // Use Java File so we don't need to do a full VFS refresh val projectRootFile = VfsUtil.virtualToIoFile(projectRoot) - val yamlFiles = projectRootFile.listFiles(FileFilter { - it.isFile && it.name.endsWith("yaml") || it.name.endsWith("yml") - })?.toList() ?: emptyList() + val yamlFiles = projectRootFile.listFiles( + FileFilter { + it.isFile && it.name.endsWith("yaml") || it.name.endsWith("yml") + } + )?.toList() ?: emptyList() assert(yamlFiles.size == 1) { message("cloudformation.yaml.too_many_files", yamlFiles.size) } return LocalFileSystem.getInstance().refreshAndFindFileByIoFile(yamlFiles.first()) } diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/sam/SamExecutable.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/sam/SamExecutable.kt index 04a5f91273..7dc66580a4 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/sam/SamExecutable.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/sam/SamExecutable.kt @@ -41,17 +41,19 @@ class SamExecutable : ExecutableType, AutoResolvable, Validatable { } override fun resolve(): Path? { - val path = (if (SystemInfo.isWindows) { - ExecutableDetector().find( - arrayOf("C:\\Program Files\\Amazon\\AWSSAMCLI\\bin", "C:\\Program Files (x86)\\Amazon\\AWSSAMCLI\\bin"), - arrayOf("sam.cmd", "sam.exe") - ) - } else { - ExecutableDetector().find( - arrayOf("/usr/local/bin", "/usr/bin"), - arrayOf("sam") - ) - }) ?: return null + val path = ( + if (SystemInfo.isWindows) { + ExecutableDetector().find( + arrayOf("C:\\Program Files\\Amazon\\AWSSAMCLI\\bin", "C:\\Program Files (x86)\\Amazon\\AWSSAMCLI\\bin"), + arrayOf("sam.cmd", "sam.exe") + ) + } else { + ExecutableDetector().find( + arrayOf("/usr/local/bin", "/usr/bin"), + arrayOf("sam") + ) + } + ) ?: return null return Paths.get(path) } diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/sam/SamSchemaDownloadPostCreationAction.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/sam/SamSchemaDownloadPostCreationAction.kt index 2c01aeacf8..ca20280627 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/sam/SamSchemaDownloadPostCreationAction.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/sam/SamSchemaDownloadPostCreationAction.kt @@ -35,7 +35,10 @@ class SamSchemaDownloadPostCreationAction { codeGenDownloader.downloadCode( SchemaCodeDownloadRequestDetails( - schemaTemplateParameters.schema, schemaTemplateParameters.schemaVersion, language, schemaSourceRoot.toString() + schemaTemplateParameters.schema, + schemaTemplateParameters.schemaVersion, + language, + schemaSourceRoot.toString() ), indicator ).toCompletableFuture().get() diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/sam/SamTemplateUtils.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/sam/SamTemplateUtils.kt index e8ce31a3cd..f02b923ae5 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/sam/SamTemplateUtils.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/sam/SamTemplateUtils.kt @@ -64,22 +64,24 @@ object SamTemplateUtils { if (!tempFile.exists()) { tempFile.createFile() } - tempFile.writeText(yamlWriter { - mapping("Resources") { - mapping(logicalId) { - keyValue("Type", SERVERLESS_FUNCTION_TYPE) - mapping("Properties") { - keyValue("Handler", handler) - keyValue("CodeUri", codeUri) - keyValue("Runtime", runtime.toString()) - keyValue("Timeout", timeout.toString()) - keyValue("MemorySize", memorySize.toString()) + tempFile.writeText( + yamlWriter { + mapping("Resources") { + mapping(logicalId) { + keyValue("Type", SERVERLESS_FUNCTION_TYPE) + mapping("Properties") { + keyValue("Handler", handler) + keyValue("CodeUri", codeUri) + keyValue("Runtime", runtime.toString()) + keyValue("Timeout", timeout.toString()) + keyValue("MemorySize", memorySize.toString()) - if (envVars.isNotEmpty()) { - mapping("Environment") { - mapping("Variables") { - envVars.forEach { (key, value) -> - keyValue(key, value) + if (envVars.isNotEmpty()) { + mapping("Environment") { + mapping("Variables") { + envVars.forEach { (key, value) -> + keyValue(key, value) + } } } } @@ -87,6 +89,6 @@ object SamTemplateUtils { } } } - }) + ) } } diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/upload/EditFunctionDialog.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/upload/EditFunctionDialog.kt index f2ba9ae567..7df9375d64 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/upload/EditFunctionDialog.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/upload/EditFunctionDialog.kt @@ -176,15 +176,17 @@ class EditFunctionDialog( loadSettings() } - private fun configurationChanged(): Boolean = mode != NEW && !(name == view.name.text && - description == view.description.text && - runtime == view.runtime.selected() && - handlerName == view.handlerPanel.handler.text && - envVariables.entries == view.envVars.envVars.entries && - timeout == view.timeoutSlider.value && - memorySize == view.memorySlider.value && - xrayEnabled == view.xrayEnabled.isSelected && - role == view.iamRole.selected()) + private fun configurationChanged(): Boolean = mode != NEW && !( + name == view.name.text && + description == view.description.text && + runtime == view.runtime.selected() && + handlerName == view.handlerPanel.handler.text && + envVariables.entries == view.envVars.envVars.entries && + timeout == view.timeoutSlider.value && + memorySize == view.memorySlider.value && + xrayEnabled == view.xrayEnabled.isSelected && + role == view.iamRole.selected() + ) override fun createCenterPanel(): JComponent? = view.content diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/upload/LambdaPolicies.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/upload/LambdaPolicies.kt index f3f1256241..51a68f36d4 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/upload/LambdaPolicies.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/upload/LambdaPolicies.kt @@ -8,7 +8,8 @@ import org.intellij.lang.annotations.Language const val LAMBDA_PRINCIPAL = "lambda.amazonaws.com" @Language("JSON") -val DEFAULT_ASSUME_ROLE_POLICY = """ +val DEFAULT_ASSUME_ROLE_POLICY = + """ { "Version": "2012-10-17", "Statement": [ @@ -24,7 +25,8 @@ val DEFAULT_ASSUME_ROLE_POLICY = """ """.trim() @Language("JSON") -val DEFAULT_POLICY = """ +val DEFAULT_POLICY = + """ { "Version": "2012-10-17", "Statement": [ diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/s3/CreateS3BucketDialog.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/s3/CreateS3BucketDialog.kt index ca5cad70f1..7bfbcb2cc1 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/s3/CreateS3BucketDialog.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/s3/CreateS3BucketDialog.kt @@ -52,9 +52,12 @@ class CreateS3BucketDialog( ApplicationManager.getApplication().executeOnPooledThread { try { createBucket() - ApplicationManager.getApplication().invokeLater({ - close(OK_EXIT_CODE) - }, ModalityState.stateForComponent(view.component)) + ApplicationManager.getApplication().invokeLater( + { + close(OK_EXIT_CODE) + }, + ModalityState.stateForComponent(view.component) + ) project.refreshAwsTree(S3Resources.LIST_BUCKETS) S3Telemetry.createBucket(project, Result.Succeeded) } catch (e: Exception) { diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/s3/TransferUtils.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/s3/TransferUtils.kt index c022243dd0..6cd6174ead 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/s3/TransferUtils.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/s3/TransferUtils.kt @@ -50,19 +50,21 @@ fun S3Client.upload( ): CompletionStage { val future = CompletableFuture() val request = PutObjectRequest.builder().bucket(bucket).key(key).build() - ProgressManager.getInstance().run(object : Task.Backgroundable(project, message, true, if (startInBackground) ALWAYS_BACKGROUND else null) { - override fun run(indicator: ProgressIndicator) { - indicator.isIndeterminate = false - try { - val result = ProgressMonitorInputStream(indicator, source, length = length).use { - this@upload.putObject(request, RequestBody.fromInputStream(it, length)) + ProgressManager.getInstance().run( + object : Task.Backgroundable(project, message, true, if (startInBackground) ALWAYS_BACKGROUND else null) { + override fun run(indicator: ProgressIndicator) { + indicator.isIndeterminate = false + try { + val result = ProgressMonitorInputStream(indicator, source, length = length).use { + this@upload.putObject(request, RequestBody.fromInputStream(it, length)) + } + future.complete(result) + } catch (e: Exception) { + future.completeExceptionally(e) } - future.complete(result) - } catch (e: Exception) { - future.completeExceptionally(e) } } - }) + ) return future } @@ -85,22 +87,24 @@ fun S3Client.download( ): CompletionStage { val future = CompletableFuture() val request = GetObjectRequest.builder().bucket(bucket).key(key).build() - ProgressManager.getInstance().run(object : Task.Backgroundable(project, message, true, if (startInBackground) ALWAYS_BACKGROUND else null) { - override fun run(indicator: ProgressIndicator) { - try { - this@download.getObject(request) { response, inputStream -> - indicator.isIndeterminate = false - inputStream.use { input -> - ProgressMonitorOutputStream(indicator, destination, response.contentLength()).use { output -> - IoUtils.copy(input, output) + ProgressManager.getInstance().run( + object : Task.Backgroundable(project, message, true, if (startInBackground) ALWAYS_BACKGROUND else null) { + override fun run(indicator: ProgressIndicator) { + try { + this@download.getObject(request) { response, inputStream -> + indicator.isIndeterminate = false + inputStream.use { input -> + ProgressMonitorOutputStream(indicator, destination, response.contentLength()).use { output -> + IoUtils.copy(input, output) + } } + future.complete(response) } - future.complete(response) + } catch (e: Exception) { + future.completeExceptionally(e) } - } catch (e: Exception) { - future.completeExceptionally(e) } } - }) + ) return future } diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/s3/editor/S3TreeNode.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/s3/editor/S3TreeNode.kt index c2644c583c..6f079c25b2 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/s3/editor/S3TreeNode.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/s3/editor/S3TreeNode.kt @@ -53,9 +53,11 @@ class S3TreeDirectoryNode(private val bucket: S3VirtualBucket, parent: S3TreeDir bucket.listObjects(key, continuationToken) } - val continuation = listOfNotNull(response.nextContinuationToken()?.let { - S3TreeContinuationNode(bucketName, this, "${this.key}/${message("s3.load_more")}", it) - }) + val continuation = listOfNotNull( + response.nextContinuationToken()?.let { + S3TreeContinuationNode(bucketName, this, "${this.key}/${message("s3.load_more")}", it) + } + ) val folders = response.commonPrefixes()?.map { S3TreeDirectoryNode(bucket, this, it.prefix()) } ?: emptyList() diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/s3/editor/S3TreeTable.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/s3/editor/S3TreeTable.kt index 01661a3770..562abaa085 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/s3/editor/S3TreeTable.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/s3/editor/S3TreeTable.kt @@ -144,15 +144,18 @@ class S3TreeTable( init { // Associate the drop target listener with this instance which will allow uploading by drag and drop DropTarget(this, dropTargetListener) - TreeTableSpeedSearch(this, Convertor { obj -> - val node = obj.lastPathComponent as DefaultMutableTreeNode - val userObject = node.userObject as? S3TreeNode ?: return@Convertor null - return@Convertor if (userObject !is S3TreeContinuationNode) { - userObject.name - } else { - null + TreeTableSpeedSearch( + this, + Convertor { obj -> + val node = obj.lastPathComponent as DefaultMutableTreeNode + val userObject = node.userObject as? S3TreeNode ?: return@Convertor null + return@Convertor if (userObject !is S3TreeContinuationNode) { + userObject.name + } else { + null + } } - }) + ) loadMoreListener.installOn(this) openFileListener.installOn(this) super.addKeyListener(keyListener) diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/s3/editor/S3ViewerPanel.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/s3/editor/S3ViewerPanel.kt index 8f84ee3e89..751deb51af 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/s3/editor/S3ViewerPanel.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/s3/editor/S3ViewerPanel.kt @@ -76,9 +76,11 @@ class S3ViewerPanel(disposable: Disposable, private val project: Project, privat it.add(UploadObjectAction(project, table)) it.add(Separator()) it.add(NewFolderAction(project, table)) - it.add(RenameObjectAction(project, table).apply { - registerCustomShortcutSet(CommonShortcuts.getRename(), table) - }) + it.add( + RenameObjectAction(project, table).apply { + registerCustomShortcutSet(CommonShortcuts.getRename(), table) + } + ) it.add(CopyPathAction(project, table)) it.add(Separator()) it.add(DeleteObjectAction(project, table)) diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/s3/objectActions/DeleteObjectAction.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/s3/objectActions/DeleteObjectAction.kt index c3c1e82fff..2ca5c1cb98 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/s3/objectActions/DeleteObjectAction.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/s3/objectActions/DeleteObjectAction.kt @@ -37,7 +37,8 @@ private fun deleteNodes(project: Project, treeTable: S3TreeTable, nodes: List - refreshDownloadCodeDirectory(schemaCodeDownloadDetails) - openSchemaCoreCodeFileInEditor(schemaCoreCodeFile, project) - } - .thenApply { - showDownloadCompletionNotification(schemaName, project) - } - .exceptionally { error -> - showDownloadCompletionErrorNotification(error, project) - } - .toCompletableFuture().get() + ProgressManager.getInstance().run( + object : Task.Backgroundable(project, message("schemas.schema.download_code_bindings.title", schemaName), false) { + override fun run(indicator: ProgressIndicator) { + notifyInfo( + title = NOTIFICATION_TITLE, + content = message("schemas.schema.download_code_bindings.notification.start", schemaName), + project = project + ) + + schemaCodeDownloader.downloadCode(schemaCodeDownloadDetails, indicator) + .thenCompose { schemaCoreCodeFile -> + refreshDownloadCodeDirectory(schemaCodeDownloadDetails) + openSchemaCoreCodeFileInEditor(schemaCoreCodeFile, project) + } + .thenApply { + showDownloadCompletionNotification(schemaName, project) + } + .exceptionally { error -> + showDownloadCompletionErrorNotification(error, project) + } + .toCompletableFuture().get() + } } - }) + ) onClose?.let { it() } diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/schemas/code/SchemaCodeDownloader.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/schemas/code/SchemaCodeDownloader.kt index cf16cfc893..395d353dca 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/schemas/code/SchemaCodeDownloader.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/schemas/code/SchemaCodeDownloader.kt @@ -247,21 +247,23 @@ class CodeExtractor { // Ensure that the downloaded code hierarchy has no collisions with the destination directory private fun validateNoFileCollisions(codeZipFile: File, destinationDirectory: File) { - ZipFile(codeZipFile).use({ zipFile -> - val zipEntries = zipFile.entries() - Collections.list(zipEntries).forEach { zipEntry -> - if (zipEntry.isDirectory()) { - // Ignore directories because those can/will be merged - } else { - val intendedDestinationPath = Paths.get(destinationDirectory.path, zipEntry.name) - val intendedDestinationFile = intendedDestinationPath.toFile() - if (intendedDestinationFile.exists() && !intendedDestinationFile.isDirectory) { - val name = intendedDestinationFile.name - throw SchemaCodeDownloadFileCollisionException(name) + ZipFile(codeZipFile).use( + { zipFile -> + val zipEntries = zipFile.entries() + Collections.list(zipEntries).forEach { zipEntry -> + if (zipEntry.isDirectory()) { +// Ignore directories because those can/will be merged + } else { + val intendedDestinationPath = Paths.get(destinationDirectory.path, zipEntry.name) + val intendedDestinationFile = intendedDestinationPath.toFile() + if (intendedDestinationFile.exists() && !intendedDestinationFile.isDirectory) { + val name = intendedDestinationFile.name + throw SchemaCodeDownloadFileCollisionException(name) + } } } } - }) + ) } } diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/schemas/search/SchemaSearchDialog.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/schemas/search/SchemaSearchDialog.kt index 5e89a67a51..ebbc4f5596 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/schemas/search/SchemaSearchDialog.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/schemas/search/SchemaSearchDialog.kt @@ -49,11 +49,11 @@ class SchemaSearchAllRegistriesDialog( schemaViewer: SchemaViewer = SchemaViewer(project), onCancelCallback: (SchemaSearchDialogState) -> Unit ) : SchemasSearchDialogBase( - project, - schemaViewer, - message("schemas.search.header.text.allRegistries"), - onCancelCallback - ) { + project, + schemaViewer, + message("schemas.search.header.text.allRegistries"), + onCancelCallback +) { override fun createResultRenderer(): (SchemaSearchResultWithRegistry) -> JComponent = { JBLabel("${it.registry}/${it.name}") diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/schemas/search/SchemasSearchDialogBase.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/schemas/search/SchemasSearchDialogBase.kt index 3f51531b28..58650b6b94 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/services/schemas/search/SchemasSearchDialogBase.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/services/schemas/search/SchemasSearchDialogBase.kt @@ -118,39 +118,44 @@ abstract class SchemasSearchDialogBase( } } - searchTextField.document.addDocumentListener(object : DocumentListener { - override fun changedUpdate(e: DocumentEvent?) = search() - override fun insertUpdate(e: DocumentEvent?) = search() - override fun removeUpdate(e: DocumentEvent?) = search() + searchTextField.document.addDocumentListener( + object : DocumentListener { + override fun changedUpdate(e: DocumentEvent?) = search() + override fun insertUpdate(e: DocumentEvent?) = search() + override fun removeUpdate(e: DocumentEvent?) = search() - private fun search() { - if (searchTextAlarm.isDisposed) return + private fun search() { + if (searchTextAlarm.isDisposed) return - searchTextAlarm.cancelAllRequests() + searchTextAlarm.cancelAllRequests() - searchTextAlarm.addRequest({ - val searchText = searchTextField.text + searchTextAlarm.addRequest( + { + val searchText = searchTextField.text - if (searchText.isNullOrEmpty()) { - clearState() - return@addRequest - } + if (searchText.isNullOrEmpty()) { + clearState() + return@addRequest + } - clearState() - resultsList.setEmptyText(message("schemas.search.searching")) - searchSchemas(searchText, { onSearchResultsReturned(it) }, { onErrorSearchingRegistry(it) }) - }, SEARCH_DELAY_MS) - } + clearState() + resultsList.setEmptyText(message("schemas.search.searching")) + searchSchemas(searchText, { onSearchResultsReturned(it) }, { onErrorSearchingRegistry(it) }) + }, + SEARCH_DELAY_MS + ) + } - private fun clearState() { - previewText.text = "" - resultsList.setEmptyText(message("schemas.search.no_results")) - getDownloadButton()?.isEnabled = false - resultsModel.removeAllElements() - versionsModel.removeAllElements() - currentSearchErrors.clear() + private fun clearState() { + previewText.text = "" + resultsList.setEmptyText(message("schemas.search.no_results")) + getDownloadButton()?.isEnabled = false + resultsModel.removeAllElements() + versionsModel.removeAllElements() + currentSearchErrors.clear() + } } - }) + ) } private fun onSearchResultsReturned(searchResults: List) { diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/settings/DeploySettings.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/settings/DeploySettings.kt index c0530c2a68..f0e21acd7e 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/settings/DeploySettings.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/settings/DeploySettings.kt @@ -70,6 +70,6 @@ data class DeploySamConfig( * @see DeployConfigs.samConfigs */ fun relativeSamPath(module: Module, templateFile: VirtualFile): String? = module.rootManager.contentRoots - .find { Paths.get(templateFile.path).startsWith(it.path) } - ?.let { Paths.get(it.path).relativize(Paths.get(templateFile.path)) } - ?.toString() + .find { Paths.get(templateFile.path).startsWith(it.path) } + ?.let { Paths.get(it.path).relativize(Paths.get(templateFile.path)) } + ?.toString() diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/ui/HandlerPanel.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/ui/HandlerPanel.kt index 1374eea891..61e85bb0f6 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/ui/HandlerPanel.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/ui/HandlerPanel.kt @@ -60,22 +60,26 @@ class HandlerPanel(private val project: Project) : JPanel(MigLayout("novisualpad private fun initSimpleHandler() { simpleHandler.toolTipText = message("lambda.function.handler.tooltip") - simpleHandler.addComponentListener(object : ComponentAdapter() { - override fun componentShown(e: ComponentEvent?) { - super.componentShown(e) - simpleHandler.text = handlerWithCompletion.text + simpleHandler.addComponentListener( + object : ComponentAdapter() { + override fun componentShown(e: ComponentEvent?) { + super.componentShown(e) + simpleHandler.text = handlerWithCompletion.text + } } - }) + ) } private fun initHandlerWithCompletion() { handlerWithCompletion.toolTipText = message("lambda.function.handler.tooltip") - handlerWithCompletion.addComponentListener(object : ComponentAdapter() { - override fun componentShown(e: ComponentEvent?) { - super.componentShown(e) - handlerWithCompletion.text = simpleHandler.text + handlerWithCompletion.addComponentListener( + object : ComponentAdapter() { + override fun componentShown(e: ComponentEvent?) { + super.componentShown(e) + handlerWithCompletion.text = simpleHandler.text + } } - }) + ) } private fun switchCompletion() { 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 c8a8ca85ce..2052e0d75f 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/ui/ResourceSelector.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/ui/ResourceSelector.kt @@ -98,8 +98,8 @@ class ResourceSelector private constructor( } override fun getModel(): MutableCollectionComboBoxModel = - // javax.swing.DefaultComboBoxModel.addAll(java.util.Collection) isn't in Java 8 - // The addElement method can lead to multiple selection events firing as elements are added + // javax.swing.DefaultComboBoxModel.addAll(java.util.Collection) isn't in Java 8 + // The addElement method can lead to multiple selection events firing as elements are added // Use IntelliJ's to work around this short coming super.getModel() as MutableCollectionComboBoxModel @@ -261,9 +261,17 @@ class ResourceSelector private constructor( it.awsConnection = awsConnection } - fun build() = ResourceSelector(project, resource, comboBoxModel, resolveCustomRenderer(), loadOnCreate, sortOnLoad, awsConnection ?: { - val settings = AwsConnectionManager.getInstance(project) - settings.activeRegion to settings.activeCredentialProvider - }) + fun build() = ResourceSelector( + project, + resource, + comboBoxModel, + resolveCustomRenderer(), + loadOnCreate, + sortOnLoad, + awsConnection ?: { + val settings = AwsConnectionManager.getInstance(project) + settings.activeRegion to settings.activeCredentialProvider + } + ) } } diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/ui/wizard/PyCharmSdkSelectionPanel.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/ui/wizard/PyCharmSdkSelectionPanel.kt index b841a181b9..a6003302d2 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/ui/wizard/PyCharmSdkSelectionPanel.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/ui/wizard/PyCharmSdkSelectionPanel.kt @@ -35,12 +35,15 @@ class PyCharmSdkSelectionPanel(val step: SamProjectRuntimeSelectionStep) : SdkSe override val sdkSelectionLabel: JLabel? = null private fun newSdkPanel(): PyAddSdkGroupPanel = - // construct a py-specific settings step and grab its sdk panel instance - object : ProjectSpecificSettingsStep(object : PythonProjectGenerator() { - override fun getLogo(): Icon? = AwsIcons.Logos.AWS - - override fun getName(): String = message("sam.init.name") - }, AbstractNewProjectStep.AbstractCallback()) { + // construct a py-specific settings step and grab its sdk panel instance + object : ProjectSpecificSettingsStep( + object : PythonProjectGenerator() { + override fun getLogo(): Icon? = AwsIcons.Logos.AWS + + override fun getName(): String = message("sam.init.name") + }, + AbstractNewProjectStep.AbstractCallback() + ) { // shim validation back to the user UI... override fun setErrorText(text: String?) { step.setErrorText(text) @@ -77,9 +80,11 @@ class PyCharmSdkSelectionPanel(val step: SamProjectRuntimeSelectionStep) : SdkSe document.addDocumentListener(documentListener) - sdkSelectionPanel.addChangeListener(Runnable { - step.checkValid() - }) + sdkSelectionPanel.addChangeListener( + Runnable { + step.checkValid() + } + ) sdkSelectionPanel.newProjectPath = step.getLocationField().text.trim() } diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/ui/wizard/SamInitProjectBuilderCommon.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/ui/wizard/SamInitProjectBuilderCommon.kt index dec1cfce0c..9be92a7b50 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/ui/wizard/SamInitProjectBuilderCommon.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/ui/wizard/SamInitProjectBuilderCommon.kt @@ -57,15 +57,20 @@ fun setupSamSelectionElements(samExecutableField: JTextField, editButton: JButto samExecutableField.toolTipText = toolTipText editButton.toolTipText = toolTipText - ProgressManager.getInstance().runProcessWithProgressSynchronously({ - try { - val validSamPath = when (ExecutableManager.getInstance().getExecutable().toCompletableFuture().get()) { - is ExecutableInstance.Executable -> true - else -> false + ProgressManager.getInstance().runProcessWithProgressSynchronously( + { + try { + val validSamPath = when (ExecutableManager.getInstance().getExecutable().toCompletableFuture().get()) { + is ExecutableInstance.Executable -> true + else -> false + } + updateUi(validSamPath) + } catch (e: Throwable) { + updateUi(validSamPath = false) } - updateUi(validSamPath) - } catch (e: Throwable) { - updateUi(validSamPath = false) - } - }, message("lambda.run_configuration.sam.validating"), false, null) + }, + message("lambda.run_configuration.sam.validating"), + false, + null + ) } diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/ui/wizard/SamProjectGenerator.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/ui/wizard/SamProjectGenerator.kt index bd3a4b96ec..92afc88a45 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/ui/wizard/SamProjectGenerator.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/ui/wizard/SamProjectGenerator.kt @@ -33,11 +33,13 @@ import javax.swing.Icon import javax.swing.JComponent // ref: https://github.com/JetBrains/intellij-plugins/blob/master/vuejs/src/org/jetbrains/vuejs/cli/VueCliProjectGenerator.kt -class SamProjectGenerator : ProjectTemplate, - WebProjectTemplate(), // pycharm hack - DirectoryProjectGenerator, - CustomStepProjectGenerator, - HideableProjectGenerator { +class SamProjectGenerator : + ProjectTemplate, + WebProjectTemplate(), + // pycharm hack + DirectoryProjectGenerator, + CustomStepProjectGenerator, + HideableProjectGenerator { val builder = SamProjectBuilder(this) val step = SamProjectRuntimeSelectionStep(this) val peer = SamProjectGeneratorSettingsPeer(this) diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/ui/wizard/SamProjectGeneratorIntelliJShims.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/ui/wizard/SamProjectGeneratorIntelliJShims.kt index 6b8431884c..662fb4b77f 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/ui/wizard/SamProjectGeneratorIntelliJShims.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/ui/wizard/SamProjectGeneratorIntelliJShims.kt @@ -56,23 +56,25 @@ class SamProjectBuilder(private val generator: SamProjectGenerator) : ModuleBuil val outputDir: VirtualFile = contentEntry.file ?: throw Exception(message("sam.init.error.no.virtual.file")) StartupManager.getInstance(rootModel.project).runWhenProjectIsInitialized { - ProgressManager.getInstance().run(object : Task.Backgroundable(rootModel.project, message("sam.init.generating.template"), false) { - override fun run(indicator: ProgressIndicator) { - ModuleRootModificationUtil.updateModel(rootModel.module) { model -> - val samTemplate = settings.template - samTemplate.build(project, selectedRuntime, settings.schemaParameters, outputDir) - VfsUtil.markDirtyAndRefresh(false, true, true, outputDir) - runInEdt { - try { - samTemplate.postCreationAction(settings, outputDir, model, generator.defaultSourceCreatingProject, indicator) - } catch (t: Throwable) { - LOG.error(t) { "Exception thrown during postCreationAction" } - model.dispose() + ProgressManager.getInstance().run( + object : Task.Backgroundable(rootModel.project, message("sam.init.generating.template"), false) { + override fun run(indicator: ProgressIndicator) { + ModuleRootModificationUtil.updateModel(rootModel.module) { model -> + val samTemplate = settings.template + samTemplate.build(project, selectedRuntime, settings.schemaParameters, outputDir) + VfsUtil.markDirtyAndRefresh(false, true, true, outputDir) + runInEdt { + try { + samTemplate.postCreationAction(settings, outputDir, model, generator.defaultSourceCreatingProject, indicator) + } catch (t: Throwable) { + LOG.error(t) { "Exception thrown during postCreationAction" } + model.dispose() + } } } } } - }) + ) } } diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/utils/NotificationUtils.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/utils/NotificationUtils.kt index ebf27db98d..2387dcfdc1 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/utils/NotificationUtils.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/utils/NotificationUtils.kt @@ -37,7 +37,8 @@ fun Throwable.notifyError(title: String = "", project: Project? = null) { title, message, NotificationType.ERROR - ), project + ), + project ) } diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/utils/ui/ScrollBarUtils.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/utils/ui/ScrollBarUtils.kt index 3c94c8a485..c792645d74 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/utils/ui/ScrollBarUtils.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/utils/ui/ScrollBarUtils.kt @@ -9,33 +9,37 @@ import javax.swing.JScrollBar import javax.swing.JScrollPane fun JScrollPane.topReached(block: () -> Unit) { - verticalScrollBar.addAdjustmentListener(object : AdjustmentListener { - var lastAdjustment = verticalScrollBar.minimum - override fun adjustmentValueChanged(e: AdjustmentEvent?) { - if (e == null || e.value == lastAdjustment) { - return - } - lastAdjustment = e.value - if (verticalScrollBar.isAtTop()) { - block() + verticalScrollBar.addAdjustmentListener( + object : AdjustmentListener { + var lastAdjustment = verticalScrollBar.minimum + override fun adjustmentValueChanged(e: AdjustmentEvent?) { + if (e == null || e.value == lastAdjustment) { + return + } + lastAdjustment = e.value + if (verticalScrollBar.isAtTop()) { + block() + } } } - }) + ) } fun JScrollPane.bottomReached(block: () -> Unit) { - verticalScrollBar.addAdjustmentListener(object : AdjustmentListener { - var lastAdjustment = verticalScrollBar.minimum - override fun adjustmentValueChanged(e: AdjustmentEvent?) { - if (e == null || e.value == lastAdjustment) { - return - } - lastAdjustment = e.value - if (verticalScrollBar.isAtBottom()) { - block() + verticalScrollBar.addAdjustmentListener( + object : AdjustmentListener { + var lastAdjustment = verticalScrollBar.minimum + override fun adjustmentValueChanged(e: AdjustmentEvent?) { + if (e == null || e.value == lastAdjustment) { + return + } + lastAdjustment = e.value + if (verticalScrollBar.isAtBottom()) { + block() + } } } - }) + ) } private fun JScrollBar.isAtBottom(): Boolean = value == (maximum - visibleAmount) diff --git a/jetbrains-core/src/software/aws/toolkits/jetbrains/utils/ui/SearchFieldUtils.kt b/jetbrains-core/src/software/aws/toolkits/jetbrains/utils/ui/SearchFieldUtils.kt index 6710b9f2f9..8e5ec4479b 100644 --- a/jetbrains-core/src/software/aws/toolkits/jetbrains/utils/ui/SearchFieldUtils.kt +++ b/jetbrains-core/src/software/aws/toolkits/jetbrains/utils/ui/SearchFieldUtils.kt @@ -10,31 +10,35 @@ import java.beans.PropertyChangeEvent import java.beans.PropertyChangeListener fun SearchTextField.onEmpty(block: () -> Unit) { - textEditor.addPropertyChangeListener(object : PropertyChangeListener { - private var lastText = "" - override fun propertyChange(evt: PropertyChangeEvent?) { - val searchFieldText = text.trim() - if (searchFieldText == lastText) { - return - } - lastText = searchFieldText - if (text.isEmpty()) { - block() + textEditor.addPropertyChangeListener( + object : PropertyChangeListener { + private var lastText = "" + override fun propertyChange(evt: PropertyChangeEvent?) { + val searchFieldText = text.trim() + if (searchFieldText == lastText) { + return + } + lastText = searchFieldText + if (text.isEmpty()) { + block() + } } } - }) + ) } fun SearchTextField.onEnter(block: () -> Unit) { - textEditor.addActionListener(object : ActionListener { - private var lastText = "" - override fun actionPerformed(e: ActionEvent?) { - val searchFieldText = text.trim() - if (searchFieldText == lastText) { - return + textEditor.addActionListener( + object : ActionListener { + private var lastText = "" + override fun actionPerformed(e: ActionEvent?) { + val searchFieldText = text.trim() + if (searchFieldText == lastText) { + return + } + lastText = searchFieldText + block() } - lastText = searchFieldText - block() } - }) + ) } diff --git a/jetbrains-core/tst/software/aws/toolkits/jetbrains/core/AwsClientManagerTest.kt b/jetbrains-core/tst/software/aws/toolkits/jetbrains/core/AwsClientManagerTest.kt index c88925b488..540f2c3818 100644 --- a/jetbrains-core/tst/software/aws/toolkits/jetbrains/core/AwsClientManagerTest.kt +++ b/jetbrains-core/tst/software/aws/toolkits/jetbrains/core/AwsClientManagerTest.kt @@ -151,11 +151,14 @@ class AwsClientManagerTest { @Test fun globalServicesCanBeGivenAnyRegion() { val sut = getClientManager() - MockRegionProvider.getInstance().addService("DummyService", Service( - endpoints = mapOf("global" to Endpoint()), - isRegionalized = false, - partitionEndpoint = "global" - )) + MockRegionProvider.getInstance().addService( + "DummyService", + Service( + endpoints = mapOf("global" to Endpoint()), + isRegionalized = false, + partitionEndpoint = "global" + ) + ) val first = sut.getClient(regionOverride = AwsRegion("us-east-1", "us-east-1", "aws")) val second = sut.getClient(regionOverride = AwsRegion("us-west-2", "us-west-2", "aws")) diff --git a/jetbrains-core/tst/software/aws/toolkits/jetbrains/core/credentials/DefaultAwsConnectionManagerTest.kt b/jetbrains-core/tst/software/aws/toolkits/jetbrains/core/credentials/DefaultAwsConnectionManagerTest.kt index 60b7338dab..3009f2eccd 100644 --- a/jetbrains-core/tst/software/aws/toolkits/jetbrains/core/credentials/DefaultAwsConnectionManagerTest.kt +++ b/jetbrains-core/tst/software/aws/toolkits/jetbrains/core/credentials/DefaultAwsConnectionManagerTest.kt @@ -84,7 +84,8 @@ class DefaultAwsConnectionManagerTest { @Test fun `On load, default region of credential is used if there is no other active region`() { - val element = """ + val element = + """ @@ -160,11 +161,14 @@ class DefaultAwsConnectionManagerTest { var gotNotification = false val busConnection = project.messageBus.connect() - busConnection.subscribe(AwsConnectionManager.CONNECTION_SETTINGS_STATE_CHANGED, object : ConnectionSettingsStateChangeNotifier { - override fun settingsStateChanged(newState: ConnectionState) { - gotNotification = true + busConnection.subscribe( + AwsConnectionManager.CONNECTION_SETTINGS_STATE_CHANGED, + object : ConnectionSettingsStateChangeNotifier { + override fun settingsStateChanged(newState: ConnectionState) { + gotNotification = true + } } - }) + ) changeRegion(AwsRegionProvider.getInstance().defaultRegion()) @@ -178,11 +182,14 @@ class DefaultAwsConnectionManagerTest { var gotNotification = false val busConnection = project.messageBus.connect() - busConnection.subscribe(AwsConnectionManager.CONNECTION_SETTINGS_STATE_CHANGED, object : ConnectionSettingsStateChangeNotifier { - override fun settingsStateChanged(newState: ConnectionState) { - gotNotification = true + busConnection.subscribe( + AwsConnectionManager.CONNECTION_SETTINGS_STATE_CHANGED, + object : ConnectionSettingsStateChangeNotifier { + override fun settingsStateChanged(newState: ConnectionState) { + gotNotification = true + } } - }) + ) changeCredentialProvider( mockCredentialManager.addCredentials("Mock") @@ -232,7 +239,8 @@ class DefaultAwsConnectionManagerTest { @Test fun `Active credential can be restored from persistence`() { - val element = """ + val element = + """