Skip to content

Commit 42a862a

Browse files
authored
feat(lambda): Refactor and optimize Lambda Remote Invoke UI with enhanced payload management (#8068)
## Problem Few UI feedback: - Payload is not intuitive - Open Handler should be a button Inconsistent layer behavior ## UI before <img width="921" height="653" alt="image" src="https://github.com/user-attachments/assets/0d7c2f27-78f6-453d-91fb-02ac00f2b043" /> ## Solution This PR refactors the Lambda Remote Invoke UI to follow VSCode's native design patterns, optimizes the codebase by removing unused components, and enhances the payload management experience with integrated quickpick functionality for remote test events. <img width="857" height="899" alt="image" src="https://github.com/user-attachments/assets/1e446174-8cea-43a6-8ef1-44c1c8008c23" /> Update global layer version to 2 ### 🎨 UI/UX Improvements - __Redesigned Remote Debugging section__ to follow VSCode Settings UI pattern - Title with inline "Remove Debug Setup" button and timer info - Checkbox aligned with description on same line for better visual hierarchy - __Redesigned Local Root Path section__ with VSCode Settings style - Added "Open Handler" button with disabled state and helpful tooltips - Shortened button labels to "Browse" and "Download" for cleaner interface - Enhanced descriptions with bold text to guide user actions - __Completely redesigned Payload section__ - Removed radio buttons for cleaner, unified interface - Added button group: "Load sample event", "Load local file", "Load remote event", "Save as remote event" - Improved textarea with monospace font and better sizing ### 🚀 New Features - __VSCode quickpick integration for remote test events__ - `selectRemoteTestEvent()`: Shows native quickpick for loading saved events - `saveRemoteTestEvent()`: Quickpick with options to create new or overwrite existing events - Input validation for event names - Confirmation dialogs for overwriting existing events - __Enhanced error handling__ - Gracefully handles "lambda-testevent-schemas registry not found" error - Returns empty array instead of throwing when no test events exist - Helpful user messages when no events are found - __Force flag support__ for overwriting existing test events - Added `force` parameter to `SamCliRemoteTestEventsParameters` - Automatically uses `--force` when user confirms overwrite ### --- - Treat all work as PUBLIC. Private `feature/x` branches will not be squash-merged at release time. - Your code changes must meet the guidelines in [CONTRIBUTING.md](https://github.com/aws/aws-toolkit-vscode/blob/master/CONTRIBUTING.md#guidelines). - License: I confirm that my contribution is made under the terms of the Apache 2.0 license.
1 parent 9b0deb2 commit 42a862a

File tree

9 files changed

+803
-414
lines changed

9 files changed

+803
-414
lines changed

packages/core/src/lambda/remoteDebugging/ldkClient.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,10 @@ export class LdkClient {
302302
updatedEnv.ORIGINAL_AWS_LAMBDA_EXEC_WRAPPER = currentEnv['AWS_LAMBDA_EXEC_WRAPPER']
303303
}
304304

305+
if (getLogger().logLevelEnabled('debug')) {
306+
updatedEnv.RUST_LOG = 'debug'
307+
}
308+
305309
// Create Lambda client using AWS SDK
306310
const lambda = this.getLambdaClient(region)
307311

packages/core/src/lambda/remoteDebugging/ldkLayers.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ export const regionToAccount: RegionAccountMapping = {
3131
}
3232

3333
// Global layer version
34-
const globalLayerVersion = 1
34+
const globalLayerVersion = 2
3535

3636
export function getRemoteDebugLayerForArch(region: string, arch: string): string | undefined {
3737
const account = regionToAccount[region]

packages/core/src/lambda/vue/remoteInvoke/invokeLambda.ts

Lines changed: 157 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -102,19 +102,12 @@ export interface RuntimeDebugSettings {
102102
// UI state sub-interface
103103
export interface UIState {
104104
isCollapsed: boolean
105-
showNameInput: boolean
106-
payload: string
107105
extraRegionInfo: string
108106
}
109107

110108
// Payload/Event handling sub-interface
111109
export interface PayloadData {
112-
selectedSampleRequest: string
113110
sampleText: string
114-
selectedFile: string
115-
selectedFilePath: string
116-
selectedTestEvent: string
117-
newTestEventName: string
118111
}
119112

120113
export interface RemoteInvokeData {
@@ -377,7 +370,7 @@ export class RemoteInvokeWebview extends VueWebview {
377370
this.data.LambdaFunctionNode?.configuration.Handler
378371
)
379372
getLogger().warn(warning)
380-
void vscode.window.showWarningMessage(warning)
373+
void showMessage('warn', warning)
381374
}
382375
return fileLocations[0].fsPath
383376
}
@@ -443,22 +436,166 @@ export class RemoteInvokeWebview extends VueWebview {
443436
}
444437

445438
public async listRemoteTestEvents(functionArn: string, region: string): Promise<string[]> {
446-
const params: SamCliRemoteTestEventsParameters = {
447-
functionArn: functionArn,
448-
operation: TestEventsOperation.List,
449-
region: region,
439+
try {
440+
const params: SamCliRemoteTestEventsParameters = {
441+
functionArn: functionArn,
442+
operation: TestEventsOperation.List,
443+
region: region,
444+
}
445+
const result = await this.remoteTestEvents(params)
446+
return result.split('\n').filter((event) => event.trim() !== '')
447+
} catch (error) {
448+
// Suppress "lambda-testevent-schemas registry not found" error - this is normal when no test events exist
449+
const errorMessage = error instanceof Error ? error.message : String(error)
450+
if (
451+
errorMessage.includes('lambda-testevent-schemas registry not found') ||
452+
errorMessage.includes('There are no saved events')
453+
) {
454+
getLogger().debug('No remote test events found for function: %s', functionArn)
455+
return []
456+
}
457+
// Re-throw other errors
458+
throw error
459+
}
460+
}
461+
462+
public async selectRemoteTestEvent(functionArn: string, region: string): Promise<string | undefined> {
463+
let events: string[] = []
464+
465+
try {
466+
events = await this.listRemoteTestEvents(functionArn, region)
467+
} catch (error) {
468+
getLogger().error('Failed to list remote test events: %O', error)
469+
void showMessage(
470+
'error',
471+
localize('AWS.lambda.remoteInvoke.failedToListEvents', 'Failed to list remote test events')
472+
)
473+
return undefined
474+
}
475+
476+
if (events.length === 0) {
477+
void showMessage(
478+
'info',
479+
localize(
480+
'AWS.lambda.remoteInvoke.noRemoteEvents',
481+
'No remote test events found. You can create one using "Save as remote event".'
482+
)
483+
)
484+
return undefined
485+
}
486+
487+
const selected = await vscode.window.showQuickPick(events, {
488+
placeHolder: localize('AWS.lambda.remoteInvoke.selectRemoteEvent', 'Select a remote test event'),
489+
title: localize('AWS.lambda.remoteInvoke.loadRemoteEvent', 'Load Remote Test Event'),
490+
})
491+
492+
if (selected) {
493+
const eventData = {
494+
name: selected,
495+
region: region,
496+
arn: functionArn,
497+
}
498+
const resp = await this.getRemoteTestEvents(eventData)
499+
return resp
450500
}
451-
const result = await this.remoteTestEvents(params)
452-
return result.split('\n')
501+
502+
return undefined
453503
}
454504

455-
public async createRemoteTestEvents(putEvent: Event) {
505+
public async saveRemoteTestEvent(
506+
functionArn: string,
507+
region: string,
508+
eventContent: string
509+
): Promise<string | undefined> {
510+
let events: string[] = []
511+
512+
try {
513+
events = await this.listRemoteTestEvents(functionArn, region)
514+
} catch (error) {
515+
// Log error but continue - user can still create new events
516+
getLogger().debug('Failed to list existing remote test events (may not exist yet): %O', error)
517+
}
518+
519+
// Create options for quickpick
520+
const createNewOption = '$(add) Create new test event'
521+
const options = events.length > 0 ? [createNewOption, ...events] : [createNewOption]
522+
523+
const selected = await vscode.window.showQuickPick(options, {
524+
placeHolder: localize(
525+
'AWS.lambda.remoteInvoke.saveEventChoice',
526+
'Create new or overwrite existing test event'
527+
),
528+
title: localize('AWS.lambda.remoteInvoke.saveRemoteEvent', 'Save as Remote Event'),
529+
})
530+
531+
if (!selected) {
532+
return undefined
533+
}
534+
535+
let eventName: string | undefined
536+
537+
if (selected === createNewOption) {
538+
// Prompt for new event name
539+
eventName = await vscode.window.showInputBox({
540+
prompt: localize('AWS.lambda.remoteInvoke.enterEventName', 'Enter a name for the test event'),
541+
placeHolder: localize('AWS.lambda.remoteInvoke.eventNamePlaceholder', 'MyTestEvent'),
542+
validateInput: (value) => {
543+
if (!value || value.trim() === '') {
544+
return localize('AWS.lambda.remoteInvoke.eventNameRequired', 'Event name is required')
545+
}
546+
if (events.includes(value)) {
547+
return localize(
548+
'AWS.lambda.remoteInvoke.eventNameExists',
549+
'An event with this name already exists'
550+
)
551+
}
552+
return undefined
553+
},
554+
})
555+
} else {
556+
// Use selected existing event name
557+
const confirm = await showConfirmationMessage({
558+
prompt: localize(
559+
'AWS.lambda.remoteInvoke.overwriteEvent',
560+
'Overwrite existing test event "{0}"?',
561+
selected
562+
),
563+
confirm: localize('AWS.lambda.remoteInvoke.overwrite', 'Overwrite'),
564+
cancel: 'Cancel',
565+
type: 'warning',
566+
})
567+
568+
if (confirm) {
569+
eventName = selected
570+
}
571+
}
572+
573+
if (eventName) {
574+
// Use force flag when overwriting existing events
575+
const isOverwriting = selected !== createNewOption
576+
const params: SamCliRemoteTestEventsParameters = {
577+
functionArn: functionArn,
578+
operation: TestEventsOperation.Put,
579+
name: eventName,
580+
eventSample: eventContent,
581+
region: region,
582+
force: isOverwriting,
583+
}
584+
await this.remoteTestEvents(params)
585+
return eventName
586+
}
587+
588+
return undefined
589+
}
590+
591+
public async createRemoteTestEvents(putEvent: Event, force: boolean = false) {
456592
const params: SamCliRemoteTestEventsParameters = {
457593
functionArn: putEvent.arn,
458594
operation: TestEventsOperation.Put,
459595
name: putEvent.name,
460596
eventSample: putEvent.event,
461597
region: putEvent.region,
598+
force: force,
462599
}
463600
return await this.remoteTestEvents(params)
464601
}
@@ -549,7 +686,8 @@ export class RemoteInvokeWebview extends VueWebview {
549686
// this serves as a lock for invoke
550687
public checkReadyToInvoke(): boolean {
551688
if (this.isInvoking) {
552-
void vscode.window.showWarningMessage(
689+
void showMessage(
690+
'warn',
553691
localize(
554692
'AWS.lambda.remoteInvoke.invokeInProgress',
555693
'A remote invoke is already in progress, please wait for previous invoke, or remove debug setup'
@@ -558,12 +696,14 @@ export class RemoteInvokeWebview extends VueWebview {
558696
return false
559697
}
560698
if (this.isStartingDebug) {
561-
void vscode.window.showWarningMessage(
699+
void showMessage(
700+
'warn',
562701
localize(
563702
'AWS.lambda.remoteInvoke.debugSetupInProgress',
564703
'A debugger setup is already in progress, please wait for previous setup to complete, or remove debug setup'
565704
)
566705
)
706+
return false
567707
}
568708
return true
569709
}

0 commit comments

Comments
 (0)