Skip to content

Commit 69b57cc

Browse files
authored
Merge pull request #1075 from Will-ShaoHua/feature/vector
2 parents e2a20c4 + 1b7c68a commit 69b57cc

22 files changed

+895
-36
lines changed

docs/api.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# AWS Toolkit API
2+
3+
Details about any publicly accessible functionalities exposed through [extension commands](https://code.visualstudio.com/api/references/vscode-api#commands) or [exported APIs](https://code.visualstudio.com/api/references/vscode-api#extensions).
4+
5+
## Pseudo (Internal-only) API
6+
7+
### Commands
8+
9+
#### `aws.codeWhisperer.connect`
10+
11+
**Signature**: _async (startUrl?: string, region?: string, customizationArn?: string, customizationName?: string, customizationDescription?: string) => Promise<void>_
12+
13+
Shortcut command to directly connect to Identity Center or prompt start URL entry, as well as set a customization for CodeWhisperer requests.
14+
15+
This command supports two sets of arguments:
16+
17+
- startUrl and region. If both arguments are provided they will be used, otherwise the command prompts for them interactively.
18+
- customization{Arn, Name, Description}. If at least customizationArn is provided, the command selects this customization.

package.nls.json

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -216,5 +216,14 @@
216216
"AWS.walkthrough.gettingStarted.connect": "Connect to AWS",
217217
"AWS.walkthrough.gettingStarted.changeRegions": "Change AWS Regions",
218218
"AWS.walkthrough.gettingStarted.setupToolchain": "Configure your toolchain",
219-
"AWS.command.codewhisperer.title": "CodeWhisperer Invoke Service"
219+
"AWS.command.codewhisperer.title": "CodeWhisperer Invoke Service",
220+
"AWS.explorerNode.selectCustomization.label": "Select a Customization",
221+
"AWS.codewhisperer.customization.base.label": "CodeWhisperer foundation (Default)",
222+
"AWS.codewhisperer.customization.base.description": "default",
223+
"AWS.codewhisperer.customization.base.detail": "Receive suggestions from CodeWhisperer base model",
224+
"AWS.codewhisperer.customization.selected": "Connected",
225+
"AWS.codewhisperer.customization.quickPick.title": "Select a Customization",
226+
"AWS.codewhisperer.customization.quickPick.placeholder": "You have access to the following customizations",
227+
"AWS.codewhisperer.customization.notification.new_customizations.select": "Select Customization",
228+
"AWS.codewhisperer.customization.notification.new_customizations.learn_more": "Learn More"
220229
}

src/codewhisperer/activation.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ import {
3333
showIntroduction,
3434
reconnect,
3535
refreshStatusBar,
36+
selectCustomizationPrompt,
37+
notifyNewCustomizationsCmd,
38+
connectWithCustomization,
3639
} from './commands/basicCommands'
3740
import { sleep } from '../shared/utilities/timeoutUtils'
3841
import { ReferenceLogViewProvider } from './service/referenceLogViewProvider'
@@ -49,6 +52,7 @@ import { AuthUtil } from './util/authUtil'
4952
import { ImportAdderProvider } from './service/importAdderProvider'
5053
import { TelemetryHelper } from './util/telemetryHelper'
5154
import { openUrl } from '../shared/utilities/vsCodeUtils'
55+
import { notifyNewCustomizations } from './util/customizationUtil'
5256
import { CodeWhispererCommandBackend, CodeWhispererCommandDeclarations } from './commands/gettingStartedPageCommands'
5357
const performance = globalThis.performance ?? require('perf_hooks').performance
5458

