public async makeConfig()

in src/shared/sam/debugger/awsSamDebugger.ts [330:603]


    public async makeConfig(
        folder: vscode.WorkspaceFolder | undefined,
        config: AwsSamDebuggerConfiguration,
        token?: vscode.CancellationToken
    ): Promise<SamLaunchRequestArgs | undefined> {
        if (token?.isCancellationRequested) {
            return undefined
        }
        folder =
            folder ?? (vscode.workspace.workspaceFolders?.length ? vscode.workspace.workspaceFolders[0] : undefined)
        if (!folder) {
            getLogger().error(`SAM debug: no workspace folder`)
            vscode.window.showErrorMessage(
                localize(
                    'AWS.sam.debugger.noWorkspace',
                    '{0} SAM debug: choose a workspace, then try again',
                    getIdeProperties().company
                )
            )
            return undefined
        }

        // If "request" field is missing this means launch.json does not exist.
        // User/vscode expects us to dynamically decide defaults if possible.
        const hasLaunchJson = !!config.request
        const configValidator: AwsSamDebugConfigurationValidator = new DefaultAwsSamDebugConfigurationValidator(folder)

        if (!hasLaunchJson) {
            vscode.window
                .showErrorMessage(
                    localize(
                        'AWS.sam.debugger.noLaunchJson',
                        '{0} SAM: To debug a Lambda locally, create a launch.json from the Run panel, then select a configuration.',
                        getIdeProperties().company
                    ),
                    localize('AWS.gotoRunPanel', 'Run panel')
                )
                .then(async result => {
                    if (!result) {
                        return
                    }
                    await vscode.commands.executeCommand('workbench.view.debug')
                })
            return undefined
        } else {
            const rv = configValidator.validate(config)
            if (!rv.isValid) {
                getLogger().error(`SAM debug: invalid config: ${rv.message!}`)
                vscode.window.showErrorMessage(rv.message!)
                return undefined
            } else if (rv.message) {
                vscode.window.showInformationMessage(rv.message)
            }
            getLogger().verbose(`SAM debug: config: ${JSON.stringify(config.name)}`)
        }

        const editor = vscode.window.activeTextEditor
        const templateInvoke = config.invokeTarget as TemplateTargetProperties
        const template = getTemplate(folder, config)
        const templateResource = getTemplateResource(folder, config)
        const codeRoot = getCodeRoot(folder, config)
        const architecture = getArchitecture(template, templateResource, config.invokeTarget)
        // Handler is the only field that we need to parse refs for.
        // This is necessary for Python debugging since we have to create the temporary entry file
        // Other refs can fail; SAM will handle them.
        const handlerName = getHandlerName(folder, config)

        config.baseBuildDir = resolve(folder.uri.fsPath, config.sam?.buildDir ?? (await makeTemporaryToolkitFolder()))
        fs.ensureDir(config.baseBuildDir)

        if (templateInvoke?.templatePath) {
            // Normalize to absolute path.
            // TODO: If path is relative, it is relative to launch.json (i.e. .vscode directory).
            templateInvoke.templatePath = pathutil.normalize(tryGetAbsolutePath(folder, templateInvoke.templatePath))
        } else if (config.invokeTarget.target === 'code') {
            const codeConfig = config as SamLaunchRequestArgs & { invokeTarget: { target: 'code' } }
            // 'projectRoot' may be a relative path
            // Older code left this property as relative, but there's no benefit in doing that since it's relative to the workspace
            codeConfig.invokeTarget.projectRoot = pathutil.normalize(
                resolve(folder.uri.fsPath, config.invokeTarget.projectRoot)
            )
            templateInvoke.templatePath = await makeInputTemplate(codeConfig)
        }

        const isZip = CloudFormation.isZipLambdaResource(templateResource?.Properties)
        const runtime: string | undefined =
            config.lambda?.runtime ??
            (template && isZip
                ? CloudFormation.getStringForProperty(templateResource?.Properties, 'Runtime', template)
                : undefined) ??
            getDefaultRuntime(getRuntimeFamily(editor?.document?.languageId ?? 'unknown'))

        const lambdaMemory =
            (template
                ? CloudFormation.getNumberForProperty(templateResource?.Properties, 'MemorySize', template)
                : undefined) ?? config.lambda?.memoryMb
        const lambdaTimeout =
            (template
                ? CloudFormation.getNumberForProperty(templateResource?.Properties, 'Timeout', template)
                : undefined) ?? config.lambda?.timeoutSec

        // TODO: Remove this when min sam version is > 1.13.0
        if (!isZip) {
            const samCliVersion = await getSamCliVersion(this.ctx.samCliContext())
            if (semver.lt(samCliVersion, MINIMUM_SAM_CLI_VERSION_INCLUSIVE_FOR_IMAGE_SUPPORT)) {
                getLogger().error(`SAM debug: version (${samCliVersion}) too low for Image lambdas: ${config})`)
                vscode.window.showErrorMessage(
                    localize(
                        'AWS.output.sam.no.image.support',
                        'Support for Image-based Lambdas requires a minimum SAM CLI version of 1.13.0.'
                    )
                )
                return undefined
            }
        }

        if (!runtime) {
            getLogger().error(`SAM debug: failed to launch config (missing runtime): ${config})`)
            vscode.window.showErrorMessage(
                localize(
                    'AWS.sam.debugger.failedLaunch.missingRuntime',
                    'Toolkit could not infer a runtime for config: {0}. Add a "lambda.runtime" field to your launch configuration.',
                    config.name
                )
            )
            return undefined
        }

        // SAM CLI versions before 1.18.1 do not work correctly for Go debugging.
        // TODO: remove this when min sam version is >= 1.18.1
        if (goRuntimes.includes(runtime) && !config.noDebug) {
            const samCliVersion = await getSamCliVersion(this.ctx.samCliContext())
            if (semver.lt(samCliVersion, MINIMUM_SAM_CLI_VERSION_INCLUSIVE_FOR_GO_SUPPORT)) {
                vscode.window.showWarningMessage(
                    localize(
                        'AWS.output.sam.local.no.go.support',
                        'Debugging go1.x lambdas requires a minimum SAM CLI version of {0}. Function will run locally without debug.',
                        MINIMUM_SAM_CLI_VERSION_INCLUSIVE_FOR_GO_SUPPORT
                    )
                )
                config.noDebug = true
            }
        }

        const runtimeFamily = getFamily(runtime)
        const documentUri =
            vscode.window.activeTextEditor?.document.uri ??
            // XXX: don't know what URI to choose...
            vscode.Uri.parse(templateInvoke.templatePath!)

        let awsCredentials: Credentials | undefined

        if (config.aws?.credentials) {
            const credentials = fromString(config.aws.credentials)
            try {
                awsCredentials = await getCredentialsFromStore(credentials, this.ctx.credentialsStore)
            } catch (err) {
                getLogger().error(err as Error)
                notifyUserInvalidCredentials(credentials)
                return undefined
            }
        }

        if (config.api) {
            config.api.headers = {
                'content-type': 'application/json',
                ...(config.api.headers ? config.api.headers : {}),
            }
        }

        let parameterOverrideArr: string[] | undefined
        const params = config.sam?.template?.parameters
        if (params) {
            parameterOverrideArr = []
            for (const key of Object.keys(params)) {
                parameterOverrideArr.push(`${key}=${params[key].toString()}`)
            }
        }

        // TODO: Let the OS (or SAM CLI) assign the port, then we need to
        // scrape SAM CLI to find the port that was actually used?
        const apiPort = config.invokeTarget.target === 'api' ? await getStartPort() : undefined
        const debugPort = config.noDebug ? undefined : await getStartPort(apiPort ? apiPort + 1 : undefined)
        let launchConfig: SamLaunchRequestArgs = {
            ...config,
            request: 'attach',
            codeRoot: codeRoot ?? '',
            workspaceFolder: folder,
            runtime: runtime,
            runtimeFamily: runtimeFamily,
            handlerName: handlerName,
            documentUri: documentUri,
            templatePath: pathutil.normalize(templateInvoke?.templatePath),
            eventPayloadFile: '', // Populated by makeConfig().
            envFile: '', // Populated by makeConfig().
            apiPort: apiPort,
            debugPort: debugPort,
            lambda: {
                ...config.lambda,
                memoryMb: lambdaMemory,
                timeoutSec: lambdaTimeout,
                environmentVariables: { ...config.lambda?.environmentVariables },
            },
            awsCredentials: awsCredentials,
            parameterOverrides: parameterOverrideArr,
            useIkpdb: isCloud9() || !!(config as any).useIkpdb,
            architecture: architecture,
        }

        //
        // Configure and launch.
        //
        // 1. prepare a bunch of arguments
        // 2. do `sam build`
        // 3. do `sam local invoke`
        //
        switch (launchConfig.runtimeFamily) {
            case RuntimeFamily.NodeJS: {
                // Make a NodeJS launch-config from the generic config.
                launchConfig = await tsDebug.makeTypescriptConfig(launchConfig)
                break
            }
            case RuntimeFamily.Python: {
                // Make a Python launch-config from the generic config.
                launchConfig = await pythonDebug.makePythonDebugConfig(launchConfig)
                break
            }
            case RuntimeFamily.DotNetCore: {
                // Make a DotNet launch-config from the generic config.
                launchConfig = await csharpDebug.makeCsharpConfig(launchConfig)
                break
            }
            case RuntimeFamily.Go: {
                launchConfig = await goDebug.makeGoConfig(launchConfig)
                break
            }
            case RuntimeFamily.Java: {
                // Make a Java launch-config from the generic config.
                launchConfig = await javaDebug.makeJavaConfig(launchConfig)
                break
            }
            default: {
                getLogger().error(`SAM debug: unknown runtime: ${runtime})`)
                vscode.window.showErrorMessage(
                    localize(
                        'AWS.sam.debugger.invalidRuntime',
                        '{0} SAM debug: unknown runtime: {1}',
                        getIdeProperties().company,
                        runtime
                    )
                )
                return undefined
            }
        }
        await makeJsonFiles(launchConfig)

        // Set the type, then vscode will pass the config to SamDebugSession.attachRequest().
        // (Registered in sam/activation.ts which calls registerDebugAdapterDescriptorFactory()).
        // By this point launchConfig.request is now set to "attach" (not "direct-invoke").
        launchConfig.type = AWS_SAM_DEBUG_TYPE

        if (launchConfig.request !== 'attach' && launchConfig.request !== 'launch') {
            // The "request" field must be updated so that it routes to the
            // DebugAdapter (SamDebugSession.attachRequest()), else this will
            // just cycle back (and it indicates a bug in the config logic).
            throw Error(
                `resolveDebugConfiguration: launchConfig was not correctly resolved before return: ${JSON.stringify(
                    launchConfig
                )}`
            )
        }

        return launchConfig
    }