Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"type": "Bug Fix",
"description": "Amazon Q (/dev): Allow end-user access history diff generated along iterations"
}
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,8 @@ describe('session', () => {
[],
[],
tabID,
0
0,
{}
)
const session = await createSession({ messenger, sessionState: codeGenState, conversationID })
encodedContent = new TextEncoder().encode(notRejectedFileContent)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,12 +94,13 @@ export class Connector {
})
}

onOpenDiff = (tabID: string, filePath: string, deleted: boolean): void => {
onOpenDiff = (tabID: string, filePath: string, deleted: boolean, messageId?: string): void => {
this.sendMessageToExtension({
command: 'open-diff',
tabID,
filePath,
deleted,
messageId,
tabType: 'featuredev',
})
}
Expand Down Expand Up @@ -167,7 +168,11 @@ export class Connector {
canBeVoted: true,
codeReference: messageData.references,
// TODO get the backend to store a message id in addition to conversationID
messageId: messageData.messageID ?? messageData.triggerID ?? messageData.conversationID,
messageId:
messageData.codeGenerationId ??
messageData.messageID ??
messageData.triggerID ??
messageData.conversationID,
fileList: {
rootFolderTitle: 'Changes',
filePaths: messageData.filePaths.map((f: DiffTreeFileInfo) => f.zipFilePath),
Expand Down
14 changes: 12 additions & 2 deletions packages/core/src/amazonq/webview/ui/connector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,16 @@ export interface CodeReference {
}
}

export interface UploadHistory {
[key: string]: {
uploadId: string
timestamp: number
tabId: string
filePaths: DiffTreeFileInfo[]
deletedFiles: DiffTreeFileInfo[]
}
}

export interface ChatPayload {
chatMessage: string
chatCommand?: string
Expand Down Expand Up @@ -355,10 +365,10 @@ export class Connector {
}
}

onOpenDiff = (tabID: string, filePath: string, deleted: boolean): void => {
onOpenDiff = (tabID: string, filePath: string, deleted: boolean, messageId?: string): void => {
switch (this.tabsStorage.getTab(tabID)?.type) {
case 'featuredev':
this.featureDevChatConnector.onOpenDiff(tabID, filePath, deleted)
this.featureDevChatConnector.onOpenDiff(tabID, filePath, deleted, messageId)
break
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ type OpenDiffMessage = {
// currently the zip file path
filePath: string
deleted: boolean
codeGenerationId: string
}

type fileClickedMessage = {
Expand Down Expand Up @@ -400,7 +401,8 @@ export class FeatureDevController {
deletedFiles,
session.state.references ?? [],
tabID,
session.uploadId
session.uploadId,
session.state.codeGenerationId ?? ''
)

const remainingIterations = session.state.codeGenerationRemainingIterationCount
Expand Down Expand Up @@ -686,6 +688,7 @@ export class FeatureDevController {

private async openDiff(message: OpenDiffMessage) {
const tabId: string = message.tabID
const codeGenerationId: string = message.messageId
const zipFilePath: string = message.filePath
const session = await this.sessionStorage.getSession(tabId)
telemetry.amazonq_isReviewedChanges.emit({
Expand All @@ -702,7 +705,11 @@ export class FeatureDevController {
const name = path.basename(pathInfos.relativePath)
await openDeletedDiff(pathInfos.absolutePath, name, tabId)
} else {
const rightPath = path.join(session.uploadId, zipFilePath)
let uploadId = session.uploadId
if (session?.state?.uploadHistory && session.state.uploadHistory[codeGenerationId]) {
uploadId = session?.state?.uploadHistory[codeGenerationId].uploadId
}
const rightPath = path.join(uploadId, zipFilePath)
await openDiff(pathInfos.absolutePath, rightPath, tabId)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,9 +115,12 @@ export class Messenger {
deletedFiles: DeletedFileInfo[],
references: CodeReference[],
tabID: string,
uploadId: string
uploadId: string,
codeGenerationId: string
) {
this.dispatcher.sendCodeResult(new CodeResultMessage(filePaths, deletedFiles, references, tabID, uploadId))
this.dispatcher.sendCodeResult(
new CodeResultMessage(filePaths, deletedFiles, references, tabID, uploadId, codeGenerationId)
)
}

public sendAsyncEventProgress(tabID: string, inProgress: boolean, message: string | undefined) {
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/amazonqFeatureDev/session/session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ export class Session {
fs: this.config.fs,
messenger: this.messenger,
telemetry: this.telemetry,
uploadHistory: this.state.uploadHistory,
})

if (resp.nextState) {
Expand Down
30 changes: 25 additions & 5 deletions packages/core/src/amazonqFeatureDev/session/sessionState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ import {
import { prepareRepoData } from '../util/files'
import { TelemetryHelper } from '../util/telemetryHelper'
import { uploadCode } from '../util/upload'
import { CodeReference } from '../../amazonq/webview/ui/connector'
import { CodeReference, UploadHistory } from '../../amazonq/webview/ui/connector'
import { isPresent } from '../../shared/utilities/collectionUtils'
import { AuthUtil } from '../../codewhisperer/util/authUtil'
import { randomUUID } from '../../shared/crypto'
Expand Down Expand Up @@ -261,6 +261,7 @@ export class CodeGenState extends CodeGenBase implements SessionState {
public references: CodeReference[],
tabID: string,
private currentIteration: number,
public uploadHistory: UploadHistory,
public codeGenerationRemainingIterationCount?: number,
public codeGenerationTotalIterationCount?: number
) {
Expand Down Expand Up @@ -308,6 +309,16 @@ export class CodeGenState extends CodeGenBase implements SessionState {
this.codeGenerationRemainingIterationCount = codeGeneration.codeGenerationRemainingIterationCount
this.codeGenerationTotalIterationCount = codeGeneration.codeGenerationTotalIterationCount

if (action.uploadHistory && !action.uploadHistory[codeGenerationId] && codeGenerationId) {
action.uploadHistory[codeGenerationId] = {
timestamp: Date.now(),
uploadId: this.config.uploadId,
filePaths: codeGeneration.newFiles,
deletedFiles: codeGeneration.deletedFiles,
tabId: this.tabID,
}
}

action.telemetry.setAmazonqNumberOfReferences(this.references.length)
action.telemetry.recordUserCodeGenerationTelemetry(span, this.conversationId)
const nextState = new PrepareCodeGenState(
Expand All @@ -318,7 +329,9 @@ export class CodeGenState extends CodeGenBase implements SessionState {
this.tabID,
this.currentIteration + 1,
this.codeGenerationRemainingIterationCount,
this.codeGenerationTotalIterationCount
this.codeGenerationTotalIterationCount,
action.uploadHistory,
codeGenerationId
)
return {
nextState,
Expand All @@ -338,6 +351,7 @@ export class MockCodeGenState implements SessionState {
public filePaths: NewFileInfo[]
public deletedFiles: DeletedFileInfo[]
public readonly conversationId: string
public readonly codeGenerationId?: string
public readonly uploadId: string

constructor(
Expand Down Expand Up @@ -384,7 +398,8 @@ export class MockCodeGenState implements SessionState {
},
],
this.tabID,
this.uploadId
this.uploadId,
this.codeGenerationId ?? ''
)
action.messenger.sendAnswer({
message: undefined,
Expand Down Expand Up @@ -431,11 +446,15 @@ export class PrepareCodeGenState implements SessionState {
public tabID: string,
private currentIteration: number,
public codeGenerationRemainingIterationCount?: number,
public codeGenerationTotalIterationCount?: number
public codeGenerationTotalIterationCount?: number,
public uploadHistory: UploadHistory = {},
public codeGenerationId?: string
) {
this.tokenSource = new vscode.CancellationTokenSource()
this.uploadId = config.uploadId
this.conversationId = config.conversationId
this.uploadHistory = uploadHistory
this.codeGenerationId = codeGenerationId
}

updateWorkspaceRoot(workspaceRoot: string) {
Expand Down Expand Up @@ -490,7 +509,8 @@ export class PrepareCodeGenState implements SessionState {
this.deletedFiles,
this.references,
this.tabID,
this.currentIteration
this.currentIteration,
this.uploadHistory
)
return nextState.interact(action)
}
Expand Down
5 changes: 4 additions & 1 deletion packages/core/src/amazonqFeatureDev/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import type { CancellationTokenSource } from 'vscode'
import { Messenger } from './controllers/chat/messenger/messenger'
import { FeatureDevClient } from './client/featureDev'
import { TelemetryHelper } from './util/telemetryHelper'
import { CodeReference } from '../amazonq/webview/ui/connector'
import { CodeReference, UploadHistory } from '../amazonq/webview/ui/connector'
import { DiffTreeFileInfo } from '../amazonq/webview/ui/diffTree/types'

export type Interaction = {
Expand Down Expand Up @@ -61,11 +61,13 @@ export interface SessionState {
readonly phase?: SessionStatePhase
readonly uploadId: string
readonly tokenSource: CancellationTokenSource
readonly codeGenerationId?: string
readonly tabID: string
interact(action: SessionStateAction): Promise<SessionStateInteraction>
updateWorkspaceRoot?: (workspaceRoot: string) => void
codeGenerationRemainingIterationCount?: number
codeGenerationTotalIterationCount?: number
uploadHistory?: UploadHistory
}

export interface SessionStateConfig {
Expand All @@ -82,6 +84,7 @@ export interface SessionStateAction {
messenger: Messenger
fs: VirtualFileSystem
telemetry: TelemetryHelper
uploadHistory?: UploadHistory
}

export type NewFileZipContents = { zipFilePath: string; fileContent: string }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ export class UIMessageListener {
tabID: msg.tabID,
filePath: msg.filePath,
deleted: msg.deleted,
messageId: msg.messageId,
})
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ export class ErrorMessage extends UiMessage {

export class CodeResultMessage extends UiMessage {
readonly message!: string
readonly codeGenerationId!: string
readonly references!: {
information: string
recommendationContentSpan: {
Expand All @@ -48,7 +49,8 @@ export class CodeResultMessage extends UiMessage {
readonly deletedFiles: DeletedFileInfo[],
references: CodeReference[],
tabID: string,
conversationID: string
conversationID: string,
codeGenerationId: string
) {
super(tabID)
this.references = references
Expand All @@ -64,6 +66,7 @@ export class CodeResultMessage extends UiMessage {
},
}
})
this.codeGenerationId = codeGenerationId
this.conversationID = conversationID
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,7 @@ describe('Controller', () => {
workspaceFolders,
}

const codeGenState = new CodeGenState(testConfig, getFilePaths(controllerSetup), [], [], tabID, 0)
const codeGenState = new CodeGenState(testConfig, getFilePaths(controllerSetup), [], [], tabID, 0, {})
const newSession = await createSession({
messenger: controllerSetup.messenger,
sessionState: codeGenState,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,10 @@ const mockSessionStateAction = (msg?: string): SessionStateAction => {
new AppToWebViewMessageDispatcher(new MessagePublisher<any>(new vscode.EventEmitter<any>()))
),
telemetry: new TelemetryHelper(),
uploadHistory: {},

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Consider adding a test where uploadHistory is populated and show we can access an element from it

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, used the wrong account here.

Copy link
Contributor Author

@tverney tverney Oct 9, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, I thought was somekind of automatic messaging. Thanks, yea, due the timeline I would say we can add those testing scenarios as a follow up PR

}
}

let mockGeneratePlan: sinon.SinonStub
let mockGetCodeGeneration: sinon.SinonStub
let mockExportResultArchive: sinon.SinonStub
let mockCreateUploadUrl: sinon.SinonStub
Expand All @@ -49,7 +49,6 @@ const mockSessionStateConfig = ({
proxyClient: {
createConversation: () => sinon.stub(),
createUploadUrl: () => mockCreateUploadUrl(),
generatePlan: () => mockGeneratePlan(),
startCodeGeneration: () => sinon.stub(),
getCodeGeneration: () => mockGetCodeGeneration(),
exportResultArchive: () => mockExportResultArchive(),
Expand Down Expand Up @@ -111,7 +110,7 @@ describe('sessionState', () => {
mockExportResultArchive = sinon.stub().resolves({ newFileContents: [], deletedFiles: [], references: [] })

const testAction = mockSessionStateAction()
const state = new CodeGenState(testConfig, [], [], [], tabId, 0, 2, 3)
const state = new CodeGenState(testConfig, [], [], [], tabId, 0, {}, 2, 3)
const result = await state.interact(testAction)

const nextState = new PrepareCodeGenState(testConfig, [], [], [], tabId, 1, 2, 3)
Expand All @@ -125,7 +124,7 @@ describe('sessionState', () => {
it('fails when codeGenerationStatus failed ', async () => {
mockGetCodeGeneration = sinon.stub().rejects(new ToolkitError('Code generation failed'))
const testAction = mockSessionStateAction()
const state = new CodeGenState(testConfig, [], [], [], tabId, 0)
const state = new CodeGenState(testConfig, [], [], [], tabId, 0, {})
try {
await state.interact(testAction)
assert.fail('failed code generations should throw an error')
Expand Down
Loading