@@ -155,6 +159,8 @@ export async function activate(context: ExtContext): Promise<void> {
155159
}),
156160
// show introduction
157161
showIntroduction.register(),
162+
// direct CodeWhisperer connection setup with customization
163+
connectWithCustomization.register(),
158164
// toggle code suggestions
159165
toggleCodeSuggestions.register(context.extensionContext.globalState),
160166
// enable code suggestions
@@ -177,6 +183,10 @@ export async function activate(context: ExtContext): Promise<void> {
177183
Commands.register({ id: 'aws.codeWhisperer', autoconnect: true }, async () => {
178184
invokeRecommendation(vscode.window.activeTextEditor as vscode.TextEditor, client, await getConfigEntry())
179185
}),
186+
// select customization
187+
selectCustomizationPrompt.register(),
188+
// notify new customizations
189+
notifyNewCustomizationsCmd.register(),
180190
/**
181191
* On recommendation acceptance
182192
*/
@@ -210,6 +220,9 @@ export async function activate(context: ExtContext): Promise<void> {
210220
if (auth.isConnectionExpired()) {
211221
auth.showReauthenticatePrompt()
212222
}
223+
if (auth.isValidEnterpriseSsoInUse()) {
224+
await notifyNewCustomizations()
225+
}
213226

214227
function activateSecurityScan() {
215228
context.extensionContext.subscriptions.push(

src/codewhisperer/client/codewhisperer.ts

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,22 @@
22
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
33
* SPDX-License-Identifier: Apache-2.0
44
*/
5-
import { AWSError, Service } from 'aws-sdk'
6-
import apiConfig = require('./service-2.json')
7-
import userApiConfig = require('./user-service-2.json')
5+
import { AWSError, Credentials, Service } from 'aws-sdk'
86
import globals from '../../shared/extensionGlobals'
97
import * as CodeWhispererClient from './codewhispererclient'
108
import * as CodeWhispererUserClient from './codewhispereruserclient'
9+
import { ListAvailableCustomizationsResponse, SendTelemetryEventRequest } from './codewhispereruserclient'
1110
import * as CodeWhispererConstants from '../models/constants'
1211
import { ServiceOptions } from '../../shared/awsClientBuilder'
1312
import { isCloud9 } from '../../shared/extensionUtilities'
1413
import { CodeWhispererSettings } from '../util/codewhispererSettings'
1514
import { PromiseResult } from 'aws-sdk/lib/request'
16-
import { Credentials } from 'aws-sdk'
1715
import { AuthUtil } from '../util/authUtil'
1816
import { isSsoConnection } from '../../auth/connection'
17+
import { pageableToCollection } from '../../shared/utilities/collectionUtils'
18+
import apiConfig = require('./service-2.json')
19+
import userApiConfig = require('./user-service-2.json')
1920
import { session } from '../util/codeWhispererSession'
20-
import { SendTelemetryEventRequest } from './codewhispereruserclient'
2121
import { getLogger } from '../../shared/logger'
2222

2323
export type ProgrammingLanguage = Readonly<
@@ -192,6 +192,13 @@ export class DefaultCodeWhispererClient {
192192
.promise()
193193
}
194194

195+
public async listAvailableCustomizations(): Promise<ListAvailableCustomizationsResponse[]> {
196+
const client = await this.createUserSdkClient()
197+
const requester = async (request: CodeWhispererUserClient.ListAvailableCustomizationsRequest) =>
198+
client.listAvailableCustomizations(request).promise()
199+
return pageableToCollection(requester, {}, 'nextToken').promise()
200+
}
201+
195202
public async sendTelemetryEvent(request: SendTelemetryEventRequest) {
196203
const requestWithOptOut: SendTelemetryEventRequest = {
197204
...request,

src/codewhisperer/client/user-service-2.json

Lines changed: 69 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,21 @@
7575
{ "shape": "AccessDeniedException" }
7676
]
7777
},
78+
"ListAvailableCustomizations": {
79+
"name": "ListAvailableCustomizations",
80+
"http": {
81+
"method": "POST",
82+
"requestUri": "/"
83+
},
84+
"input": { "shape": "ListAvailableCustomizationsRequest" },
85+
"output": { "shape": "ListAvailableCustomizationsResponse" },
86+
"errors": [
87+
{ "shape": "ThrottlingException" },
88+
{ "shape": "InternalServerException" },
89+
{ "shape": "ValidationException" },
90+
{ "shape": "AccessDeniedException" }
91+
]
92+
},
7893
"ListCodeAnalysisFindings": {
7994
"name": "ListCodeAnalysisFindings",
8095
"http": {
@@ -164,6 +179,7 @@
164179
"type": "structure",
165180
"required": ["programmingLanguage", "acceptedCharacterCount", "totalCharacterCount", "timestamp"],
166181
"members": {
182+
"customizationArn": { "shape": "ResourceArn" },
167183
"programmingLanguage": { "shape": "ProgrammingLanguage" },
168184
"acceptedCharacterCount": { "shape": "SensitiveInteger" },
169185
"totalCharacterCount": { "shape": "SensitiveInteger" },
@@ -240,6 +256,34 @@
240256
"kmsKeyArn": { "shape": "ResourceArn" }
241257
}
242258
},
259+
"Customization": {
260+
"type": "structure",
261+
"required": ["arn"],
262+
"members": {
263+
"arn": { "shape": "ResourceArn" },
264+
"name": { "shape": "CustomizationName" },
265+
"description": { "shape": "Description" }
266+
}
267+
},
268+
"CustomizationName": {
269+
"type": "string",
270+
"max": 100,
271+
"min": 1,
272+
"pattern": "[a-zA-Z][a-zA-Z0-9_-]*"
273+
},
274+
"Customizations": {
275+
"type": "list",
276+
"member": { "shape": "Customization" }
277+
},
278+
"Description": {
279+
"type": "string",
280+
"max": 256,
281+
"min": 0
282+
},
283+
"Double": {
284+
"type": "double",
285+
"box": true
286+
},
243287
"FileContext": {
244288
"type": "structure",
245289
"required": ["leftFileContent", "rightFileContent", "filename", "programmingLanguage"],
@@ -276,7 +320,8 @@
276320
"maxResults": { "shape": "GenerateCompletionsRequestMaxResultsInteger" },
277321
"nextToken": { "shape": "GenerateCompletionsRequestNextTokenString" },
278322
"referenceTrackerConfiguration": { "shape": "ReferenceTrackerConfiguration" },
279-
"supplementalContexts": { "shape": "SupplementalContextList" }
323+
"supplementalContexts": { "shape": "SupplementalContextList" },
324+
"customizationArn": { "shape": "ResourceArn" }
280325
}
281326
},
282327
"GenerateCompletionsRequestMaxResultsInteger": {
@@ -351,6 +396,27 @@
351396
"fault": true,
352397
"retryable": { "throttling": false }
353398
},
399+
"ListAvailableCustomizationsRequest": {
400+
"type": "structure",
401+
"members": {
402+
"maxResults": { "shape": "ListAvailableCustomizationsRequestMaxResultsInteger" },
403+
"nextToken": { "shape": "Base64EncodedPaginationToken" }
404+
}
405+
},
406+
"ListAvailableCustomizationsRequestMaxResultsInteger": {
407+
"type": "integer",
408+
"box": true,
409+
"max": 100,
410+
"min": 1
411+
},
412+
"ListAvailableCustomizationsResponse": {
413+
"type": "structure",
414+
"required": ["customizations"],
415+
"members": {
416+
"customizations": { "shape": "Customizations" },
417+
"nextToken": { "shape": "Base64EncodedPaginationToken" }
418+
}
419+
},
354420
"ListCodeAnalysisFindingsRequest": {
355421
"type": "structure",
356422
"required": ["jobId", "codeAnalysisFindingsSchema"],
@@ -598,6 +664,7 @@
598664
"requestId": { "shape": "UUID" },
599665
"programmingLanguage": { "shape": "ProgrammingLanguage" },
600666
"modificationPercentage": { "shape": "SensitiveDouble" },
667+
"customizationArn": { "shape": "ResourceArn" },
601668
"timestamp": { "shape": "Timestamp" }
602669
}
603670
},
@@ -615,6 +682,7 @@
615682
"members": {
616683
"sessionId": { "shape": "UUID" },
617684
"requestId": { "shape": "UUID" },
685+
"customizationArn": { "shape": "ResourceArn" },
618686
"programmingLanguage": { "shape": "ProgrammingLanguage" },
619687
"completionType": { "shape": "CompletionType" },
620688
"suggestionState": { "shape": "SuggestionState" },

src/codewhisperer/commands/basicCommands.ts

Lines changed: 55 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,24 @@ import { telemetry } from '../../shared/telemetry/telemetry'
88
import { ExtContext } from '../../shared/extensions'
99
import { Commands } from '../../shared/vscode/commands2'
1010
import * as CodeWhispererConstants from '../models/constants'
11-
import { getLogger } from '../../shared/logger'
1211
import { DefaultCodeWhispererClient } from '../client/codewhisperer'
1312
import { startSecurityScanWithProgress, confirmStopSecurityScan } from './startSecurityScan'
1413
import { SecurityPanelViewProvider } from '../views/securityPanelViewProvider'
1514
import { codeScanState } from '../models/model'
15+
import { connectToEnterpriseSso, getStartUrl } from '../util/getStartUrl'
1616
import { showConnectionPrompt } from '../util/showSsoPrompt'
1717
import { ReferenceLogViewProvider } from '../service/referenceLogViewProvider'
1818
import { AuthUtil } from '../util/authUtil'
1919
import { isCloud9 } from '../../shared/extensionUtilities'
2020
import { InlineCompletionService } from '../service/inlineCompletionService'
2121
import { openUrl } from '../../shared/utilities/vsCodeUtils'
22+
import {
23+
getPersistedCustomizations,
24+
notifyNewCustomizations,
25+
selectCustomization,
26+
showCustomizationPrompt,
27+
} from '../util/customizationUtil'
28+
import { get, set } from '../util/commonUtil'
2229
import { CodeWhispererCommandDeclarations } from '../commands/gettingStartedPageCommands'
2330
import { getIcon } from '../../shared/icons'
2431
import { localize } from '../../shared/utilities/vsCodeUtils'
@@ -38,7 +45,7 @@ export const toggleCodeSuggestions = Commands.declare(
3845
})
3946
}
4047
)
41-
/*
48+
/*
4249
createGettingStartedNode(Learn) will be a childnode of CodeWhisperer
4350
onClick on this "Learn" Node will open the Learn CodeWhisperer Page.
4451
*/
@@ -98,28 +105,54 @@ export const showSecurityScan = Commands.declare(
98105
}
99106
)
100107

