Skip to content

Commit 667109c

Browse files
committed
codewhisperer: Allow endpoint/region overrides for dev mode
Problem: - It's not easy to change the codewhisperer endpoint when running tests against different versions of the api Solution: - Allow configuration via a setting for dev mode
1 parent 3d6d597 commit 667109c

File tree

7 files changed

+120
-57
lines changed

7 files changed

+120
-57
lines changed

CONTRIBUTING.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -342,6 +342,17 @@ Example:
342342
}
343343
```
344344

345+
Overrides specifically for CodeWhisperer/Amazon Q can be set using the `aws.dev.codewhispererService` setting. This is a JSON object consisting of keys/values required to override API calls to CodeWhisperer/Amazon Q: `region` and `endpoint`. If this setting is present, then all keys need to be explicitly provided.
346+
347+
Example:
348+
349+
```json
350+
"aws.dev.codewhispererService": {
351+
"region": "us-west-2",
352+
"endpoint": "https://codewhisperer-gamma.example.com"
353+
}
354+
```
355+
345356
### Environment variables
346357

347358
Environment variables can be used to modify the behaviour of VSCode. The following are environment variables that can be used to configure the extension:

src/amazonqFeatureDev/client/featureDev.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,18 +15,19 @@ import * as FeatureDevProxyClient from './featuredevproxyclient'
1515
import apiConfig = require('./codewhispererruntime-2022-11-11.json')
1616
import { featureName } from '../constants'
1717
import { ApiError, ContentLengthError, UnknownApiError } from '../errors'
18-
import { endpoint, region } from '../../codewhisperer/models/constants'
1918
import { isAwsError, isCodeWhispererStreamingServiceException } from '../../shared/errors'
19+
import { getCodewhispererConfig } from '../../codewhisperer/client/codewhisperer'
2020

2121
// Create a client for featureDev proxy client based off of aws sdk v2
2222
export async function createFeatureDevProxyClient(): Promise<FeatureDevProxyClient> {
2323
const bearerToken = await AuthUtil.instance.getBearerToken()
24+
const cwsprConfig = getCodewhispererConfig()
2425
return (await globals.sdkClientBuilder.createAwsService(
2526
Service,
2627
{
2728
apiConfig: apiConfig,
28-
region,
29-
endpoint,
29+
region: cwsprConfig.region,
30+
endpoint: cwsprConfig.endpoint,
3031
token: new Token({ token: bearerToken }),
3132
// SETTING TO 0 FOR BETA. RE-ENABLE FOR RE-INVENT
3233
maxRetries: 0,
@@ -42,9 +43,10 @@ export async function createFeatureDevProxyClient(): Promise<FeatureDevProxyClie
4243
// Create a client for featureDev streaming based off of aws sdk v3
4344
async function createFeatureDevStreamingClient(): Promise<CodeWhispererStreaming> {
4445
const bearerToken = await AuthUtil.instance.getBearerToken()
46+
const cwsprConfig = getCodewhispererConfig()
4547
const streamingClient = new CodeWhispererStreaming({
46-
region,
47-
endpoint,
48+
region: cwsprConfig.region,
49+
endpoint: cwsprConfig.endpoint,
4850
token: { token: bearerToken },
4951
// SETTING max attempts to 0 FOR BETA. RE-ENABLE FOR RE-INVENT
5052
// Implement exponential back off starting with a base of 500ms (500 + attempt^10)

src/codewhisperer/client/codewhisperer.ts

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ import {
1313
ListFeatureEvaluationsResponse,
1414
SendTelemetryEventRequest,
1515
} from './codewhispereruserclient'
16-
import * as CodeWhispererConstants from '../models/constants'
1716
import { ServiceOptions } from '../../shared/awsClientBuilder'
1817
import { hasVendedIamCredentials } from '../../auth/auth'
1918
import { CodeWhispererSettings } from '../util/codewhispererSettings'
@@ -31,6 +30,21 @@ import { getOptOutPreference } from '../util/commonUtil'
3130
import * as os from 'os'
3231
import { getClientId } from '../../shared/telemetry/util'
3332
import { extensionVersion } from '../../shared/vscode/env'
33+
import { DevSettings } from '../../shared/settings'
34+
35+
export interface CodeWhispererConfig {
36+
readonly region: string
37+
readonly endpoint: string
38+
}
39+
40+
export const defaultServiceConfig: CodeWhispererConfig = {
41+
region: 'us-east-1',
42+
endpoint: 'https://codewhisperer.us-east-1.amazonaws.com/',
43+
}
44+
45+
export function getCodewhispererConfig(): CodeWhispererConfig {
46+
return DevSettings.instance.getServiceConfig('codewhispererService', defaultServiceConfig)
47+
}
3448

3549
export type ProgrammingLanguage = Readonly<
3650
CodeWhispererClient.ProgrammingLanguage | CodeWhispererUserClient.ProgrammingLanguage
@@ -80,14 +94,14 @@ export type Imports = CodeWhispererUserClient.Imports
8094
export class DefaultCodeWhispererClient {
8195
private async createSdkClient(): Promise<CodeWhispererClient> {
8296
const isOptedOut = CodeWhispererSettings.instance.isOptoutEnabled()
83-
97+
const cwsprConfig = getCodewhispererConfig()
8498
return (await globals.sdkClientBuilder.createAwsService(
8599
Service,
86100
{
87101
apiConfig: apiConfig,
88-
region: CodeWhispererConstants.region,
102+
region: cwsprConfig.region,
89103
credentials: await AuthUtil.instance.getCredentials(),
90-
endpoint: CodeWhispererConstants.endpoint,
104+
endpoint: cwsprConfig.endpoint,
91105
onRequestSetup: [
92106
req => {
93107
if (req.operation === 'listRecommendations') {
@@ -123,12 +137,13 @@ export class DefaultCodeWhispererClient {
123137
session.setFetchCredentialStart()
124138
const bearerToken = await AuthUtil.instance.getBearerToken()
125139
session.setSdkApiCallStart()
140+
const cwsprConfig = getCodewhispererConfig()
126141
return (await globals.sdkClientBuilder.createAwsService(
127142
Service,
128143
{
129144
apiConfig: userApiConfig,
130-
region: CodeWhispererConstants.region,
131-
endpoint: CodeWhispererConstants.endpoint,
145+
region: cwsprConfig.region,
146+
endpoint: cwsprConfig.endpoint,
132147
credentials: new Credentials({ accessKeyId: 'xxx', secretAccessKey: 'xxx' }),
133148
onRequestSetup: [
134149
req => {

src/codewhisperer/models/constants.ts

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,6 @@
33
* SPDX-License-Identifier: Apache-2.0
44
*/
55

6-
/**
7-
* SDK Client
8-
*/
9-
export const endpoint = 'https://codewhisperer.us-east-1.amazonaws.com/'
10-
11-
export const region = 'us-east-1'
12-
136
/**
147
* Automated and manual trigger
158
*/

src/shared/clients/codecatalystClient.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ export const defaultServiceConfig: CodeCatalystConfig = {
4747
}
4848

4949
export function getCodeCatalystConfig(): CodeCatalystConfig {
50-
return DevSettings.instance.getCodeCatalystConfig(defaultServiceConfig)
50+
return DevSettings.instance.getServiceConfig('codecatalystService', defaultServiceConfig)
5151
}
5252

5353
export interface DevEnvironment extends CodeCatalyst.DevEnvironmentSummary {

src/shared/settings.ts

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import * as vscode from 'vscode'
77
import * as packageJson from '../../package.json'
88
import * as codecatalyst from './clients/codecatalystClient'
9+
import * as codewhisperer from '../codewhisperer/client/codewhisperer'
910
import { getLogger } from './logger'
1011
import { cast, FromDescriptor, Record, TypeConstructor, TypeDescriptor } from './utilities/typeConstructors'
1112
import { assertHasProps, ClassToInterfaceType, keys } from './utilities/tsUtils'
@@ -662,11 +663,18 @@ const devSettings = {
662663
renderDebugDetails: Boolean,
663664
endpoints: Record(String, String),
664665
codecatalystService: Record(String, String),
666+
codewhispererService: Record(String, String),
665667
ssoCacheDirectory: String,
666668
}
667669
type ResolvedDevSettings = FromDescriptor<typeof devSettings>
668670
type AwsDevSetting = keyof ResolvedDevSettings
669671

672+
type ServiceClients = keyof ServiceTypeMap
673+
interface ServiceTypeMap {
674+
codecatalystService: codecatalyst.CodeCatalystConfig
675+
codewhispererService: codewhisperer.CodeWhispererConfig
676+
}
677+
670678
/**
671679
* Developer settings are intended to be used by developers of this codebase for anything
672680
* that may be potentially useful during active development and/or testing. Examples include
@@ -721,14 +729,14 @@ export class DevSettings extends Settings.define('aws.dev', devSettings) {
721729
return Object.keys(this.activeSettings).length > 0
722730
}
723731

724-
public getCodeCatalystConfig(
725-
defaultConfig: codecatalyst.CodeCatalystConfig
726-
): Readonly<codecatalyst.CodeCatalystConfig> {
727-
const devSetting = 'codecatalystService'
728-
const devConfig = this.get(devSetting, {})
732+
public getServiceConfig<T extends ServiceClients>(
733+
devSetting: T,
734+
defaultConfig: ServiceTypeMap[T]
735+
): Readonly<ServiceTypeMap[T]> {
736+
const devConfig = this.get<ServiceClients>(devSetting, {})
729737

730738
if (Object.keys(devConfig).length === 0) {
731-
this.logCodeCatalystConfigOnce('default')
739+
this.logConfigOnce(devSetting, 'default')
732740
return defaultConfig
733741
}
734742

@@ -739,8 +747,8 @@ export class DevSettings extends Settings.define('aws.dev', devSettings) {
739747
throw ToolkitError.chain(err, `Dev setting '${devSetting}' has missing or invalid properties.`)
740748
}
741749

742-
this.logCodeCatalystConfigOnce(JSON.stringify(devConfig, undefined, 4))
743-
return devConfig as unknown as codecatalyst.CodeCatalystConfig
750+
this.logConfigOnce(devSetting, JSON.stringify(devConfig, undefined, 4))
751+
return devConfig as unknown as ServiceTypeMap[T]
744752
}
745753

746754
public override get<K extends AwsDevSetting>(key: K, defaultValue: ResolvedDevSettings[K]) {
@@ -770,8 +778,8 @@ export class DevSettings extends Settings.define('aws.dev', devSettings) {
770778
}
771779
}
772780

773-
private logCodeCatalystConfigOnce = onceChanged(val => {
774-
getLogger().info(`using CodeCatalyst service configuration: ${val}`)
781+
private logConfigOnce = onceChanged((serviceName, val) => {
782+
getLogger().info(`using ${serviceName} service configuration: ${val}`)
775783
})
776784

777785
static #instance: DevSettings

src/test/shared/settingsConfiguration.test.ts

Lines changed: 62 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -313,41 +313,75 @@ describe('DevSetting', function () {
313313
})
314314
})
315315

316-
describe('getCodeCatalystConfig()', function () {
317-
const devSettingName = 'aws.dev.codecatalystService'
318-
const defaultConfig = {
319-
region: 'default',
320-
endpoint: 'default',
321-
hostname: 'default',
322-
gitHostname: 'default',
323-
}
324-
325-
it('throws an error for incomplete dev configuration', async function () {
326-
const testSetting = {
327-
// missing region
328-
endpoint: 'test_endpoint',
329-
hostname: 'test_hostname',
330-
gitHostname: 'test_githostname',
316+
describe('getServiceConfig()', function () {
317+
describe('get CodeCatalyst config', function () {
318+
const devSettingName = 'aws.dev.codecatalystService'
319+
const defaultConfig = {
320+
region: 'default',
321+
endpoint: 'default',
322+
hostname: 'default',
323+
gitHostname: 'default',
331324
}
332325

333-
await settings.update(devSettingName, testSetting)
334-
assert.throws(() => sut.getCodeCatalystConfig(defaultConfig), ToolkitError)
326+
it('throws an error for incomplete dev configuration', async function () {
327+
const testSetting = {
328+
// missing region
329+
endpoint: 'test_endpoint',
330+
hostname: 'test_hostname',
331+
gitHostname: 'test_githostname',
332+
}
333+
334+
await settings.update(devSettingName, testSetting)
335+
assert.throws(() => sut.getServiceConfig('codecatalystService', defaultConfig), ToolkitError)
336+
})
337+
338+
it('returns dev settings configuration when provided', async function () {
339+
const testSetting = {
340+
region: 'test_region',
341+
endpoint: 'test_endpoint',
342+
hostname: 'test_hostname',
343+
gitHostname: 'test_githostname',
344+
}
345+
346+
await settings.update(devSettingName, testSetting)
347+
assert.deepStrictEqual(sut.getServiceConfig('codecatalystService', defaultConfig), testSetting)
348+
})
349+
350+
it('returns default configuration when dev settings are not provided', function () {
351+
assert.deepStrictEqual(sut.getServiceConfig('codecatalystService', defaultConfig), defaultConfig)
352+
})
335353
})
336354

337-
it('returns dev settings configuration when provided', async function () {
338-
const testSetting = {
339-
region: 'test_region',
340-
endpoint: 'test_endpoint',
341-
hostname: 'test_hostname',
342-
gitHostname: 'test_githostname',
355+
describe('get Codewhisperer config', function () {
356+
const devSettingName = 'aws.dev.codewhispererService'
357+
const defaultConfig = {
358+
region: 'default',
359+
endpoint: 'default',
343360
}
344361

345-
await settings.update(devSettingName, testSetting)
346-
assert.deepStrictEqual(sut.getCodeCatalystConfig(defaultConfig), testSetting)
347-
})
362+
it('throws an error for incomplete dev configuration', async function () {
363+
const testSetting = {
364+
// missing region
365+
endpoint: 'test_endpoint',
366+
}
367+
368+
await settings.update(devSettingName, testSetting)
369+
assert.throws(() => sut.getServiceConfig('codewhispererService', defaultConfig), ToolkitError)
370+
})
348371

349-
it('returns default configuration when dev settings are not provided', function () {
350-
assert.deepStrictEqual(sut.getCodeCatalystConfig(defaultConfig), defaultConfig)
372+
it('returns dev settings configuration when provided', async function () {
373+
const testSetting = {
374+
region: 'test_region',
375+
endpoint: 'test_endpoint',
376+
}
377+
378+
await settings.update(devSettingName, testSetting)
379+
assert.deepStrictEqual(sut.getServiceConfig('codewhispererService', defaultConfig), testSetting)
380+
})
381+
382+
it('returns default configuration when dev settings are not provided', function () {
383+
assert.deepStrictEqual(sut.getServiceConfig('codewhispererService', defaultConfig), defaultConfig)
384+
})
351385
})
352386
})
353387
})

0 commit comments

Comments
 (0)