Skip to content

Commit

Permalink
Working POC for Auto Scans and Projects scans from Q chat Panel
Browse files Browse the repository at this point in the history
  • Loading branch information
laileni-aws committed Sep 10, 2024
1 parent 2915614 commit 69a662a
Show file tree
Hide file tree
Showing 39 changed files with 2,764 additions and 14 deletions.
1 change: 1 addition & 0 deletions packages/amazonq/src/app/chat/activation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ function registerApps(appInitContext: amazonq.AmazonQAppInitContext) {
amazonq.cwChatAppInit(appInitContext)
amazonq.featureDevChatAppInit(appInitContext)
amazonq.gumbyChatAppInit(appInitContext)
amazonq.scanChatAppInit(appInitContext)
}

/**
Expand Down
2 changes: 2 additions & 0 deletions packages/core/package.nls.json
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@
"AWS.command.amazonq.optimizeCode": "Optimize",
"AWS.command.amazonq.sendToPrompt": "Send to prompt",
"AWS.command.amazonq.security.scan": "Run Project Scan",
"AWS.command.amazonq.security.autoscan": "Run File Scan",
"AWS.command.deploySamApplication": "Deploy SAM Application",
"AWS.command.aboutToolkit": "About",
"AWS.command.downloadLambda": "Download...",
Expand Down Expand Up @@ -311,6 +312,7 @@
"AWS.amazonq.featureDev.answer.qGeneratedCode": "The Amazon Q Developer Agent for software development has generated code for you to review",
"AWS.amazonq.featureDev.answer.howCodeCanBeImproved": "How can the code be improved?",
"AWS.amazonq.featureDev.answer.approachCreation": "Ok, let me create a plan. This may take a few minutes.",
"AWS.amazonq.scans.runProjectScans": "Scanning code in this workspace ...",
"AWS.amazonq.featureDev.answer.updateCode": "Code has been updated. Would you like to work on another task?",
"AWS.amazonq.featureDev.answer.sessionClosed": "Your session is now closed.",
"AWS.amazonq.featureDev.answer.newTaskChanges": "What change would you like to make?",
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/amazonq/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export { AmazonQChatViewProvider } from './webview/webView'
export { init as cwChatAppInit } from '../codewhispererChat/app'
export { init as featureDevChatAppInit } from '../amazonqFeatureDev/app'
export { init as gumbyChatAppInit } from '../amazonqGumby/app'
export { init as scanChatAppInit } from '../amazonqScans/app'
export { activateBadge } from './util/viewBadgeHandler'
export { amazonQHelpUrl } from '../shared/constants'
export { listCodeWhispererCommandsWalkthrough } from '../codewhisperer/ui/statusBarMenu'
Expand Down
224 changes: 224 additions & 0 deletions packages/core/src/amazonq/webview/ui/apps/scanChatConnector.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,224 @@
/*!
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*
* This class is responsible for listening to and processing events
* from the webview and translating them into events to be handled by the extension,
* and events from the extension and translating them into events to be handled by the webview.
*/

import { ChatItem, ChatItemType } from '@aws/mynah-ui'
import { ExtensionMessage } from '../commands'
import { TabOpenType, TabsStorage } from '../storages/tabsStorage'
import { GumbyMessageType } from '../../../../amazonqGumby/chat/views/connector/connector' //TODO
import { ChatPayload } from '../connector'

export interface ConnectorProps {
sendMessageToExtension: (message: ExtensionMessage) => void
onMessageReceived?: (tabID: string, messageData: any, needToShowAPIDocsTab: boolean) => void
onAsyncEventProgress: (tabID: string, inProgress: boolean, message: string, messageId: string) => void
onChatAnswerReceived?: (tabID: string, message: ChatItem) => void
onChatAnswerUpdated?: (tabID: string, message: ChatItem) => void
onQuickHandlerCommand: (tabID: string, command: string, eventId?: string) => void
onError: (tabID: string, message: string, title: string) => void
onWarning: (tabID: string, message: string, title: string) => void
onUpdateAuthentication: (gumbyEnabled: boolean, authenticatingTabIDs: string[]) => void
onChatInputEnabled: (tabID: string, enabled: boolean) => void
onUpdatePlaceholder: (tabID: string, newPlaceholder: string) => void
tabsStorage: TabsStorage
}

export interface MessageData {
tabID: string
type: GumbyMessageType
}