108+
export const selectCustomizationPrompt = Commands.declare('aws.codeWhisperer.selectCustomization', () => async () => {
109+
telemetry.ui_click.emit({ elementId: 'cw_selectCustomization_Cta' })
110+
showCustomizationPrompt().then()
111+
})
112+
101113
export const reconnect = Commands.declare('aws.codeWhisperer.reconnect', () => async () => {
102114
await AuthUtil.instance.reauthenticate()
103115
})
104116

105-
export function get(key: string, context: vscode.Memento): any {
106-
return context.get(key)
107-
}
108-
109-
export async function set(key: string, value: any, context: vscode.Memento): Promise<void> {
110-
await context.update(key, value).then(
111-
() => {},
112-
error => {
113-
getLogger().verbose(`Failed to update global state: ${error}`)
114-
}
115-
)
116-
}
117-
118117
export const showSsoSignIn = Commands.declare('aws.codeWhisperer.sso', () => async () => {
119118
telemetry.ui_click.emit({ elementId: 'cw_signUp_Cta' })
120119
await showConnectionPrompt()
121120
})
122121

122+
// Shortcut command to directly connect to Identity Center or prompt start URL entry
123+
// It can optionally set a customization too.
124+
export const connectWithCustomization = Commands.declare(
125+
'aws.codeWhisperer.connect',
126+
() =>
127+
async (
128+
startUrl?: string,
129+
region?: string,
130+
customizationArn?: string,
131+
customizationName?: string,
132+
customizationDescription?: string
133+
) => {
134+
// This command supports two sets of arguments:
135+
// * startUrl and region. If both arguments are provided they will be used, otherwise
136+
// the command prompts for them interactively.
137+
// * customization{Arn, Name, Description}. If at least customizationArn is provided,
138+
// the command selects this customization.
139+
if (startUrl && region) {
140+
await connectToEnterpriseSso(startUrl, region)
141+
} else {
142+
await getStartUrl()
143+
}
144+
if (customizationArn) {
145+
const match = getPersistedCustomizations().find(c => c.arn == customizationArn)
146+
const customization = {
147+
arn: customizationArn,
148+
name: customizationName ?? match?.name ?? 'unknown',
149+
description: customizationDescription ?? match?.description ?? 'unknown',
150+
}
151+
await selectCustomization(customization)
152+
}
153+
}
154+
)
155+
123156
export const showLearnMore = Commands.declare('aws.codeWhisperer.learnMore', () => async () => {
124157
telemetry.ui_click.emit({ elementId: 'cw_learnMore_Cta' })
125158
openUrl(vscode.Uri.parse(CodeWhispererConstants.learnMoreUriGeneral))
@@ -149,3 +182,10 @@ export const refreshStatusBar = Commands.declare(
149182
}
150183
}
151184
)
185+
186+
export const notifyNewCustomizationsCmd = Commands.declare(
187+
{ id: 'aws.codeWhisperer.notifyNewCustomizations', logging: false },
188+
() => () => {
189+
notifyNewCustomizations().then()
190+
}
191+
)

