Skip to content

Commit c343a03

Browse files
authored
env: Generalize environment variable for service overrides #4366
Problem: - Only codecatalyst has environment variables overrides and having them for Amazon Q makes testing some things a bit easier Solution: - Generalize the environment variables overrides and extend them to Amazon Q
1 parent 9c5edff commit c343a03

File tree

5 files changed

+95
-39
lines changed

5 files changed

+95
-39
lines changed

CONTRIBUTING.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -357,7 +357,7 @@ Example:
357357
}
358358
```
359359

360-
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.
360+
<a name="codewhisperer-settings">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.</a>
361361

362362
Example:
363363

@@ -406,6 +406,14 @@ Unlike the user setting overrides, not all of these environment variables have t
406406
- `__CODECATALYST_HOSTNAME`: for aws.dev.codecatalystService.hostname
407407
- `__CODECATALYST_GIT_HOSTNAME`: for aws.dev.codecatalystService.gitHostname
408408

409+
#### Codewhisperer/Amazon Q
410+
411+
The following are environment variable versions of the user `settings.json` overrides mentioned [here](#codewhisperer-settings). These will always override the toolkit defaults and those defined in `settings.json`.
412+
Unlike the user setting overrides, not all of these environment variables have to be set to make use of them.
413+
414+
- `__CODEWHISPERER_REGION`: for aws.dev.codewhispererService.region
415+
- `__CODEWHISPERER_ENDPOINT`: for aws.dev.codewhispererService.endpoint
416+
409417
#### Lambda
410418

411419
- `AUTH_UTIL_LAMBDA_ARN`: The Amazon Resource Name (ARN) of the lambda function

src/codewhisperer/client/codewhisperer.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ import { keepAliveHeader } from './agent'
2929
import { getOptOutPreference } from '../util/commonUtil'
3030
import * as os from 'os'
3131
import { getClientId } from '../../shared/telemetry/util'
32-
import { extensionVersion } from '../../shared/vscode/env'
32+
import { extensionVersion, getServiceEnvVarConfig } from '../../shared/vscode/env'
3333
import { DevSettings } from '../../shared/settings'
3434

3535
export interface CodeWhispererConfig {
@@ -43,7 +43,12 @@ export const defaultServiceConfig: CodeWhispererConfig = {
4343
}
4444

4545
export function getCodewhispererConfig(): CodeWhispererConfig {
46-
return DevSettings.instance.getServiceConfig('codewhispererService', defaultServiceConfig)
46+
return {
47+
...DevSettings.instance.getServiceConfig('codewhispererService', defaultServiceConfig),
48+
49+
// Environment variable overrides
50+
...getServiceEnvVarConfig('codewhisperer', Object.keys(defaultServiceConfig)),
51+
}
4752
}
4853

4954
export type ProgrammingLanguage = Readonly<

src/shared/clients/codecatalystClient.ts

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -40,13 +40,6 @@ export interface CodeCatalystConfig {
4040
readonly gitHostname: string
4141
}
4242

43-
const configToEnvMap: { [K in keyof CodeCatalystConfig]: string } = {
44-
region: '__CODECATALYST_REGION',
45-
endpoint: '__CODECATALYST_ENDPOINT',
46-
hostname: '__CODECATALYST_HOSTNAME',
47-
gitHostname: '__CODECATALYST_GIT_HOSTNAME',
48-
}
49-
5043
export const defaultServiceConfig: CodeCatalystConfig = {
5144
region: 'us-east-1',
5245
endpoint: 'https://codecatalyst.global.api.aws',
@@ -59,7 +52,7 @@ export function getCodeCatalystConfig(): CodeCatalystConfig {
5952
...DevSettings.instance.getServiceConfig('codecatalystService', defaultServiceConfig),
6053

6154
// Environment variable overrides
62-
...getServiceEnvVarConfig('codecatalyst', configToEnvMap),
55+
...getServiceEnvVarConfig('codecatalyst', Object.keys(defaultServiceConfig)),
6356
}
6457
}
6558

src/shared/vscode/env.ts

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -92,24 +92,50 @@ export function getCodeCatalystSpaceName(): string | undefined {
9292
return process.env['__DEV_ENVIRONMENT_SPACE_NAME'] || process.env['__DEV_ENVIRONMENT_ORGANIZATION_NAME']
9393
}
9494

95-
type ConfigToEnvMap = { [key: string]: string }
96-
type ServiceConfig = Partial<{ [K in keyof ConfigToEnvMap]: any }>
95+
type ServiceConfig<T extends string[]> = Partial<{
96+
[K in T[number]]: string
97+
}>
9798
const logConfigsOnce: { [key: string]: ReturnType<typeof onceChanged> } = {}
9899

99100
/**
100-
* Accepts a service name and a {config key -> expected env var name} map.
101+
* Generate a map of environment variable names to environment variables.
102+
*
103+
* It does this by converting camel case entries in envVarNames to uppercase joined by underscores
104+
* If there is no camel case in an entry in envVarNames then it just converts the envVar to uppercase
105+
*
106+
* E.g. if service is codecatalyst:
107+
* gitHostname -> __CODECATALYST_GIT_HOSTNAME
108+
* region -> __CODECATALYST_REGION
109+
*/
110+
export function getEnvVars<T extends string[]>(service: string, envVarNames: T): ServiceConfig<T> {
111+
const envVars: ServiceConfig<T> = {}
112+
for (const name of envVarNames) {
113+
// convert camel case to uppercase joined by underscores
114+
// e.g. gitHostname -> GIT_HOSTNAME
115+
const envVarName = name
116+
.split(/(?=[A-Z])/)
117+
.map(s => s.toUpperCase())
118+
.join('_')
119+
envVars[name as T[number]] = `__${service.toUpperCase()}_${envVarName}`
120+
}
121+
return envVars
122+
}
123+
124+
/**
125+
* Accepts a service name and an array of configs.
101126
* For each config key, check if the associated env var exists and return
102127
* a config map with the found values. Changes are logged once for each
103128
* service/found env var combos.
104129
*/
105-
export function getServiceEnvVarConfig(service: string, configToEnvMap: ConfigToEnvMap): ServiceConfig {
106-
const config: ServiceConfig = {}
130+
export function getServiceEnvVarConfig<T extends string[]>(service: string, configs: T): ServiceConfig<T> {
131+
const config: ServiceConfig<T> = {}
107132
const overriden: string[] = []
108133

109134
// Find env vars for each field in the config
110-
for (const [field, envKey] of Object.entries(configToEnvMap)) {
135+
const envVars = getEnvVars<T>(service, configs)
136+
for (const [field, envKey] of Object.entries(envVars) as [string, string][]) {
111137
if (envKey in process.env) {
112-
config[field] = process.env[envKey]
138+
config[field as T[number]] = process.env[envKey]
113139
overriden.push(envKey)
114140
}
115141
}

src/test/shared/vscode/env.test.ts

Lines changed: 45 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,32 +4,56 @@
44
*/
55

66
import assert from 'assert'
7-
import { getServiceEnvVarConfig } from '../../../shared/vscode/env'
7+
import { getEnvVars, getServiceEnvVarConfig } from '../../../shared/vscode/env'
88

9-
describe('getServiceEnvVarConfig', function () {
10-
const envVars: string[] = []
9+
describe('env', function () {
10+
describe('getServiceEnvVarConfig', function () {
11+
const envVars: string[] = []
1112

12-
afterEach(() => {
13-
envVars.forEach(v => delete process.env[v])
14-
envVars.length = 0
13+
afterEach(() => {
14+
envVars.forEach(v => delete process.env[v])
15+
envVars.length = 0
16+
})
17+
18+
function addEnvVar(k: string, v: string) {
19+
process.env[k] = v
20+
envVars.push(k)
21+
}
22+
23+
it('gets service config', async function () {
24+
const service = 'codecatalyst'
25+
const serviceConfigs = ['region', 'endpoint', 'gitHostname']
26+
addEnvVar('__CODECATALYST_ENDPOINT', 'test.endpoint')
27+
addEnvVar('__CODECATALYST_GIT_HOSTNAME', 'test.gitHostname')
28+
29+
const expectedConfig = {
30+
endpoint: 'test.endpoint',
31+
gitHostname: 'test.gitHostname',
32+
}
33+
assert.deepStrictEqual(getServiceEnvVarConfig(service, serviceConfigs), expectedConfig)
34+
})
1535
})
1636

17-
function addEnvVar(k: string, v: string) {
18-
process.env[k] = v
19-
envVars.push(k)
20-
}
37+
describe('getEnvVars', function () {
38+
it('gets codecatalyst environment variables', async function () {
39+
const expectedEnvVars = {
40+
region: '__CODECATALYST_REGION',
41+
endpoint: '__CODECATALYST_ENDPOINT',
42+
hostname: '__CODECATALYST_HOSTNAME',
43+
gitHostname: '__CODECATALYST_GIT_HOSTNAME',
44+
}
2145

22-
it('gets service config', async function () {
23-
const service = 'codecatalyst'
24-
const configToEnvMap = {
25-
region: '__CODECATALYST_REGION',
26-
endpoint: '__CODECATALYST_ENDPOINT',
27-
}
28-
addEnvVar('__CODECATALYST_ENDPOINT', 'test.endpoint')
46+
const envVar = getEnvVars('codecatalyst', Object.keys(expectedEnvVars))
47+
assert.deepStrictEqual(envVar, expectedEnvVars)
48+
})
2949

30-
const expectedConfig = {
31-
endpoint: 'test.endpoint',
32-
}
33-
assert.deepStrictEqual(getServiceEnvVarConfig(service, configToEnvMap), expectedConfig)
50+
it('gets codewhisperer environment variables', async function () {
51+
const expectedEnvVars = {
52+
region: '__CODEWHISPERER_REGION',
53+
endpoint: '__CODEWHISPERER_ENDPOINT',
54+
}
55+
const envVar = getEnvVars('codewhisperer', Object.keys(expectedEnvVars))
56+
assert.deepStrictEqual(envVar, expectedEnvVars)
57+
})
3458
})
3559
})

0 commit comments

Comments
 (0)