export class Connector {
private readonly onAuthenticationUpdate
private readonly sendMessageToExtension
private readonly onError
private readonly onChatAnswerReceived
private readonly onChatAnswerUpdated
private readonly chatInputEnabled
private readonly onAsyncEventProgress
private readonly onQuickHandlerCommand
private readonly updatePlaceholder
private readonly tabStorage

constructor(props: ConnectorProps) {
this.sendMessageToExtension = props.sendMessageToExtension
this.onChatAnswerReceived = props.onChatAnswerReceived
this.onChatAnswerUpdated = props.onChatAnswerUpdated
this.onError = props.onError
this.chatInputEnabled = props.onChatInputEnabled
this.onAsyncEventProgress = props.onAsyncEventProgress
this.updatePlaceholder = props.onUpdatePlaceholder
this.onQuickHandlerCommand = props.onQuickHandlerCommand
this.onAuthenticationUpdate = props.onUpdateAuthentication
this.tabStorage = props.tabsStorage
}

onTabAdd = (tabID: string, tabOpenInteractionType?: TabOpenType): void => {
this.sendMessageToExtension({
tabID: tabID,
command: 'new-tab-was-created',
tabType: 'scan',
tabOpenInteractionType,
})
}

onTabRemove(tabID: string) {
this.sendMessageToExtension({
tabID: tabID,
command: 'tab-was-removed',
tabType: 'scan',
})
}

private processChatPrompt = async (messageData: any, tabID: string): Promise<void> => {
if (this.onChatAnswerReceived === undefined) {
return
}

const answer: ChatItem = {
type: ChatItemType.AI_PROMPT,
body: messageData.message,
formItems: messageData.formItems,
buttons: messageData.formButtons,
followUp: undefined,
status: 'info',
canBeVoted: false,
}

this.onChatAnswerReceived(tabID, answer)

return
}

private processChatMessage = async (messageData: any): Promise<void> => {
if (this.onChatAnswerReceived === undefined || this.onChatAnswerUpdated === undefined) {
return
}

if (messageData.message !== undefined) {
const answer: ChatItem = {
type: messageData.messageType,
messageId: messageData.messageId ?? messageData.triggerID,
body: messageData.message,
buttons: messageData.buttons ?? [],
canBeVoted: false,
}

if (messageData.messageId !== undefined) {
this.onChatAnswerUpdated(messageData.tabID, answer)
return
}

this.onChatAnswerReceived(messageData.tabID, answer)
}
}

transform = (tabID: string): void => {
this.sendMessageToExtension({
tabID: tabID,
command: 'scan',
chatMessage: 'transform',
tabType: 'scan', //TODO
})
}

requestAnswer = (tabID: string, payload: ChatPayload) => {
this.tabStorage.updateTabStatus(tabID, 'busy')
this.sendMessageToExtension({
tabID: tabID,
command: 'chat-prompt',
chatMessage: payload.chatMessage,
chatCommand: payload.chatCommand,
tabType: 'scan',
})
}

private processAuthNeededException = async (messageData: any): Promise<void> => {
if (this.onChatAnswerReceived === undefined) {
return
}

this.onChatAnswerReceived(messageData.tabID, {
type: ChatItemType.SYSTEM_PROMPT,
body: messageData.message,
})
}

onCustomFormAction(
tabId: string,
action: {
id: string
text?: string | undefined
formItemValues?: Record<string, string> | undefined
}
) {
if (action === undefined) {
return
}

this.sendMessageToExtension({
command: 'form-action-click',
action: action.id,
formSelectedValues: action.formItemValues,
tabType: 'scan',
tabID: tabId,
})
}

onResponseBodyLinkClick = (tabID: string, messageId: string, link: string): void => {
this.sendMessageToExtension({
command: 'response-body-link-click',
tabID,
messageId,
link,
tabType: 'scan',
})
}

private processExecuteCommand = async (messageData: any): Promise<void> => {
this.onQuickHandlerCommand(messageData.tabID, messageData.command, messageData.eventId)
}

// This handles messages received from the extension, to be forwarded to the webview
handleMessageReceive = async (messageData: { type: GumbyMessageType } & Record<string, any>) => {
switch (messageData.type) {
case 'asyncEventProgressMessage':
this.onAsyncEventProgress(
messageData.tabID,
messageData.inProgress,
messageData.message,
messageData.messageId
)
break
case 'authNeededException':
await this.processAuthNeededException(messageData)
break
case 'authenticationUpdateMessage':
this.onAuthenticationUpdate(messageData.gumbyEnabled, messageData.authenticatingTabIDs)
break
case 'chatInputEnabledMessage':
this.chatInputEnabled(messageData.tabID, messageData.enabled)
break
case 'chatMessage':
await this.processChatMessage(messageData)
break
case 'chatPrompt':
await this.processChatPrompt(messageData, messageData.tabID)
break
case 'errorMessage':
this.onError(messageData.tabID, messageData.message, messageData.title)
break
case 'sendCommandMessage':
await this.processExecuteCommand(messageData)
break
case 'updatePlaceholderMessage':
this.updatePlaceholder(messageData.tabID, messageData.newPlaceholder)
break
}
}
}
1 change: 1 addition & 0 deletions packages/core/src/amazonq/webview/ui/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,5 +31,6 @@ type MessageCommand =
| 'file-click'
| 'form-action-click'
| 'open-settings'
| 'scan'

