Skip to content

Commit 23ef07a

Browse files
authored
Improve UX for uncaught UnsupportedAction exception (#2898)
1 parent 9d13b6c commit 23ef07a

File tree

4 files changed

+93
-2
lines changed

4 files changed

+93
-2
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"type" : "bugfix",
3+
"description" : "Fix uncaught exception when a resource does not support LIST in a certain region."
4+
}

core/tst/software/aws/toolkits/core/utils/test/AssertJAsserts.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
package software.aws.toolkits.core.utils.test
55

66
import org.assertj.core.api.IterableAssert
7+
import org.assertj.core.api.ListAssert
78
import org.assertj.core.api.ObjectAssert
89

910
@Suppress("UNCHECKED_CAST")
@@ -13,3 +14,7 @@ val <T : Any> ObjectAssert<T?>.notNull: ObjectAssert<T>
1314
@Suppress("UNCHECKED_CAST")
1415
inline fun <reified SubType : Any> IterableAssert<*>.hasOnlyElementsOfType(): IterableAssert<SubType> =
1516
hasOnlyElementsOfType(SubType::class.java) as IterableAssert<SubType>
17+
18+
@Suppress("UNCHECKED_CAST")
19+
inline fun <reified SubType : Any> ListAssert<*>.hasOnlyOneElementOfType(): ObjectAssert<SubType> =
20+
(hasOnlyElementsOfType(SubType::class.java) as ListAssert<SubType>).singleElement()

jetbrains-core/src/software/aws/toolkits/jetbrains/services/dynamic/explorer/DynamicResourceServiceNode.kt

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,11 @@ import com.intellij.openapi.progress.ProgressIndicator
88
import com.intellij.openapi.progress.Task
99
import com.intellij.openapi.project.Project
1010
import com.intellij.ui.EditorNotifications
11+
import software.amazon.awssdk.services.cloudcontrol.model.UnsupportedActionException
1112
import software.aws.toolkits.core.utils.getLogger
1213
import software.aws.toolkits.jetbrains.core.awsClient
1314
import software.aws.toolkits.jetbrains.core.credentials.getConnectionSettingsOrThrow
15+
import software.aws.toolkits.jetbrains.core.explorer.nodes.AwsExplorerEmptyNode
1416
import software.aws.toolkits.jetbrains.core.explorer.nodes.AwsExplorerNode
1517
import software.aws.toolkits.jetbrains.core.explorer.nodes.ResourceActionNode
1618
import software.aws.toolkits.jetbrains.core.explorer.nodes.ResourceParentNode
@@ -24,6 +26,7 @@ import software.aws.toolkits.jetbrains.services.dynamic.OpenViewEditableDynamicR
2426
import software.aws.toolkits.jetbrains.services.dynamic.ViewEditableDynamicResourceVirtualFile
2527
import software.aws.toolkits.resources.message
2628
import software.aws.toolkits.telemetry.DynamicresourceTelemetry
29+
import software.aws.toolkits.telemetry.Result
2730

2831
class DynamicResourceResourceTypeNode(project: Project, private val resourceType: String) :
2932
AwsExplorerNode<String>(project, resourceType, null),
@@ -43,8 +46,16 @@ class DynamicResourceResourceTypeNode(project: Project, private val resourceType
4346
.map { DynamicResourceNode(nodeProject, it) }
4447
.also { DynamicresourceTelemetry.listResource(project = nodeProject, success = true, resourceType = resourceType) }
4548
} catch (e: Exception) {
46-
DynamicresourceTelemetry.listResource(project = nodeProject, success = false, resourceType = resourceType)
47-
throw e
49+
when (e) {
50+
is UnsupportedActionException -> {
51+
DynamicresourceTelemetry.listResource(project = nodeProject, result = Result.Cancelled, resourceType = resourceType)
52+
listOf(AwsExplorerEmptyNode(nodeProject, message("dynamic_resources.unavailable_in_region", region.id)))
53+
}
54+
else -> {
55+
DynamicresourceTelemetry.listResource(project = nodeProject, success = false, resourceType = resourceType)
56+
throw e
57+
}
58+
}
4859
}
4960

5061
override fun actionGroupName(): String = "aws.toolkit.explorer.dynamic.resource.type"
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
// Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package software.aws.toolkits.jetbrains.services.dynamic.explorer
5+
6+
import com.intellij.testFramework.ProjectRule
7+
import org.assertj.core.api.Assertions.assertThat
8+
import org.junit.Rule
9+
import org.junit.Test
10+
import software.amazon.awssdk.services.cloudcontrol.model.UnsupportedActionException
11+
import software.aws.toolkits.core.utils.test.aString
12+
import software.aws.toolkits.core.utils.test.hasOnlyOneElementOfType
13+
import software.aws.toolkits.jetbrains.core.MockResourceCacheRule
14+
import software.aws.toolkits.jetbrains.core.explorer.nodes.AwsExplorerEmptyNode
15+
import software.aws.toolkits.jetbrains.core.explorer.nodes.AwsExplorerErrorNode
16+
import software.aws.toolkits.jetbrains.services.dynamic.CloudControlApiResources
17+
import software.aws.toolkits.jetbrains.services.dynamic.DynamicResource
18+
import software.aws.toolkits.jetbrains.services.dynamic.ResourceType
19+
import software.aws.toolkits.resources.message
20+
import java.util.concurrent.CompletableFuture
21+
22+
class DynamicResourceResourceTypeNodeTest {
23+
24+
@Rule
25+
@JvmField
26+
val projectRule = ProjectRule()
27+
28+
@Rule
29+
@JvmField
30+
val resourceCache = MockResourceCacheRule()
31+
32+
@Test
33+
fun returnsListFromProvider() {
34+
val type = aString()
35+
val identifier = aString()
36+
val resources = listOf(DynamicResource(ResourceType(type, "foo", "bah"), identifier))
37+
resourceCache.addEntry(projectRule.project, CloudControlApiResources.listResources(type), resources)
38+
39+
val sut = DynamicResourceResourceTypeNode(projectRule.project, type)
40+
41+
assertThat(sut.children).hasOnlyOneElementOfType<DynamicResourceNode>().satisfies {
42+
assertThat(it.displayName()).isEqualTo(identifier)
43+
}
44+
}
45+
46+
@Test
47+
fun unsupportedExceptionResultsInEmptyNode() {
48+
val type = aString()
49+
resourceCache.addEntry(
50+
projectRule.project,
51+
CloudControlApiResources.listResources(type),
52+
CompletableFuture.failedFuture(UnsupportedActionException.builder().build())
53+
)
54+
55+
val sut = DynamicResourceResourceTypeNode(projectRule.project, type)
56+
57+
assertThat(sut.children).hasOnlyOneElementOfType<AwsExplorerEmptyNode>().satisfies {
58+
assertThat(it.displayName()).startsWith(message("dynamic_resources.unavailable_in_region", ""))
59+
}
60+
}
61+
62+
@Test
63+
fun otherExceptionsBubble() {
64+
val type = aString()
65+
resourceCache.addEntry(projectRule.project, CloudControlApiResources.listResources(type), CompletableFuture.failedFuture(RuntimeException()))
66+
67+
val sut = DynamicResourceResourceTypeNode(projectRule.project, type)
68+
69+
assertThat(sut.children).hasOnlyOneElementOfType<AwsExplorerErrorNode>()
70+
}
71+
}

0 commit comments

Comments
 (0)