Skip to content

Commit 53e5ebd

Browse files
authored
Add option to override CodeCatalyst API endpoint to registry (#4042)
1 parent 578aec1 commit 53e5ebd

File tree

9 files changed

+254
-87
lines changed

9 files changed

+254
-87
lines changed

jetbrains-core/resources/META-INF/plugin.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -494,6 +494,7 @@
494494
<registryKey key="aws.credentialProcess.timeout" description="AWS Credential Process timeout (ms)" restartRequired="false" defaultValue="30000"/>
495495
<registryKey key="aws.debuggerAttach.timeout" description="Time allowed for debuggers to attach before timing out (ms)" restartRequired="false"
496496
defaultValue="60000"/>
497+
<registryKey key="aws.codecatalyst.endpoint" description="API endpoint for the CodeCatalyst service" restartRequired="true" defaultValue=""/>
497498
<registryKey key="aws.toolkit.developerMode" description="Enables features to facilitate development of the toolkit" restartRequired="false"
498499
defaultValue="false"/>
499500
<typedHandler implementation="software.aws.toolkits.jetbrains.services.codewhisperer.editor.CodeWhispererTypedHandler"/>

jetbrains-core/src/software/aws/toolkits/jetbrains/services/caws/CawsClientCustomizer.kt

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33

44
package software.aws.toolkits.jetbrains.services.caws
55

6+
import com.intellij.openapi.util.registry.Registry
7+
import com.intellij.util.text.nullize
68
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider
79
import software.amazon.awssdk.auth.token.credentials.SdkTokenProvider
810
import software.amazon.awssdk.awscore.client.builder.AwsClientBuilder
@@ -15,7 +17,9 @@ import software.amazon.awssdk.services.codecatalyst.model.CodeCatalystException
1517
import software.aws.toolkits.core.ToolkitClientCustomizer
1618
import software.aws.toolkits.core.utils.debug
1719
import software.aws.toolkits.core.utils.getLogger
20+
import software.aws.toolkits.core.utils.tryOrNull
1821
import software.aws.toolkits.core.utils.warn
22+
import java.net.URI
1923

2024
class CawsClientCustomizer : ToolkitClientCustomizer {
2125
override fun customize(
@@ -26,6 +30,20 @@ class CawsClientCustomizer : ToolkitClientCustomizer {
2630
clientOverrideConfiguration: ClientOverrideConfiguration.Builder
2731
) {
2832
if (builder is CodeCatalystClientBuilder) {
33+
val endpointOverride = Registry.get("aws.codecatalyst.endpoint").asString().nullize(true)
34+
if (endpointOverride != null) {
35+
tryOrNull {
36+
val uri = URI.create(endpointOverride)
37+
if (uri.scheme == null || uri.authority == null) {
38+
null
39+
} else {
40+
uri
41+
}
42+
}?.let {
43+
builder.endpointOverride(it)
44+
}
45+
}
46+
2947
clientOverrideConfiguration.addExecutionInterceptor(object : ExecutionInterceptor {
3048
override fun onExecutionFailure(context: Context.FailedExecution, executionAttributes: ExecutionAttributes) {
3149
val exception = context.exception()

jetbrains-core/src/software/aws/toolkits/jetbrains/services/caws/CawsCloneDialogComponent.kt

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -157,16 +157,7 @@ class CawsCloneDialogComponent(
157157
val cache = AwsResourceCache.getInstance()
158158
val projects = cache.getResource(CawsResources.ALL_PROJECTS, cawsConnectionSettings).await()
159159
projects.forEach { cawsProject ->
160-
val items = cache.getResource(CawsResources.codeRepositories(cawsProject), cawsConnectionSettings).await()
161-
items.forEach { item ->
162-
val url = cache.getResource(
163-
CawsResources.cloneUrls(CawsCodeRepository(cawsProject.space, cawsProject.project, item.name)),
164-
cawsConnectionSettings
165-
).toCompletableFuture().get()
166-
if (url.contains(CawsEndpoints.CAWS_GIT_PATTERN)) {
167-
repoListModel.add(item)
168-
}
169-
}
160+
repoListModel.add(cache.getResource(CawsResources.codeRepositories(cawsProject), cawsConnectionSettings).await())
170161
}
171162

172163
with(getCoroutineUiContext()) {

jetbrains-core/src/software/aws/toolkits/jetbrains/services/caws/CawsEndpoints.kt

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33

44
package software.aws.toolkits.jetbrains.services.caws
55

6+
import software.aws.toolkits.core.utils.tryOrNull
7+
import java.net.URI
8+
69
object CawsEndpoints {
710
const val CAWS_DOCS = "https://docs.aws.amazon.com/codecatalyst/latest/userguide/welcome.html"
811
const val CAWS_DEV_ENV_MARKETING = "https://codecatalyst.aws/explore/dev-environments"
@@ -11,8 +14,17 @@ object CawsEndpoints {
1114

1215
private const val CAWS_PROD_CONSOLE_BASE = "https://codecatalyst.aws/"
1316

14-
// TODO: fix this heuristic
15-
const val CAWS_GIT_PATTERN = "codecatalyst.aws"
17+
private val CAWS_PROD_GIT_PATTERN = """git\..*?\.codecatalyst.aws""".toRegex(RegexOption.IGNORE_CASE)
18+
private val CAWS_GAMMA_GIT_PATTERN = """git\..*?\.aws.dev""".toRegex(RegexOption.IGNORE_CASE)
19+
fun isCawsGit(url: String): Boolean {
20+
val uri = tryOrNull {
21+
URI.create(url)
22+
} ?: return false
23+
24+
return uri.host?.let {
25+
it.matches(CAWS_PROD_GIT_PATTERN) || it.matches(CAWS_GAMMA_GIT_PATTERN)
26+
} ?: false
27+
}
1628

1729
object ConsoleFactory {
1830
fun baseUrl() = CAWS_PROD_CONSOLE_BASE

jetbrains-core/src/software/aws/toolkits/jetbrains/services/caws/CawsHttpAuthProvider.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ class CawsHttpAuthProvider : GitHttpAuthDataProvider {
4444
}
4545

4646
override fun getAuthData(project: Project, url: String, login: String): AuthData? {
47-
if (url.contains(CawsEndpoints.CAWS_GIT_PATTERN)) {
47+
if (CawsEndpoints.isCawsGit(url)) {
4848
return getPat(login)?.let { AuthData(login, it.getPasswordAsString()) }
4949
}
5050

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
// Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package software.aws.toolkits.jetbrains.services.caws
5+
6+
import com.intellij.testFramework.ApplicationExtension
7+
import org.junit.jupiter.api.Test
8+
import org.junit.jupiter.api.extension.ExtendWith
9+
import org.junit.jupiter.api.extension.RegisterExtension
10+
import org.mockito.kotlin.eq
11+
import org.mockito.kotlin.mock
12+
import org.mockito.kotlin.verify
13+
import org.mockito.kotlin.verifyNoInteractions
14+
import software.amazon.awssdk.services.codecatalyst.CodeCatalystClientBuilder
15+
import software.aws.toolkits.jetbrains.utils.rules.RegistryExtension
16+
import java.net.URI
17+
18+
@ExtendWith(ApplicationExtension::class)
19+
class CawsClientCustomizerTest {
20+
@JvmField
21+
@RegisterExtension
22+
val registryExtension = RegistryExtension()
23+
24+
private val registryKey = "aws.codecatalyst.endpoint"
25+
26+
@Test
27+
fun `empty registry does not override`() {
28+
registryExtension.setValue(registryKey, "")
29+
30+
val mock = mock<CodeCatalystClientBuilder>()
31+
CawsClientCustomizer().customize(
32+
null,
33+
null,
34+
"",
35+
mock,
36+
mock()
37+
)
38+
39+
verifyNoInteractions(mock)
40+
}
41+
42+
@Test
43+
fun `spaces in registry does not override`() {
44+
registryExtension.setValue(registryKey, " ")
45+
46+
val mock = mock<CodeCatalystClientBuilder>()
47+
CawsClientCustomizer().customize(
48+
null,
49+
null,
50+
"",
51+
mock,
52+
mock()
53+
)
54+
55+
verifyNoInteractions(mock)
56+
}
57+
58+
@Test
59+
fun `can override through registry`() {
60+
registryExtension.setValue(registryKey, "https://example.com")
61+
62+
val mock = mock<CodeCatalystClientBuilder>()
63+
CawsClientCustomizer().customize(
64+
null,
65+
null,
66+
"",
67+
mock,
68+
mock()
69+
)
70+
71+
verify(mock).endpointOverride(eq(URI.create("https://example.com")))
72+
}
73+
74+
@Test
75+
fun `ignores URI without scheme`() {
76+
registryExtension.setValue(registryKey, "kjdfajkl;afdsjklfads.csd")
77+
78+
val mock = mock<CodeCatalystClientBuilder>()
79+
CawsClientCustomizer().customize(
80+
null,
81+
null,
82+
"",
83+
mock,
84+
mock()
85+
)
86+
87+
verifyNoInteractions(mock)
88+
}
89+
90+
@Test
91+
fun `ignores URI without authority`() {
92+
registryExtension.setValue(registryKey, "https://")
93+
94+
val mock = mock<CodeCatalystClientBuilder>()
95+
CawsClientCustomizer().customize(
96+
null,
97+
null,
98+
"",
99+
mock,
100+
mock()
101+
)
102+
103+
verifyNoInteractions(mock)
104+
}
105+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package software.aws.toolkits.jetbrains.services.caws
5+
6+
import org.assertj.core.api.Assertions.assertThat
7+
import org.junit.jupiter.api.Test
8+
9+
class CawsEndpointsTest {
10+
@Test
11+
fun `isCawsGit returns true for prod-like`() {
12+
assertThat(CawsEndpoints.isCawsGit("https://git.us-west-2.codecatalyst.aws/v1/a/b/c")).isTrue()
13+
assertThat(CawsEndpoints.isCawsGit("https://[email protected]/v1/a/b/c")).isTrue()
14+
assertThat(CawsEndpoints.isCawsGit("https://[email protected]/v1/a/b/c")).isTrue()
15+
}
16+
17+
@Test
18+
fun `isCawsGit returns true for gamma-like`() {
19+
assertThat(CawsEndpoints.isCawsGit("https://git.a.something.aws.dev/v1/a/b/c")).isTrue()
20+
assertThat(CawsEndpoints.isCawsGit("https://[email protected]/v1/a/b/c")).isTrue()
21+
}
22+
23+
@Test
24+
fun `isCawsGit returns false for non-caws`() {
25+
assertThat(CawsEndpoints.isCawsGit("https://example.com")).isFalse()
26+
}
27+
28+
@Test
29+
fun `isCawsGit returns false for malformed`() {
30+
assertThat(CawsEndpoints.isCawsGit("<>^`{|}")).isFalse()
31+
assertThat(CawsEndpoints.isCawsGit("something")).isFalse()
32+
assertThat(CawsEndpoints.isCawsGit("127.0.0.1")).isFalse()
33+
}
34+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package software.aws.toolkits.jetbrains.utils.rules
5+
6+
import com.intellij.openapi.util.registry.Registry
7+
import com.intellij.openapi.util.registry.RegistryValue
8+
import org.junit.jupiter.api.extension.AfterEachCallback
9+
import org.junit.jupiter.api.extension.ExtensionContext
10+
11+
/**
12+
* [String] variant of [com.intellij.testFramework.RegistryKeyExtension] that handles multiple values
13+
*/
14+
class RegistryExtension : AfterEachCallback {
15+
private val oldValues = mutableMapOf<String, Pair<RegistryValue, String>>()
16+
17+
override fun afterEach(context: ExtensionContext?) {
18+
oldValues.forEach { _, (key, value) -> key.setValue(value) }
19+
}
20+
21+
fun setValue(key: String, value: String) {
22+
val (registryValue, _) = oldValues.computeIfAbsent(key) {
23+
val registryValue = Registry.get(it)
24+
25+
registryValue to registryValue.asString()
26+
}
27+
28+
registryValue.setValue(value)
29+
}
30+
}

0 commit comments

Comments
 (0)