Skip to content

Commit 3d2ffd8

Browse files
Merge master into feature/amazonqLSP
2 parents b3f2e82 + 072eecd commit 3d2ffd8

File tree

9 files changed

+70
-43
lines changed

9 files changed

+70
-43
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"type": "Bug Fix",
3+
"description": "Amazon Q /doc: Improve button text phrasing"
4+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"type": "Bug Fix",
3+
"description": "Amazon Q /test: Fix to redirect /test to generate tests in chat for external files out of workspace scope."
4+
}

packages/core/package.nls.json

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -373,8 +373,8 @@
373373
"AWS.amazonq.doc.answer.scanning": "Scanning source files",
374374
"AWS.amazonq.doc.answer.summarizing": "Summarizing source files",
375375
"AWS.amazonq.doc.answer.generating": "Generating documentation",
376-
"AWS.amazonq.doc.answer.creating": "Okay, I'm creating a README for your project. This may take a few minutes.",
377-
"AWS.amazonq.doc.answer.updating": "Okay, I'm updating the README to reflect your code changes. This may take a few minutes.",
376+
"AWS.amazonq.doc.answer.creating": "Okay, I'm creating a README for your project. This might take a few minutes.",
377+
"AWS.amazonq.doc.answer.updating": "Okay, I'm updating the README. This might take a few minutes.",
378378
"AWS.amazonq.doc.answer.chooseFolder": "Choose a folder to continue.",
379379
"AWS.amazonq.doc.error.noFolderSelected": "It looks like you didn't choose a folder. Choose a folder to continue.",
380380
"AWS.amazonq.doc.error.contentLengthError": "Your workspace is too large for me to review. Your workspace must be within the quota, even if you choose a smaller folder. For more information on quotas, see the <a href=\"https://docs.aws.amazon.com/amazonq/latest/qdeveloper-ug/doc-generation.html#quotas\" target=\"_blank\">Amazon Q Developer documentation.</a>",
@@ -387,6 +387,9 @@
387387
"AWS.amazonq.doc.error.promptRefusal": "I'm sorry, I can't generate documentation for this folder. Please make sure your message and code files comply with the Please make sure your message and code files comply with the <a href=\"https://aws.amazon.com/machine-learning/responsible-ai/policy/\">AWS Responsible AI Policy.</a>",
388388
"AWS.amazonq.doc.placeholder.editReadme": "Describe documentation changes",
389389
"AWS.amazonq.doc.pillText.closeSession": "End session",
390+
"AWS.amazonq.doc.pillText.newTask": "Start a new documentation task",
391+
"AWS.amazonq.doc.pillText.update": "Update README to reflect code",
392+
"AWS.amazonq.doc.pillText.makeChange": "Make a specific change",
390393
"AWS.amazonq.inline.invokeChat": "Inline chat",
391394
"AWS.toolkit.lambda.walkthrough.quickpickTitle": "Application Builder Walkthrough",
392395
"AWS.toolkit.lambda.walkthrough.title": "Get started building your application",

packages/core/src/amazonqDoc/constants.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -93,15 +93,15 @@ export const FolderSelectorFollowUps = [
9393
]
9494

9595
export const SynchronizeDocumentation = {
96-
pillText: 'Update README with recent code changes',
97-
prompt: 'Update README with recent code changes',
98-
type: 'SynchronizeDocumentation',
96+
pillText: i18n('AWS.amazonq.doc.pillText.update'),
97+
prompt: i18n('AWS.amazonq.doc.pillText.update'),
98+
type: FollowUpTypes.SynchronizeDocumentation,
9999
}
100100

101101
export const EditDocumentation = {
102-
pillText: 'Make a specific change',
103-
prompt: 'Make a specific change',
104-
type: 'EditDocumentation',
102+
pillText: i18n('AWS.amazonq.doc.pillText.makeChange'),
103+
prompt: i18n('AWS.amazonq.doc.pillText.makeChange'),
104+
type: FollowUpTypes.EditDocumentation,
105105
}
106106