export type ExtensionMessage = Record<string, any> & { command: MessageCommand }
20 changes: 20 additions & 0 deletions packages/core/src/amazonq/webview/ui/connector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { Connector as CWChatConnector } from './apps/cwChatConnector'
import { Connector as FeatureDevChatConnector } from './apps/featureDevChatConnector'
import { Connector as AmazonQCommonsConnector } from './apps/amazonqCommonsConnector'
import { Connector as GumbyChatConnector } from './apps/gumbyChatConnector'
import { Connector as ScanChatConnector } from './apps/scanChatConnector'
import { ExtensionMessage } from './commands'
import { TabType, TabsStorage } from './storages/tabsStorage'
import { WelcomeFollowupType } from './apps/amazonqCommonsConnector'
Expand Down Expand Up @@ -61,6 +62,7 @@ export class Connector {
private readonly cwChatConnector
private readonly featureDevChatConnector
private readonly gumbyChatConnector
private readonly scanChatConnector
private readonly tabsStorage
private readonly amazonqCommonsConnector: AmazonQCommonsConnector

Expand All @@ -72,6 +74,7 @@ export class Connector {
this.cwChatConnector = new CWChatConnector(props as ConnectorProps)
this.featureDevChatConnector = new FeatureDevChatConnector(props)
this.gumbyChatConnector = new GumbyChatConnector(props)
this.scanChatConnector = new ScanChatConnector(props)
this.amazonqCommonsConnector = new AmazonQCommonsConnector({
sendMessageToExtension: this.sendMessageToExtension,
onWelcomeFollowUpClicked: props.onWelcomeFollowUpClicked,
Expand All @@ -97,6 +100,8 @@ export class Connector {
break
case 'gumby':
this.gumbyChatConnector.onResponseBodyLinkClick(tabID, messageId, link)
case 'scan':
this.scanChatConnector.onResponseBodyLinkClick(tabID, messageId, link)
}
}

Expand All @@ -112,6 +117,8 @@ export class Connector {
switch (this.tabsStorage.getTab(tabID)?.type) {
case 'gumby':
return this.gumbyChatConnector.requestAnswer(tabID, payload)
case 'scan':
return this.scanChatConnector.requestAnswer(tabID, payload)
}
}

Expand Down Expand Up @@ -151,6 +158,10 @@ export class Connector {
this.gumbyChatConnector.transform(tabID)
}

transformScans = (tabID: string): void => {
this.scanChatConnector.transform(tabID)
}

handleMessageReceive = async (message: MessageEvent): Promise<void> => {
if (message.data === undefined) {
return
Expand Down Expand Up @@ -190,6 +201,9 @@ export class Connector {
case 'gumby':
this.gumbyChatConnector.onTabAdd(tabID)
break
case 'scan':
this.scanChatConnector.onTabAdd(tabID)
break
}
}

Expand Down Expand Up @@ -277,6 +291,9 @@ export class Connector {
case 'gumby':
this.gumbyChatConnector.onTabRemove(tabID)
break
case 'scan':
this.scanChatConnector.onTabRemove(tabID)
break
}
}

Expand Down Expand Up @@ -406,6 +423,9 @@ export class Connector {
case 'gumby':
this.gumbyChatConnector.onCustomFormAction(tabId, action)
break
case 'scan':
this.scanChatConnector.onCustomFormAction(tabId, action)
break
case 'cwc':
if (action.id === `open-settings`) {
this.sendMessageToExtension({
Expand Down
9 changes: 8 additions & 1 deletion packages/core/src/amazonq/webview/ui/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ export const createMynahUI = (ideApi: any, amazonQEnabled: boolean) => {
body: 'Authentication successful. Connected to Amazon Q.',
})

if (tabsStorage.getTab(tabID)?.type === 'gumby') {
if (tabsStorage.getTab(tabID)?.type === 'gumby' || tabsStorage.getTab(tabID)?.type === 'scan') {
mynahUI.updateStore(tabID, {
promptInputDisabledState: false,
})
Expand All @@ -119,6 +119,8 @@ export const createMynahUI = (ideApi: any, amazonQEnabled: boolean) => {
quickActionHandler.handle({ command: '/transform' }, tabID, eventId)
} else if (command === 'aws.awsq.clearchat') {
quickActionHandler.handle({ command: '/clear' }, tabID)
} else if (command === 'aws.awsq.scan') {
quickActionHandler.handle({ command: '/scan' }, tabID)
}
},
onCWCContextCommandMessage: (message: ChatItem, command?: string): string | undefined => {
Expand Down Expand Up @@ -376,6 +378,11 @@ export const createMynahUI = (ideApi: any, amazonQEnabled: boolean) => {
chatMessage: prompt.prompt ?? '',
})
return
} else if (tabsStorage.getTab(tabID)?.type === 'scan') {
connector.requestAnswer(tabID, {
chatMessage: prompt.prompt ?? '',
})
return
}

if (prompt.command !== undefined && prompt.command.trim() !== '') {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ export class QuickActionGenerator {
command: '/transform',
description: 'Transform your Java 8 or 11 Maven project to Java 17',
},
{
command: '/scan',
description: 'Identify and fix code issues before committing',
},
]
: []),
],
Expand Down Expand Up @@ -77,6 +81,10 @@ export class QuickActionGenerator {
description: "This command isn't available in /transform",
unavailableItems: ['/dev', '/transform'],
},
scan: {
description: "This command isn't available in /scan",
unavailableItems: ['/dev', '/scan'],
},
unknown: {
description: '',
unavailableItems: [],
Expand Down
Loading

0 comments on commit 69a662a

Please sign in to comment.