@@ -7,7 +7,6 @@ import * as semver from 'semver'
7
7
import * as vscode from 'vscode'
8
8
import * as fs from 'fs-extra'
9
9
import * as nls from 'vscode-nls'
10
- import { Runtime } from 'aws-sdk/clients/lambda'
11
10
import {
12
11
getCodeRoot ,
13
12
getHandlerName ,
@@ -17,6 +16,7 @@ import {
17
16
GoDebugConfiguration ,
18
17
getTemplate ,
19
18
getArchitecture ,
19
+ isImageLambdaConfig ,
20
20
} from '../../../lambda/local/debugConfiguration'
21
21
import {
22
22
Architecture ,
@@ -53,10 +53,9 @@ import { makeInputTemplate, makeJsonFiles } from '../localLambdaRunner'
53
53
import { SamLocalInvokeCommand } from '../cli/samCliLocalInvoke'
54
54
import { getCredentialsFromStore } from '../../../credentials/credentialsStore'
55
55
import { fromString } from '../../../credentials/providers/credentials'
56
- import { notifyUserInvalidCredentials } from '../../../credentials/credentialsUtilities'
57
56
import { Credentials } from '@aws-sdk/types'
58
57
import { CloudFormation } from '../../cloudformation/cloudformation'
59
- import { getSamCliVersion } from '../cli/samCliContext'
58
+ import { getSamCliContext , getSamCliVersion } from '../cli/samCliContext'
60
59
import {
61
60
MINIMUM_SAM_CLI_VERSION_INCLUSIVE_FOR_IMAGE_SUPPORT ,
62
61
MINIMUM_SAM_CLI_VERSION_INCLUSIVE_FOR_GO_SUPPORT ,
@@ -65,9 +64,53 @@ import { getIdeProperties, isCloud9 } from '../../extensionUtilities'
65
64
import { resolve } from 'path'
66
65
import globals from '../../extensionGlobals'
67
66
import { LoginManager } from '../../../credentials/loginManager'
67
+ import { Runtime , telemetry } from '../../telemetry/telemetry'
68
+ import { ErrorInformation , isUserCancelledError , ToolkitError } from '../../errors'
69
+ import { openLaunchJsonFile } from './commands/addSamDebugConfiguration'
70
+ import { Logging } from '../../logger/commands'
71
+ import { credentialHelpUrl } from '../../constants'
68
72
69
73
const localize = nls . loadMessageBundle ( )
70
74
75
+ interface NotificationButton < T = unknown > {
76
+ readonly label : string
77
+ readonly onClick : ( ) => Promise < T > | T
78
+ }
79
+
80
+ class SamLaunchRequestError extends ToolkitError . named ( 'SamLaunchRequestError' ) {
81
+ private readonly buttons : NotificationButton [ ]
82
+
83
+ public constructor ( message : string , info ?: ErrorInformation & { readonly extraButtons ?: NotificationButton [ ] } ) {
84
+ super ( message , info )
85
+ this . buttons = info ?. extraButtons ?? [
86
+ {
87
+ label : localize ( 'AWS.generic.message.openConfig' , 'Open Launch Config' ) ,
88
+ onClick : openLaunchJsonFile ,
89
+ } ,
90
+ ]
91
+ }
92
+
93
+ public async showNotification ( ) : Promise < void > {
94
+ if ( isUserCancelledError ( this ) ) {
95
+ getLogger ( ) . verbose ( `SAM run/debug: user cancelled` )
96
+ return
97
+ }
98
+
99
+ const logId = getLogger ( ) . error ( this . trace )
100
+
101
+ const viewLogsButton = {
102
+ label : localize ( 'AWS.generic.message.viewLogs' , 'View Logs...' ) ,
103
+ onClick : ( ) => Logging . declared . viewLogsAtMessage . execute ( logId ) ,
104
+ }
105
+
106
+ const buttonsWithLogs = [ viewLogsButton , ...this . buttons ]
107
+
108
+ await vscode . window . showErrorMessage ( this . message , ...buttonsWithLogs . map ( b => b . label ) ) . then ( resp => {
109
+ return buttonsWithLogs . find ( ( { label } ) => label === resp ) ?. onClick ( )
110
+ } )
111
+ }
112
+ }
113
+
71
114
/**
72
115
* SAM-specific launch attributes (which are not part of the DAP).
73
116
*
@@ -310,12 +353,31 @@ export class SamDebugConfigProvider implements vscode.DebugConfigurationProvider
310
353
folder : vscode . WorkspaceFolder | undefined ,
311
354
config : AwsSamDebuggerConfiguration ,
312
355
token ?: vscode . CancellationToken
313
- ) : Promise < undefined > {
314
- const resolvedConfig = await this . makeConfig ( folder , config , token )
315
- if ( ! resolvedConfig ) {
316
- return undefined
356
+ ) : Promise < void > {
357
+ try {
358
+ if ( config . invokeTarget . target === 'api' ) {
359
+ await telemetry . apigateway_invokeLocal . run ( async span => {
360
+ const resolved = await this . makeConfig ( folder , config , token )
361
+ span . record ( { httpMethod : resolved . api ?. httpMethod } )
362
+
363
+ return this . invokeConfig ( resolved )
364
+ } )
365
+ } else {
366
+ await telemetry . lambda_invokeLocal . run ( async ( ) => {
367
+ const resolved = await this . makeConfig ( folder , config , token )
368
+
369
+ return this . invokeConfig ( resolved )
370
+ } )
371
+ }
372
+ } catch ( err ) {
373
+ if ( err instanceof SamLaunchRequestError ) {
374
+ err . showNotification ( )
375
+ } else if ( err instanceof ToolkitError ) {
376
+ new SamLaunchRequestError ( err . message , { ...err } ) . showNotification ( )
377
+ } else {
378
+ SamLaunchRequestError . chain ( err , 'Failed to run launch configuration' ) . showNotification ( )
379
+ }
317
380
}
318
- await this . invokeConfig ( resolvedConfig )
319
381
}
320
382
321
383
/**
@@ -332,22 +394,21 @@ export class SamDebugConfigProvider implements vscode.DebugConfigurationProvider
332
394
folder : vscode . WorkspaceFolder | undefined ,
333
395
config : AwsSamDebuggerConfiguration ,
334
396
token ?: vscode . CancellationToken
335
- ) : Promise < SamLaunchRequestArgs | undefined > {
397
+ ) : Promise < SamLaunchRequestArgs > {
336
398
if ( token ?. isCancellationRequested ) {
337
- return undefined
399
+ throw new ToolkitError ( 'Cancellation requested' , { cancelled : true } )
338
400
}
401
+
339
402
folder =
340
403
folder ?? ( vscode . workspace . workspaceFolders ?. length ? vscode . workspace . workspaceFolders [ 0 ] : undefined )
341
404
if ( ! folder ) {
342
- getLogger ( ) . error ( `SAM debug: no workspace folder` )
343
- vscode . window . showErrorMessage (
344
- localize (
345
- 'AWS.sam.debugger.noWorkspace' ,
346
- '{0} SAM debug: choose a workspace, then try again' ,
347
- getIdeProperties ( ) . company
348
- )
405
+ const message = localize (
406
+ 'AWS.sam.debugger.noWorkspace' ,
407
+ 'Choose a workspace, then try again' ,
408
+ getIdeProperties ( ) . company
349
409
)
350
- return undefined
410
+
411
+ throw new SamLaunchRequestError ( message , { code : 'NoWorkspaceFolder' , extraButtons : [ ] } )
351
412
}
352
413
353
414
// If "request" field is missing this means launch.json does not exist.
@@ -356,28 +417,24 @@ export class SamDebugConfigProvider implements vscode.DebugConfigurationProvider
356
417
const configValidator : AwsSamDebugConfigurationValidator = new DefaultAwsSamDebugConfigurationValidator ( folder )
357
418
358
419
if ( ! hasLaunchJson ) {
359
- vscode . window
360
- . showErrorMessage (
361
- localize (
362
- 'AWS.sam.debugger.noLaunchJson' ,
363
- '{0} SAM: To debug a Lambda locally, create a launch.json from the Run panel, then select a configuration.' ,
364
- getIdeProperties ( ) . company
365
- ) ,
366
- localize ( 'AWS.gotoRunPanel' , 'Run panel' )
367
- )
368
- . then ( async result => {
369
- if ( ! result ) {
370
- return
371
- }
372
- await vscode . commands . executeCommand ( 'workbench.view.debug' )
373
- } )
374
- return undefined
420
+ const message = localize (
421
+ 'AWS.sam.debugger.noLaunchJson' ,
422
+ 'To debug a Lambda locally, create a launch.json from the Run panel, then select a configuration.'
423
+ )
424
+
425
+ throw new SamLaunchRequestError ( message , {
426
+ code : 'NoLaunchConfig' ,
427
+ extraButtons : [
428
+ {
429
+ label : localize ( 'AWS.gotoRunPanel' , 'Run panel' ) ,
430
+ onClick : ( ) => vscode . commands . executeCommand ( 'workbench.view.debug' ) ,
431
+ } ,
432
+ ] ,
433
+ } )
375
434
} else {
376
435
const rv = configValidator . validate ( config )
377
436
if ( ! rv . isValid ) {
378
- getLogger ( ) . error ( `SAM debug: invalid config: ${ rv . message ! } ` )
379
- vscode . window . showErrorMessage ( rv . message ! )
380
- return undefined
437
+ throw new ToolkitError ( `Invalid launch configuration: ${ rv . message } ` , { code : 'BadLaunchConfig' } )
381
438
} else if ( rv . message ) {
382
439
vscode . window . showInformationMessage ( rv . message )
383
440
}
@@ -433,27 +490,23 @@ export class SamDebugConfigProvider implements vscode.DebugConfigurationProvider
433
490
if ( ! isZip ) {
434
491
const samCliVersion = await getSamCliVersion ( this . ctx . samCliContext ( ) )
435
492
if ( semver . lt ( samCliVersion , MINIMUM_SAM_CLI_VERSION_INCLUSIVE_FOR_IMAGE_SUPPORT ) ) {
436
- getLogger ( ) . error ( `SAM debug: version (${ samCliVersion } ) too low for Image lambdas: ${ config } )` )
437
- vscode . window . showErrorMessage (
438
- localize (
439
- 'AWS.output.sam.no.image.support' ,
440
- 'Support for Image-based Lambdas requires a minimum SAM CLI version of 1.13.0.'
441
- )
493
+ const message = localize (
494
+ 'AWS.output.sam.no.image.support' ,
495
+ 'Support for Image-based Lambdas requires a minimum SAM CLI version of 1.13.0.'
442
496
)
443
- return undefined
497
+
498
+ throw new SamLaunchRequestError ( message , { code : 'UnsupportedSamVersion' , details : { samCliVersion } } )
444
499
}
445
500
}
446
501
447
502
if ( ! runtime ) {
448
- getLogger ( ) . error ( `SAM debug: failed to launch config (missing runtime): ${ config } )` )
449
- vscode . window . showErrorMessage (
450
- localize (
451
- 'AWS.sam.debugger.failedLaunch.missingRuntime' ,
452
- 'Toolkit could not infer a runtime for config: {0}. Add a "lambda.runtime" field to your launch configuration.' ,
453
- config . name
454
- )
503
+ const message = localize (
504
+ 'AWS.sam.debugger.failedLaunch.missingRuntime' ,
505
+ 'Toolkit could not infer a runtime for config: {0}. Add a "lambda.runtime" field to your launch configuration.' ,
506
+ config . name
455
507
)
456
- return undefined
508
+
509
+ throw new SamLaunchRequestError ( message , { code : 'MissingRuntime' } )
457
510
}
458
511
459
512
// SAM CLI versions before 1.18.1 do not work correctly for Go debugging.
@@ -494,11 +547,22 @@ export class SamDebugConfigProvider implements vscode.DebugConfigurationProvider
494
547
if ( fromStore ) {
495
548
awsCredentials = fromStore
496
549
} else {
497
- getLogger ( ) . error (
498
- `SAM debug: invalid "aws.credentials" value in launch config: ${ config . aws . credentials } `
499
- )
500
- notifyUserInvalidCredentials ( config . aws . credentials )
501
- return undefined
550
+ const credentialsId = config . aws . credentials
551
+ const getHelp = localize ( 'AWS.generic.message.getHelp' , 'Get Help...' )
552
+ // TODO: getHelp page for Cloud9.
553
+ const extraButtons = isCloud9 ( )
554
+ ? [ ]
555
+ : [
556
+ {
557
+ label : getHelp ,
558
+ onClick : ( ) => vscode . env . openExternal ( vscode . Uri . parse ( credentialHelpUrl ) ) ,
559
+ } ,
560
+ ]
561
+
562
+ throw new SamLaunchRequestError ( `Invalid credentials found in launch configuration: ${ credentialsId } ` , {
563
+ code : 'InvalidCredentials' ,
564
+ extraButtons,
565
+ } )
502
566
}
503
567
}
504
568
@@ -527,7 +591,7 @@ export class SamDebugConfigProvider implements vscode.DebugConfigurationProvider
527
591
request : 'attach' ,
528
592
codeRoot : codeRoot ?? '' ,
529
593
workspaceFolder : folder ,
530
- runtime : runtime ,
594
+ runtime : runtime as Runtime ,
531
595
runtimeFamily : runtimeFamily ,
532
596
handlerName : handlerName ,
533
597
documentUri : documentUri ,
@@ -581,16 +645,13 @@ export class SamDebugConfigProvider implements vscode.DebugConfigurationProvider
581
645
break
582
646
}
583
647
default : {
584
- getLogger ( ) . error ( `SAM debug: unknown runtime: ${ runtime } )` )
585
- vscode . window . showErrorMessage (
586
- localize (
587
- 'AWS.sam.debugger.invalidRuntime' ,
588
- '{0} SAM debug: unknown runtime: {1}' ,
589
- getIdeProperties ( ) . company ,
590
- runtime
591
- )
648
+ const message = localize (
649
+ 'AWS.sam.debugger.invalidRuntime' ,
650
+ 'Unknown or unsupported runtime: {0}' ,
651
+ runtime
592
652
)
593
- return undefined
653
+
654
+ throw new ToolkitError ( message , { code : 'UnsupportedRuntime' } )
594
655
}
595
656
}
596
657
await makeJsonFiles ( launchConfig )
@@ -618,6 +679,14 @@ export class SamDebugConfigProvider implements vscode.DebugConfigurationProvider
618
679
* Performs the EXECUTE phase of SAM run/debug.
619
680
*/
620
681
public async invokeConfig ( config : SamLaunchRequestArgs ) : Promise < SamLaunchRequestArgs > {
682
+ telemetry . record ( {
683
+ debug : ! config . noDebug ,
684
+ runtime : config . runtime as Runtime ,
685
+ lambdaArchitecture : config . architecture ,
686
+ lambdaPackageType : isImageLambdaConfig ( config ) ? 'Image' : 'Zip' ,
687
+ version : await getSamCliVersion ( getSamCliContext ( ) ) ,
688
+ } )
689
+
621
690
await LoginManager . tryAutoConnect ( )
622
691
switch ( config . runtimeFamily ) {
623
692
case RuntimeFamily . NodeJS : {
@@ -642,7 +711,7 @@ export class SamDebugConfigProvider implements vscode.DebugConfigurationProvider
642
711
return await javaDebug . invokeJavaLambda ( this . ctx , config )
643
712
}
644
713
default : {
645
- throw Error ( `unknown runtimeFamily: ${ config . runtimeFamily } ` )
714
+ throw new Error ( `unknown runtimeFamily: ${ config . runtimeFamily } ` )
646
715
}
647
716
}
648
717
}
0 commit comments