Skip to content

Commit

Permalink
Merge AWS Builder ID/SSO feature into feature/vector branch (aws#1083)
Browse files Browse the repository at this point in the history
* (feat codewhisperer) bump ideProfilename (aws#988)

* bump ideProfilename

* *update codewhisperer service model and bump sdk version (aws#991)

* update cwspr model v2 (aws#1001)

* Cwspr sso client adaptor (aws#995)

* cwspr login dialog prototype (aws#990)

* fix not IAM comment not grayed out (aws#1006)

* cwspr nodes UX update (aws#1005)

* Cwspr setting panel grayout (for sso user) (aws#1000)

* fix transformation (aws#1009)

* (feat codewhisperer) credentail impl (aws#1003)

* remove all usage of clientManager.getClient (aws#1015)

* update codewhisperer model -- reference filter (aws#1020)

* remove accountless token ux compoentns (aws#1013)

* merge conflict resolve

* (feat codewhisperer) remove reference filter logic (aws#1017)

* (feat codewhisperer) remove codewhisperer from toolkit experimental feature (aws#1018)

* empty commit to trigger build

* (feat codewhisperer) credential flow integration  (aws#1014)

* fix compile error caused by missing

* Some adaption to new auth logic from Toolkit

* adapt to Toolkit's bearer credential flow

* update test case for connectionSettings() and some cleanup

* detekt fix

* Remove accidentally added changes

* (codewhisperer) cleanup and chores (aws#1037)

* clean up codewhisperer constants duplicate Sono/Sso settings
* remove redundant logics in codewhispererEndpointCustomizer
* add logic to handle user choose SSO login with AWS ID url

* Fix CodeWhispererCredentialManagerTest

* (codewhisperer) fix bugs (aws#1039)

* (codewhisperer) codewhisperer usage limit hit for free tier users UX flow (aws#1044)

* finish indifividual UX components for usage limit hit case, integration[WIP]

* initial commits of usage limit UX components, integration logics[WIP]

* add UX integration

* add notificationError and todos

* add back deleted import

* small fix -- only show error message on manual trigger

* (codewhisperer) access notification and logics implementation (aws#1041)

* accless notification and logics impmlementation

* address comments

* update timer API usage

* detekt fix

* small fix

* small fix

* small tweak to prevent listener get installed > 1 times

* detekt fix

* Update How to user CodeWhisperer markdown (aws#1060)

* Use local gif for HowToUseCodeWhisperer (aws#1063)

* (codewhisperer) integration with new Toolkit API, UX (profile switcher, logout) (aws#1055)

* Only show the hint text when CodeWhisperer is using secondary connection (aws#1068)

* update accless expiration date (aws#1073)

* (codewhisperer) several small fix (aws#1070)

* * Toolkit SSO action grouop help action imple
* show toast infomation when user ack codewhisperer to keep using SSO connection in the background
* small fix

* localize redirect URL

* fix typo

Co-authored-by: andrewyuq <[email protected]>

* (codewhisperer) sso display name (aws#1074)

* sso display name

* updade display format

* both SSO/Sono will show Connected with...

Co-authored-by: andrewyuq <[email protected]>

* (codewhisperer) fix test class CodeWhispererClientTest (aws#1072)

* fix test class CodeWhispererClientTest

* * fix sigv4 client pointing to gamma
* add comment
* fix naming convention

Co-authored-by: andrewyuq <[email protected]>

* (codewhisperer) add telemetry for login click events (aws#1081)

* (codewhisperer) SSO connection expire fix (aws#1075)

* fix when SSO connection expires, login will never try reauth and return the same one

* fix broken test

* update comment

* Addressing the comment

* Fix tests

Co-authored-by: andrewyuq <[email protected]>
Co-authored-by: yuxqiang <[email protected]>

* (codewhisperer) fix broken code scan test class (aws#1069)

* fix broken test cases

* revert change added accidentally

* detekt fix

Co-authored-by: andrewyuq <[email protected]>
Co-authored-by: Richard Li <[email protected]>

* (feat codewhisperer) sso ux tooltip  (aws#989)

* cwspr tooltip

* anchor tooltip location to dev tool tab component

* remove TODO

* detekt fix

* replace description text

* Update jetbrains-core/src/software/aws/toolkits/jetbrains/core/explorer/AwsToolkitExplorerToolWindow.kt

Co-authored-by: Richard Li <[email protected]>

* Address comments

Co-authored-by: yuxqiang <[email protected]>
Co-authored-by: andrewyuq <[email protected]>
Co-authored-by: Richard Li <[email protected]>

* (codewhisperer) UX improvement SSO expire (aws#1076)

* when user try to reauth with given conneciton, fill in the required metadata in the login dialog for users

* detekt fix

* detekt fix

* NPE fix

* detekt fix

* when user try to reauth with given conneciton, fill in the required metadata in the login dialog for users

* detekt fix

* detekt fix

* NPE fix

* detekt fix

* Address comments

* Sneak in two text changes

* One more text change

Co-authored-by: andrewyuq <[email protected]>
Co-authored-by: yuxqiang <[email protected]>

* Fix test integration failures

Co-authored-by: aws-toolkit-automation <43144436+aws-toolkit-automation@users.noreply.github.com>
Co-authored-by: Will Lo <[email protected]>
Co-authored-by: Shao-Hua Lo <[email protected]>
Co-authored-by: Richard Li <[email protected]>
Co-authored-by: Richard Li <[email protected]>
  • Loading branch information
6 people authored Nov 15, 2022
1 parent dc88940 commit 20c1ad7
Show file tree
Hide file tree
Showing 83 changed files with 4,580 additions and 1,347 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ package software.aws.toolkits.core.credentials

import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider
import software.amazon.awssdk.auth.token.credentials.SdkTokenProvider
import software.aws.toolkits.resources.message

enum class CredentialType {
StaticProfile,
Expand Down Expand Up @@ -88,7 +89,7 @@ interface ToolkitAuthenticationProvider {

class ToolkitCredentialsProvider(
val identifier: CredentialIdentifier,
delegate: AwsCredentialsProvider
val delegate: AwsCredentialsProvider
) : ToolkitAuthenticationProvider, AwsCredentialsProvider by delegate {
override val id: String = identifier.id
override val displayName = identifier.displayName
Expand All @@ -112,9 +113,19 @@ class ToolkitCredentialsProvider(

interface ToolkitBearerTokenProviderDelegate : SdkTokenProvider, ToolkitAuthenticationProvider

class ToolkitBearerTokenProvider(delegate: ToolkitBearerTokenProviderDelegate) : SdkTokenProvider by delegate, ToolkitAuthenticationProvider by delegate {
class ToolkitBearerTokenProvider(val delegate: ToolkitBearerTokenProviderDelegate) : SdkTokenProvider by delegate, ToolkitAuthenticationProvider by delegate {
companion object {
fun identifier(startUrl: String) = "sso;$startUrl"
fun displayName(startUrl: String) = "SSO ($startUrl)"

// TODO: For AWS Builder ID, we only have startUrl for now instead of each users' metadata data i.e. Email address
fun displayName(startUrl: String) = if (startUrl == SONO_URL) {
message("aws_builder_id.service_name")
} else {
"SSO (${extractOrgID(startUrl)})"
}
}
}

private const val SONO_URL = "https://view.awsapps.com/start"

private fun extractOrgID(startUrl: String) = startUrl.removePrefix("https://").removeSuffix(".awsapps.com/start")
4 changes: 2 additions & 2 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[versions]
apacheCommons = "2.8.0"
assertJ = "3.20.2" # Upgrading leads to SAM errors: https://youtrack.jetbrains.com/issue/KT-17765
awsSdk = "2.17.289"
awsSdk = "2.17.292"
commonmark = "0.17.1"
detekt = "1.21.0"
intellijGradle = "1.9.0"
Expand All @@ -18,7 +18,7 @@ kotlinCoroutines = "1.6.4"
mockito = "4.6.1"
mockitoKotlin = "4.0.0"
mockk = "1.13.2"
telemetryGenerator = "1.0.75"
telemetryGenerator = "1.0.79"
testLogger = "3.1.0"
testRetry = "1.2.1"
slf4j = "1.7.36"
Expand Down
51 changes: 51 additions & 0 deletions jetbrains-core/assets/WelcomeToCodeWhisperer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
## Using Amazon CodeWhisperer

CodeWhisperer uses machine learning to generate code suggestions from the existing code and comments in your IDE.
Supported languages include: **Java, Python, JavaScript, C# and TypeScript**.

---

### Navigating with the tab and arrow keys

As you enter your code, CodeWhisperer will offer inline code suggestions. Use **TAB to accept** a suggestion.
CodeWhisperer may provide multiple suggestions to choose from. Use **[left arrow] and [right arrow] to navigate**
between suggestions.

If you don’t like the suggestions you see, keep typing (or hit ESC). The suggestions will disappear, and
CodeWhisperer will generate new ones at a later point based on the additional context.

<img src="example.gif" style="max-height:400px;" alt="example">

---

### Requesting suggestions manually

You can request suggestions at any time. Use **Option-C** on Mac or **ALT-C** on Windows. After you receive
suggestions, use TAB to accept and arrow keys to navigate.

---

### Getting the best recommendations

For best results, follow these practices.
* Give CodeWhisperer something to work with. The more code your file contains, the more context CodeWhisperer
has for generating recommendations.
* Write descriptive comments. “Function to upload a file to S3” will get better results than “Upload a file”.
* Specify the libraries you prefer by using import statements.
* Use descriptive names for variable and functions. A function called “upload_file_to_S3” will get better results
than a function called “file_upload”.
* Break down complex tasks into simpler tasks.

---

### Further reading

To learn more about working with CodeWhisperer and JetBrains, see [Amazon CodeWhisperer
for JetBrains and the AWS Toolkit](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/codewhisperer.html).

---

### Providing feedback

CodeWhisperer is in preview. Let us know what you think by sharing feedback (using the AWS Toolkit feedback button)
or by reaching out to [[email protected]](mailto:[email protected]).
Binary file added jetbrains-core/assets/example.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
12 changes: 12 additions & 0 deletions jetbrains-core/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,18 @@ tasks.jar {
}
}

val codewhispererReadmeAssets = tasks.register<Sync>("codewhispererReadmeAssets") {
from("${project.projectDir}/assets")
into("$buildDir/assets")
}

tasks.prepareSandbox {
dependsOn(codewhispererReadmeAssets)
from(codewhispererReadmeAssets) {
into("aws-toolkit-jetbrains/assets")
}
}

tasks.testJar {
// classpath.index is a duplicated
duplicatesStrategy = DuplicatesStrategy.INCLUDE
Expand Down
7 changes: 4 additions & 3 deletions jetbrains-core/resources/META-INF/plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,9 @@ with what features/services are supported.
<applicationService serviceImplementation="software.aws.toolkits.jetbrains.services.codewhisperer.inlay.CodeWhispererInlayManager"/>
<applicationService serviceImplementation="software.aws.toolkits.jetbrains.services.codewhisperer.service.CodeWhispererLicenseInfoManager"/>
<projectService serviceImplementation="software.aws.toolkits.jetbrains.services.codewhisperer.toolwindow.CodeWhispererCodeReferenceManager"/>
<applicationService serviceImplementation="software.aws.toolkits.jetbrains.services.codewhisperer.credentials.CodeWhispererClientManager"/>
<projectService serviceInterface="software.aws.toolkits.jetbrains.services.codewhisperer.credentials.CodeWhispererClientAdaptor"
serviceImplementation="software.aws.toolkits.jetbrains.services.codewhisperer.credentials.CodeWhispererClientAdaptorImpl"/>
<projectService serviceImplementation="software.aws.toolkits.jetbrains.services.codewhisperer.credentials.CodeWhispererProfileSwitchingDialog"/>

<projectService serviceImplementation="software.aws.toolkits.jetbrains.core.explorer.devToolsTab.DevToolsToolWindow"/>
<projectService serviceImplementation="software.aws.toolkits.jetbrains.core.explorer.AwsToolkitExplorerToolWindow"/>
Expand Down Expand Up @@ -278,7 +280,7 @@ with what features/services are supported.
<applicationConfigurable parentId="aws" key="aws.settings.dynamic_resources_configurable.title" instance="software.aws.toolkits.jetbrains.settings.DynamicResourcesConfigurable" />
<applicationConfigurable id="aws.experiments" parentId="aws" instance="software.aws.toolkits.jetbrains.core.experiments.ExperimentConfigurable"/>
<applicationConfigurable id="aws.tools" parentId="aws" instance="software.aws.toolkits.jetbrains.core.tools.ToolConfigurable"/>
<applicationConfigurable id="aws.codewhisperer" parentId="aws" instance="software.aws.toolkits.jetbrains.services.codewhisperer.settings.CodeWhispererConfigurable"/>
<projectConfigurable id="aws.codewhisperer" parentId="aws" instance="software.aws.toolkits.jetbrains.services.codewhisperer.settings.CodeWhispererConfigurable"/>

<programRunner implementation="software.aws.toolkits.jetbrains.services.lambda.execution.sam.SamInvokeRunner"/>
<programRunner implementation="software.aws.toolkits.jetbrains.services.lambda.execution.remote.RemoteLambdaRunner"/>
Expand Down Expand Up @@ -360,7 +362,6 @@ with what features/services are supported.
<sdk.clientCustomizer implementation="software.aws.toolkits.jetbrains.services.codewhisperer.util.CodeWhispererEndpointCustomizer"/>

<!-- Experiments -->
<experiment implementation="software.aws.toolkits.jetbrains.services.codewhisperer.experiment.CodeWhispererExperiment"/>
<experiment implementation="software.aws.toolkits.jetbrains.services.ecs.EcsExecExperiment"/>
<experiment implementation="software.aws.toolkits.jetbrains.services.dynamic.JsonResourceModificationExperiment"/>

Expand Down
34 changes: 0 additions & 34 deletions jetbrains-core/resources/codewhisperer/WelcomeToCodeWhisperer.md

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import com.intellij.openapi.extensions.PluginId
import com.intellij.openapi.util.registry.Registry

object AwsToolkit {
private const val PLUGIN_ID = "aws.toolkit"
const val PLUGIN_ID = "aws.toolkit"

val PLUGIN_VERSION: String by lazy {
PluginManagerCore.getPlugin(PluginId.getId(PLUGIN_ID))?.version ?: "Unknown"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ package software.aws.toolkits.jetbrains.core.credentials

import com.intellij.icons.AllIcons
import com.intellij.ide.DataManager
import com.intellij.openapi.actionSystem.ActionManager
import com.intellij.openapi.actionSystem.AnAction
import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.actionSystem.DataContext
Expand Down Expand Up @@ -209,7 +210,10 @@ class ToolkitConnectionComboBoxAction(private val project: Project) : ComboBoxAc
e.presentation.text = logic.displayValue()
e.presentation.description = logic.tooltip()
} else {
e.presentation.text = active?.label ?: message("settings.credentials.none_selected")
e.presentation.text = active?.label?.let {
"Connected with $it"
} ?: message("settings.credentials.none_selected")

e.presentation.description = null
}
}
Expand Down Expand Up @@ -282,14 +286,11 @@ class CredsComboBoxActionGroup(private val project: Project) : DefaultActionGrou

return if (activeConnection is AwsBearerTokenConnection) {
ssoSelectorGroup
}
// TODO: uncomment to enable action
// else if (activeConnection == null) {
// arrayOf(
// ActionManager.getInstance().getAction("aws.toolkit.toolwindow.explorer.newConnection")
// )
// }
else {
} else if (activeConnection == null) {
arrayOf(
ActionManager.getInstance().getAction("aws.toolkit.toolwindow.explorer.newConnection")
)
} else {
profileRegionSelectorGroup
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,27 @@ import com.intellij.openapi.components.PersistentStateComponent
import com.intellij.openapi.components.State
import com.intellij.openapi.components.Storage
import com.intellij.openapi.project.Project
import software.aws.toolkits.jetbrains.services.codewhisperer.credentials.CodeWhispererProfileSwitchingDialog
import software.aws.toolkits.jetbrains.utils.notifyInfo
import software.aws.toolkits.resources.message

// TODO: unify with AwsConnectionManager
@State(name = "connectionManager", storages = [Storage("aws.xml")])
class DefaultToolkitConnectionManager(private val project: Project) : ToolkitConnectionManager, PersistentStateComponent<ToolkitConnectionManagerState> {
private var connection: ToolkitConnection? = null

/**
* For case when user switch from SSO/AWS ID to IAM credentials(SSO/AWS ID -> IAM), this keep use being able to invoke SSO based services
* while primary connectino shown in the UX is their IAM credentials.
* The states of [connection] and [secondaryConnection] will be one of above as followed
* (1) [connection] is IAM; [secondaryConnection] is SSO
* (2) [connection] is SSO; [secondaryConnection] is null
* (3) [connection] is null; [secondaryConnection] is null
* Note: We NEVER store 2 SSO connection at the same time, i.e. [connection] is SSO; [secondaryConnection] is SSO
*/
//
private var secondaryConnection: AwsBearerTokenConnection? = null

private val defaultConnection: ToolkitConnection?
get() {
if (CredentialManager.getInstance().getCredentialIdentifiers().isNotEmpty()) {
Expand All @@ -23,6 +39,12 @@ class DefaultToolkitConnectionManager(private val project: Project) : ToolkitCon

override fun activeConnection() = connection ?: defaultConnection

override fun activeBearerConnection(): AwsBearerTokenConnection? {
if (connection is AwsBearerTokenConnection) return connection as AwsBearerTokenConnection
if (secondaryConnection is AwsBearerTokenConnection) return secondaryConnection
return null
}

override fun getState() = ToolkitConnectionManagerState(
connection?.id
)
Expand All @@ -33,9 +55,37 @@ class DefaultToolkitConnectionManager(private val project: Project) : ToolkitCon
}
}

override fun switchConnection(connection: ToolkitConnection) {
if (this.connection != connection) {
this.connection = connection
override fun switchConnection(connection: ToolkitConnection?) {
val oldConnection = this.connection
val newConnection = connection

if (oldConnection != newConnection) {
this.connection = newConnection

// TODO: ugly, any cleaner way to do this?
// case 0: logout
if (newConnection == null) {
val connections = ToolkitAuthManager.getInstance().listConnections()
if (connections.isNotEmpty()) {
this.connection = defaultConnection ?: ToolkitAuthManager.getInstance().listConnections()[0]
} else {
this.connection = defaultConnection
}
}

// case 1: user is able to keep using SSO based credential when they are switching back to IAM i.e. SSO/AWS ID -> IAM
else if (oldConnection is AwsBearerTokenConnection && newConnection is AwsCredentialConnection) {
if (CodeWhispererProfileSwitchingDialog.getInstance(project).showDialogIfNeeded(oldConnection)) {
secondaryConnection = oldConnection
secondaryConnection?.let { notifyInfo(message("codewhisperer.credential.login.yes_no.switching_profile_notification.title", it.label)) }
}
}

// case 2: when secondaryConnection is working and user add another new SSO based credential, we only allow 1 to run at a time
else if (newConnection is AwsBearerTokenConnection && secondaryConnection is AwsBearerTokenConnection) {
secondaryConnection = null
}

project.messageBus.syncPublisher(ToolkitConnectionManagerListener.TOPIC).activeConnectionChanged(connection)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,16 @@ package software.aws.toolkits.jetbrains.core.credentials

import software.aws.toolkits.core.TokenConnectionSettings
import software.aws.toolkits.core.credentials.ToolkitBearerTokenProvider
import software.aws.toolkits.jetbrains.core.credentials.sso.SsoLoginCallback
import software.aws.toolkits.jetbrains.core.credentials.sso.bearer.BearerTokenPrompt
import software.aws.toolkits.jetbrains.core.credentials.sso.bearer.InteractiveBearerTokenProvider
import software.aws.toolkits.jetbrains.core.region.AwsRegionProvider

class ManagedBearerSsoConnection(
val startUrl: String,
val region: String,
override val scopes: List<String>,
private val prompt: SsoPrompt = SsoPrompt
private val prompt: SsoLoginCallback = BearerTokenPrompt
) : BearerSsoConnection {
override val id: String = ToolkitBearerTokenProvider.identifier(startUrl)
override val label: String = ToolkitBearerTokenProvider.displayName(startUrl)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// Copyright 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

package software.aws.toolkits.jetbrains.core.credentials

import com.intellij.ide.BrowserUtil
import com.intellij.openapi.project.Project
import com.intellij.openapi.ui.DialogWrapper.CANCEL_EXIT_CODE
import com.intellij.openapi.ui.DialogWrapper.OK_EXIT_CODE
import com.intellij.openapi.ui.Messages
import software.aws.toolkits.resources.message
import java.net.URI

class SsoConnectionExpiredDialog(private val project: Project, private val connection: ToolkitConnection?) {
fun show() {
val res = Messages.showYesNoCancelDialog(
project,
message("toolkit.sso_expire.dialog_message"),
message("toolkit.sso_expire.dialog.title"),
message("toolkit.sso_expire.dialog.yes_button"),
message("toolkit.sso_expire.dialog.no_button"),
message("toolkit.sso_expire.dialog.cancel_button"),
Messages.getWarningIcon()
)

when (res) {
OK_EXIT_CODE -> { ToolkitAddConnectionDialog(project, connection).show() }
CANCEL_EXIT_CODE -> {
// TODO: update if needed
BrowserUtil.browse(URI("https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/codewhisperer.html"))
}
else -> {}
}
}
}
Loading

0 comments on commit 20c1ad7

Please sign in to comment.