107107
export enum Mode {

packages/core/src/amazonqDoc/controllers/chat/controller.ts

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -307,7 +307,7 @@ export class DocController {
307307
message: 'Your changes have been discarded.',
308308
followUps: [
309309
{
310-
pillText: i18n('AWS.amazonq.featureDev.pillText.newTask'),
310+
pillText: i18n('AWS.amazonq.doc.pillText.newTask'),
311311
type: FollowUpTypes.NewTask,
312312
status: 'info',
313313
},
@@ -706,14 +706,12 @@ export class DocController {
706706
tabID: message.tabID,
707707
followUps: [
708708
{
709-
pillText: 'Start a new documentation task',
710-
prompt: 'Start a new documentation task',
709+
pillText: i18n('AWS.amazonq.doc.pillText.newTask'),
711710
type: FollowUpTypes.NewTask,
712711
status: 'info',
713712
},
714713
{
715-
pillText: 'End session',
716-
prompt: 'End session',
714+
pillText: i18n('AWS.amazonq.doc.pillText.closeSession'),
717715
type: FollowUpTypes.CloseSession,
718716
status: 'info',
719717
},

packages/core/src/amazonqTest/chat/controller/controller.ts

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -439,16 +439,21 @@ export class TestController {
439439

440440
const language = await this.getLanguageForFilePath(filePath)
441441
session.fileLanguage = language
442+
const workspaceFolder = vscode.workspace.getWorkspaceFolder(fileEditorToTest.document.uri)
442443

443444
/*
444445
For Re:Invent 2024 we are supporting only java and python for unit test generation, rest of the languages shows the similar experience as CWC
445446
*/
446-
if (language !== 'java' && language !== 'python') {
447-
const unsupportedLanguage = language.charAt(0).toUpperCase() + language.slice(1)
448-
let unsupportedMessage = `<span style="color: #EE9D28;">&#9888;<b>I'm sorry, but /test only supports Python and Java</b><br></span> While ${unsupportedLanguage} is not supported, I will generate a suggestion below. `
449-
// handle the case when language is undefined
450-
if (!unsupportedLanguage) {
451-
unsupportedMessage = `<span style="color: #EE9D28;">&#9888;<b>I'm sorry, but /test only supports Python and Java</b><br></span> I will still generate a suggestion below. `
447+
if (!['java', 'python'].includes(language) || workspaceFolder === undefined) {
448+
let unsupportedMessage: string
449+
const unsupportedLanguage = language ? language.charAt(0).toUpperCase() + language.slice(1) : ''
450+
if (!workspaceFolder) {
451+
// File is outside of workspace
452+
unsupportedMessage = `<span style="color: #EE9D28;">&#9888;<b>I can't generate tests for ${fileName}</b> because the file is outside of workspace scope.<br></span> I can still provide examples, instructions and code suggestions.`
453+
} else if (unsupportedLanguage) {
454+
unsupportedMessage = `<span style="color: #EE9D28;">&#9888;<b>I'm sorry, but /test only supports Python and Java</b><br></span> While ${unsupportedLanguage} is not supported, I will generate a suggestion below.`
455+
} else {
456+
unsupportedMessage = `<span style="color: #EE9D28;">&#9888;<b>I'm sorry, but /test only supports Python and Java</b><br></span> I will still generate a suggestion below.`
452457
}
453458
this.messenger.sendMessage(unsupportedMessage, tabID, 'answer')
454459
await this.onCodeGeneration(session, message.prompt, tabID, fileName, filePath)

packages/core/src/auth/sso/ssoAccessTokenProvider.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ import { randomUUID } from '../../shared/crypto'
3333
import { getExtRuntimeContext } from '../../shared/vscode/env'
3434
import { showInputBox } from '../../shared/ui/inputPrompter'
3535
import { AmazonQPromptSettings, DevSettings, PromptSettings, ToolkitPromptSettings } from '../../shared/settings'
36-
import { onceChanged } from '../../shared/utilities/functionUtils'
36+
import { debounce, onceChanged } from '../../shared/utilities/functionUtils'
3737
import { NestedMap } from '../../shared/utilities/map'
3838
import { asStringifiedStack } from '../../shared/telemetry/spans'
3939
import { showViewLogsMessage } from '../../shared/utilities/messages'
@@ -97,7 +97,20 @@ export abstract class SsoAccessTokenProvider {
9797
this.reAuthState.set(this.profile, { reAuthReason: `invalidate():${reason}` })
9898
}
9999

100+
/**
101+
* Sometimes we get many calls at once and this
102+
* can trigger redundant disk reads, or token refreshes.
103+
* We debounce to avoid this.
104+
*
105+
* NOTE: The property {@link getTokenDebounced()} does not work with being stubbed for tests, so
106+
* this redundant function was created to work around that.
107+
*/
100108
public async getToken(): Promise<SsoToken | undefined> {
109+
return this.getTokenDebounced()
110+
}
111+
private getTokenDebounced = debounce(() => this._getToken(), 50)
112+
/** Exposed for testing purposes only */
113+
public async _getToken(): Promise<SsoToken | undefined> {
101114
const data = await this.cache.token.load(this.tokenCacheKey)
102115
SsoAccessTokenProvider.logIfChanged(
103116
indent(

packages/core/src/test/credentials/provider/sharedCredentialsProvider.test.ts

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,11 @@
44
*/
55

66
import assert from 'assert'
7-
import * as FakeTimers from '@sinonjs/fake-timers'
87
import * as sinon from 'sinon'
98
import { SharedCredentialsProvider } from '../../../auth/providers/sharedCredentialsProvider'
109
import { stripUndefined } from '../../../shared/utilities/collectionUtils'
1110
import * as process from '@aws-sdk/credential-provider-process'
1211
import { ParsedIniData } from '@smithy/shared-ini-file-loader'
13-
import { installFakeClock } from '../../testUtil'
1412
import { SsoClient } from '../../../auth/sso/clients'
1513
import { stub } from '../../utilities/stubber'
1614
import { SsoAccessTokenProvider } from '../../../auth/sso/ssoAccessTokenProvider'
@@ -19,20 +17,13 @@ import { createTestSections } from '../testUtil'
1917
const missingPropertiesFragment = 'missing properties'
2018

2119
describe('SharedCredentialsProvider', async function () {
22-
let clock: FakeTimers.InstalledClock
2320
let sandbox: sinon.SinonSandbox
2421

2522
before(function () {
2623
sandbox = sinon.createSandbox()
27-
clock = installFakeClock()
28-
})
29-
30-
after(function () {
31-
clock.uninstall()
3224
})
3325

3426
afterEach(function () {
35-
clock.reset()
3627
sandbox.restore()
3728
})
3829

packages/core/src/test/credentials/sso/ssoAccessTokenProvider.test.ts

Lines changed: 23 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import { ToolkitError } from '../../../shared/errors'
2727
import * as fs from 'fs' // eslint-disable-line no-restricted-imports
2828
import * as path from 'path'
2929
import { Stub, stub } from '../../utilities/stubber'
30+
import { globals } from '../../../shared'
3031

3132
const hourInMs = 3600000
3233

@@ -37,14 +38,14 @@ describe('SsoAccessTokenProvider', function () {
3738
let oidcClient: Stub<OidcClient>
3839
let sut: SsoAccessTokenProvider
3940
let cache: ReturnType<typeof getCache>
40-
let clock: FakeTimers.InstalledClock
41+
let clock: FakeTimers.InstalledClock | undefined
4142
let tempDir: string
4243
let reAuthState: TestReAuthState
4344

4445
function createToken(timeDelta: number, extras: Partial<SsoToken> = {}) {
4546
return {
4647
accessToken: 'dummyAccessToken',
47-
expiresAt: new clock.Date(clock.Date.now() + timeDelta),
48+
expiresAt: new globals.clock.Date(globals.clock.Date.now() + timeDelta),
4849
...extras,
4950
}
5051
}
@@ -54,7 +55,7 @@ describe('SsoAccessTokenProvider', function () {
5455
scopes: [],
5556
clientId: 'dummyClientId',
5657
clientSecret: 'dummyClientSecret',
57-
expiresAt: new clock.Date(clock.Date.now() + timeDelta),
58+
expiresAt: new globals.clock.Date(globals.clock.Date.now() + timeDelta),
5859
startUrl,
5960
...extras,
6061
}
@@ -66,7 +67,7 @@ describe('SsoAccessTokenProvider', function () {
6667
deviceCode: 'dummyCode',
6768
userCode: 'dummyUserCode',
6869
verificationUri: 'dummyLink',
69-
expiresAt: new clock.Date(clock.Date.now() + timeDelta),
70+
expiresAt: new globals.clock.Date(globals.clock.Date.now() + timeDelta),
7071
}
7172
}
7273

@@ -77,14 +78,6 @@ describe('SsoAccessTokenProvider', function () {
7778
return cacheDir
7879
}
7980

80-
before(function () {
81-
clock = installFakeClock()
82-
})
83-
84-
after(function () {
85-
clock.uninstall()
86-
})
87-
8881
beforeEach(async function () {
8982
oidcClient = stub(OidcClient)
9083
tempDir = await makeTemporaryTokenCacheFolder()
@@ -95,7 +88,7 @@ describe('SsoAccessTokenProvider', function () {
9588

9689
afterEach(async function () {
9790
sinon.restore()
98-
clock.reset()
91+
clock?.uninstall()
9992
await tryRemoveFolder(tempDir)
10093
})
10194

@@ -163,6 +156,20 @@ describe('SsoAccessTokenProvider', function () {
163156
assert.strictEqual(cachedToken, undefined)
164157
})
165158

159+
it('concurrent calls are debounced', async function () {
160+
const validToken = createToken(hourInMs)
161+
await cache.token.save(startUrl, { region, startUrl, token: validToken })
162+
const actualGetToken = sinon.spy(sut, '_getToken')
163+
164+
const result = await Promise.all([sut.getToken(), sut.getToken(), sut.getToken()])
165+
166+
// Subsequent other calls were debounced so this was only called once
167+
assert.strictEqual(actualGetToken.callCount, 1)
168+
for (const r of result) {
169+
assert.deepStrictEqual(r, validToken)
170+
}
171+
})
172+
166173
describe('Exceptions', function () {
167174
it('drops expired tokens if failure was a client-fault', async function () {
168175
const exception = new UnauthorizedClientException({ message: '', $metadata: {} })
@@ -267,6 +274,7 @@ describe('SsoAccessTokenProvider', function () {
267274
})
268275

269276
it(`emits session duration between logins of the same startUrl`, async function () {
277+
clock = installFakeClock()
270278
setupFlow()
271279
stubOpen()
272280

@@ -311,6 +319,7 @@ describe('SsoAccessTokenProvider', function () {
311319
})
312320

313321
it('respects the device authorization expiration time', async function () {
322+
clock = installFakeClock()
314323
setupFlow()
315324
stubOpen()
316325
const exception = new AuthorizationPendingException({ message: '', $metadata: {} })
@@ -352,7 +361,7 @@ describe('SsoAccessTokenProvider', function () {
352361
const registration = {
353362
clientId: 'myExpiredClientId',
354363
clientSecret: 'myExpiredClientSecret',
355-
expiresAt: new clock.Date(clock.Date.now() - 1), // expired date
364+
expiresAt: new globals.clock.Date(globals.clock.Date.now() - 1), // expired date
356365
startUrl: key.startUrl,
357366
}
358367
await cache.registration.save(key, registration)

0 commit comments

Comments
 (0)