src/codewhisperer/explorer/codewhispererChildrenNodes.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,10 @@ import {
1414
showLearnMore,
1515
showFreeTierLimit,
1616
reconnect,
17+
selectCustomizationPrompt,
1718
} from '../commands/basicCommands'
1819
import { codeScanState } from '../models/model'
20+
import { getNewCustomizationAvailable, getSelectedCustomization } from '../util/customizationUtil'
1921

2022
export const createEnableCodeSuggestionsNode = () =>
2123
enableCodeSuggestions.build().asTreeNode({
@@ -88,3 +90,15 @@ export const createFreeTierLimitMetNode = () => {
8890
description: localize('AWS.explorerNode.freeTierLimitMet.tooltip', `paused until ${nextMonth}`),
8991
})
9092
}
93+
94+
export const createSelectCustomizationNode = () => {
95+
const newCustomizationsAvailable = getNewCustomizationAvailable()
96+
const selectedCustomization = getSelectedCustomization()
97+
const newText = newCustomizationsAvailable ? 'new! ' : ''
98+
99+
return selectCustomizationPrompt.build().asTreeNode({
100+
label: localize('AWS.explorerNode.selectCustomization.label', 'Select Customization'),
101+
iconPath: getIcon('vscode-extensions'),
102+
description: `${newText}${selectedCustomization.arn === '' ? '' : selectedCustomization.name}`,
103+
})
104+
}

0 commit comments

Comments
 (0)