Skip to content

Commit 7d3cdf6

Browse files
committed
Merge branch 'master' into migrateEc2
2 parents 4f46c54 + 7389d24 commit 7d3cdf6

File tree

61 files changed

+976
-439
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

61 files changed

+976
-439
lines changed

docs/telemetry.md

Lines changed: 48 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -142,20 +142,23 @@ Finally, if `setupStep2()` was the thing that failed we would see a metric like:
142142

143143
## Adding a "Stack Trace" to your metric
144144

145-
### Problem
145+
When errors are thrown we do not attach the stack trace in telemetry. We only know about the error itself, but
146+
not the path it took to get there. We sometimes need this stack trace to debug, and only have telemetry to get insight on what happened since we do not have access to logs.
147+
148+
### Scenario
146149

147150
Common example: _"I have a function, `thisFailsSometimes()` that is called in multiple places. The function sometimes fails, I know from telemetry, but I do not know if it is failing when it is a specific caller. If I knew the call stack/trace that it took to call my function that would help me debug."_
148151

149152
```typescript
150-
function outerA() {
153+
function runsSuccessfully() {
151154
thisFailsSometimes(1) // this succeeds
152155
}
153156

154-
function outerB() {
157+
function thisThrows() {
155158
thisFailsSometimes(0) // this fails
156159
}
157160

158-
function thisFailsSometimes(num: number) {
161+
function failsDependingOnInput(num: number) {
159162
return telemetry.my_Metric.run(() => {
160163
if (number === 0) {
161164
throw Error('Cannot be 0')
@@ -167,31 +170,61 @@ function thisFailsSometimes(num: number) {
167170

168171
### Solution
169172

170-
Add a value to `function` in the options of a `run()`. This will result in a stack of functions identifiers that were previously called
171-
before `thisFailsSometimes()` was run. You can then retrieve the stack in the `run()` of your final metric using `getFunctionStack()`.
173+
On class methods, use the `@withTelemetryContext()` decorator to add context to the execution. Depending on the args set, it provides features like emitting the result, or adding it's context to errors.
174+
175+
> NOTE: Decorators are currently only supported for methods and not functions
176+
177+
```typescript
178+
class MyClass {
179+
@withTelemetryContext({ name: 'runsSuccessfully', class: 'MyClass' })
180+
public runsSuccessfully() {
181+
failsDependingOnInput(1)
182+
}
183+
184+
@withTelemetryContext({ name: 'thisThrows', class: 'MyClass', errorCtx: true })
185+
public thisThrows() {
186+
failsDependingOnInput(0)
187+
}
188+
189+
@withTelemetryContext({ name: 'failsDependingOnInput' class: 'MyClass', emit: true, errorCtx: true})
190+
private failsDependingOnInput(num: number) {
191+
if (number === 0) {
192+
throw Error('Cannot be 0')
193+
}
194+
...
195+
}
196+
}
197+
198+
// Results in a metric: { source: 'MyClass#thisThrows,failsDependingOnInput', result: 'Failed' }
199+
// Results in an error that has context about the methods that lead up to it.
200+
new MyClass().thisThrows()
201+
```
202+
203+
Separately if you must use a function, add a value to `function` in the options of a `run()`. This will result in a stack of functions identifiers that were previously called
204+
before `failsDependingOnInput()` was run. You can then retrieve the stack in the `run()` of your final metric using `getFunctionStack()`.
172205

173206
```typescript
174-
function outerA() {
175-
telemetry.my_Metric.run(() => thisFailsSometimes(1), { functionId: { name: 'outerA' }})
207+
function runsSuccessfully() {
208+
telemetry.my_Metric.run(() => failsDependingOnInput(1), { functionId: { name: 'runsSuccessfully' }})
176209
}
177210

178-
function outerB() {
179-
telemetry.my_Metric.run(() => thisFailsSometimes(0), { functionId: { source: 'outerB' }})
211+
function thisThrows() {
212+
telemetry.my_Metric.run(() => failsDependingOnInput(0), { functionId: { source: 'thisThrows' }})
180213
}
181214

182-
function thisFailsSometimes(num: number) {
215+
function failsDependingOnInput(num: number) {
183216
return telemetry.my_Metric.run(() => {
184217
telemetry.record({ theCallStack: asStringifiedStack(telemetry.getFunctionStack())})
185218
if (number === 0) {
186219
throw Error('Cannot be 0')
187220
}
188221
...
189-
}, { functionId: { name: 'thisFailsSometimes' }})
222+
}, { functionId: { name: 'failsDependingOnInput' }})
190223
}
191224

192-
// Results in a metric: { theCallStack: 'outerB:thisFailsSometimes', result: 'Failed' }
193-
// { theCallStack: 'outerB:thisFailsSometimes' } implies 'outerB' was run first, then 'thisFailsSometimes'. See docstrings for more info.
194-
outerB()
225+
// Results in a metric: { theCallStack: 'thisThrows:failsDependingOnInput', result: 'Failed' }
226+
// { theCallStack: 'thisThrows:failsDependingOnInput' } implies 'thisThrows' was run first, then 'failsDependingOnInput'. See docstrings for more info.
227+
thisThrows()
195228
```
196229

197230
### Important Notes
@@ -216,25 +249,6 @@ outerB()
216249
c() // result: 'a:c', note that 'b' is not included
217250
```
218251

219-
- If you are using `run()` with a class method, you can also add the class to the entry for more context
220-
221-
```typescript
222-
class A {
223-
a() {
224-
return telemetry.my_Metric.run(() => this.b(), { functionId: { name: 'a', class: 'A' } })
225-
}
226-
227-
b() {
228-
return telemetry.my_Metric.run(() => asStringifiedStack(telemetry.getFunctionStack()), {
229-
functionId: { name: 'b', class: 'A' },
230-
})
231-
}
232-
}
233-
234-
const inst = new A()
235-
inst.a() // 'A#a,b'
236-
```
237-
238252
- If you do not want your `run()` to emit telemetry, set `emit: false` in the options
239253

240254
```typescript
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 chat: `@workspace` command shown in all tab types"
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 /dev: update diff window behavior after a change is accepted"
4+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"type": "Feature",
3+
"description": "Code Transform: Enable support for Java 17 projects."
4+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"type": "Feature",
3+
"description": "Feature(Amazon Q Code Transformation): support conversions of embedded SQL from Oracle to PostgreSQL"
4+
}

packages/amazonq/test/unit/codewhisperer/util/crossFileContextUtil.test.ts

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -40,16 +40,22 @@ describe('crossFileContextUtil', function () {
4040
tempFolder = (await createTestWorkspaceFolder()).uri.fsPath
4141
})
4242

43+
afterEach(async function () {
44+
sinon.restore()
45+
})
46+
4347
it('for control group, should return opentabs context where there will be 3 chunks and each chunk should contains 50 lines', async function () {
44-
sinon.stub(FeatureConfigProvider.instance, 'getProjectContextGroup').alwaysReturned('control')
48+
sinon.stub(FeatureConfigProvider.instance, 'getProjectContextGroup').returns('control')
4549
await toTextEditor(aStringWithLineCount(200), 'CrossFile.java', tempFolder, { preview: false })
4650
const myCurrentEditor = await toTextEditor('', 'TargetFile.java', tempFolder, {
4751
preview: false,
4852
})
53+
54+
await assertTabCount(2)
55+
4956
const actual = await crossFile.fetchSupplementalContextForSrc(myCurrentEditor, fakeCancellationToken)
5057
assert.ok(actual)
51-
assert.ok(actual.supplementalContextItems.length === 3)
52-
58+
assert.strictEqual(actual.supplementalContextItems.length, 3)
5359
assert.strictEqual(actual.supplementalContextItems[0].content.split('\n').length, 50)
5460
assert.strictEqual(actual.supplementalContextItems[1].content.split('\n').length, 50)
5561
assert.strictEqual(actual.supplementalContextItems[2].content.split('\n').length, 50)
@@ -60,6 +66,9 @@ describe('crossFileContextUtil', function () {
6066
const myCurrentEditor = await toTextEditor('', 'TargetFile.java', tempFolder, {
6167
preview: false,
6268
})
69+
70+
await assertTabCount(2)
71+
6372
sinon.stub(FeatureConfigProvider.instance, 'getProjectContextGroup').returns('t1')
6473
sinon
6574
.stub(LspController.instance, 'queryInlineProjectContext')
@@ -74,7 +83,7 @@ describe('crossFileContextUtil', function () {
7483

7584
const actual = await crossFile.fetchSupplementalContextForSrc(myCurrentEditor, fakeCancellationToken)
7685
assert.ok(actual)
77-
assert.ok(actual.supplementalContextItems.length === 4)
86+
assert.strictEqual(actual.supplementalContextItems.length, 4)
7887
assert.strictEqual(actual?.strategy, 'codemap')
7988
assert.deepEqual(actual?.supplementalContextItems[0], {
8089
content: 'foo',
@@ -92,6 +101,9 @@ describe('crossFileContextUtil', function () {
92101
const myCurrentEditor = await toTextEditor('', 'TargetFile.java', tempFolder, {
93102
preview: false,
94103
})
104+
105+
await assertTabCount(2)
106+
95107
sinon.stub(FeatureConfigProvider.instance, 'getProjectContextGroup').returns('t2')
96108
sinon
97109
.stub(LspController.instance, 'queryInlineProjectContext')
@@ -126,7 +138,7 @@ describe('crossFileContextUtil', function () {
126138

127139
const actual = await crossFile.fetchSupplementalContextForSrc(myCurrentEditor, fakeCancellationToken)
128140
assert.ok(actual)
129-
assert.ok(actual.supplementalContextItems.length === 5)
141+
assert.strictEqual(actual.supplementalContextItems.length, 5)
130142
assert.strictEqual(actual?.strategy, 'bm25')
131143

132144
assert.deepEqual(actual?.supplementalContextItems[0], {
@@ -317,7 +329,7 @@ describe('crossFileContextUtil', function () {
317329

318330
fileExtLists.forEach((fileExt) => {
319331
it('should be non empty', async function () {
320-
sinon.stub(FeatureConfigProvider.instance, 'getProjectContextGroup').alwaysReturned('control')
332+
sinon.stub(FeatureConfigProvider.instance, 'getProjectContextGroup').returns('control')
321333
const editor = await toTextEditor('content-1', `file-1.${fileExt}`, tempFolder)
322334
await toTextEditor('content-2', `file-2.${fileExt}`, tempFolder, { preview: false })
323335
await toTextEditor('content-3', `file-3.${fileExt}`, tempFolder, { preview: false })

packages/amazonq/test/unit/codewhisperer/util/supplemetalContextUtil.test.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import assert from 'assert'
77
import * as vscode from 'vscode'
88
import * as sinon from 'sinon'
99
import * as crossFile from 'aws-core-vscode/codewhisperer'
10-
import { TestFolder } from 'aws-core-vscode/test'
10+
import { TestFolder, assertTabCount } from 'aws-core-vscode/test'
1111
import { FeatureConfigProvider } from 'aws-core-vscode/codewhisperer'
1212
import { toTextEditor } from 'aws-core-vscode/test'
1313

@@ -21,7 +21,7 @@ describe('supplementalContextUtil', function () {
2121

2222
beforeEach(async function () {
2323
testFolder = await TestFolder.create()
24-
sinon.stub(FeatureConfigProvider.instance, 'getProjectContextGroup').alwaysReturned('control')
24+
sinon.stub(FeatureConfigProvider.instance, 'getProjectContextGroup').returns('control')
2525
})
2626

2727
afterEach(function () {
@@ -39,6 +39,8 @@ describe('supplementalContextUtil', function () {
3939
preview: false,
4040
})
4141

42+
await assertTabCount(4)
43+
4244
const actual = await crossFile.fetchSupplementalContext(editor, fakeCancellationToken)
4345
assert.ok(actual?.supplementalContextItems.length === 3)
4446
})
@@ -53,6 +55,8 @@ describe('supplementalContextUtil', function () {
5355
preview: false,
5456
})
5557

58+
await assertTabCount(4)
59+
5660
const actual = await crossFile.fetchSupplementalContext(editor, fakeCancellationToken)
5761
assert.ok(actual?.supplementalContextItems.length === 0)
5862
})

packages/core/src/amazonq/commons/diff.ts

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,3 @@ export function createAmazonQUri(path: string, tabId: string) {
3333
// TODO change the featureDevScheme to a more general amazon q scheme
3434
return vscode.Uri.from({ scheme: featureDevScheme, path, query: `tabID=${tabId}` })
3535
}
36-
37-
export async function openFile(path: string) {
38-
if (!(await fs.exists(path))) {
39-
return
40-
}
41-
const fileUri = vscode.Uri.file(path)
42-
await vscode.commands.executeCommand('vscode.diff', fileUri, fileUri)
43-
}

packages/core/src/amazonq/index.ts

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,7 @@ export { amazonQHelpUrl } from '../shared/constants'
2727
export { listCodeWhispererCommandsWalkthrough } from '../codewhisperer/ui/statusBarMenu'
2828
export { focusAmazonQPanel, focusAmazonQPanelKeybinding } from '../codewhispererChat/commands/registerCommands'
2929
export { TryChatCodeLensProvider, tryChatCodeLensCommand } from '../codewhispererChat/editor/codelens'
30-
export {
31-
createAmazonQUri,
32-
openDiff,
33-
openDeletedDiff,
34-
getOriginalFileUri,
35-
getFileDiffUris,
36-
openFile,
37-
} from './commons/diff'
30+
export { createAmazonQUri, openDiff, openDeletedDiff, getOriginalFileUri, getFileDiffUris } from './commons/diff'
3831
export { CodeReference } from '../codewhispererChat/view/connector/connector'
3932
export { AuthMessageDataMap, AuthFollowUpType } from './auth/model'
4033
export { extractAuthFollowUp } from './util/authUtils'

packages/core/src/amazonq/webview/ui/tabs/constants.ts

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,24 @@
22
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
33
* SPDX-License-Identifier: Apache-2.0
44
*/
5-
import { isSQLTransformReady } from '../../../../dev/config'
65
import { TabType } from '../storages/tabsStorage'
6+
import { QuickActionCommandGroup } from '@aws/mynah-ui'
77

88
export type TabTypeData = {
99
title: string
1010
placeholder: string
1111
welcome: string
12+
contextCommands?: QuickActionCommandGroup[]
13+
}
14+
15+
const workspaceCommand: QuickActionCommandGroup = {
16+
groupName: 'Mention code',
17+
commands: [
18+
{
19+
command: '@workspace',
20+
description: '(BETA) Reference all code in workspace.',
21+
},
22+
],
1223
}
1324

1425
const commonTabData: TabTypeData = {
@@ -17,6 +28,7 @@ const commonTabData: TabTypeData = {
1728
welcome: `Hi, I'm Amazon Q. I can answer your software development questions.
1829
Ask me to explain, debug, or optimize your code.
1930
You can enter \`/\` to see a list of quick actions. Add @workspace to beginning of your message to include your entire workspace as context.`,
31+
contextCommands: [workspaceCommand],
2032
}
2133

2234
export const TabTypeDataMap: Record<TabType, TabTypeData> = {
@@ -34,16 +46,6 @@ What would you like to work on?`,
3446
gumby: {
3547
title: 'Q - Code Transformation',
3648
placeholder: 'Open a new tab to chat with Q',
37-
welcome: isSQLTransformReady
38-
? `Welcome to code transformation!
39-
40-
I can help you with the following tasks:
41-
- Upgrade your Java 8 and Java 11 codebases to Java 17
42-
- Convert embedded SQL from Oracle databases to PostgreSQL
43-
44-
What would you like to do? You can enter 'language upgrade' or 'SQL conversion'.`
45-
: `Welcome to code transformation!
46-
47-
I can help you upgrade your Java 8 and 11 codebases to Java 17.`,
49+
welcome: 'Welcome to Code Transformation!',
4850
},
4951
}

0 commit comments

Comments
 (0)