Skip to content

Commit

Permalink
Merge master into feature/cwltail
Browse files Browse the repository at this point in the history
  • Loading branch information
aws-toolkit-automation authored Nov 7, 2024
2 parents 069c600 + 4add487 commit 50cbeee
Show file tree
Hide file tree
Showing 33 changed files with 133 additions and 36 deletions.
1 change: 1 addition & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@ module.exports = {
'aws-toolkits/no-string-exec-for-child-process': 'error',
'aws-toolkits/no-console-log': 'error',
'aws-toolkits/no-json-stringify-in-log': 'error',
'aws-toolkits/no-printf-mismatch': 'error',
'no-restricted-imports': [
'error',
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ export function emitTelemetryMessageHandler(message: EmitTelemetryMessage) {
return
}
} catch (e) {
getLogger().error('Could not log telemetry for App Composer', e)
getLogger().error('Could not log telemetry for App Composer %O', e)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ export async function generateDeployedNode(
.Configuration as Lambda.FunctionConfiguration
newDeployedResource = new LambdaFunctionNode(lambdaNode, regionCode, configuration)
} catch (error: any) {
getLogger().error('Error getting Lambda configuration')
getLogger().error('Error getting Lambda configuration %O', error)
throw ToolkitError.chain(error, 'Error getting Lambda configuration', {
code: 'lambdaClientError',
})
Expand Down Expand Up @@ -153,7 +153,7 @@ export async function generateDeployedNode(
}
default:
newDeployedResource = new DeployedResourceNode(deployedResource)
getLogger().info('Details are missing or are incomplete for:', deployedResource)
getLogger().info('Details are missing or are incomplete for: %O', deployedResource)
return [
createPlaceholderItem(
localize('AWS.appBuilder.explorerNode.noApps', '[This resource is not yet supported.]')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export async function getStackName(projectRoot: vscode.Uri): Promise<any> {
} catch (error: any) {
switch (error.code) {
case SamConfigErrorCode.samNoConfigFound:
getLogger().info('No stack name or region information available in samconfig.toml', error)
getLogger().info('No stack name or region information available in samconfig.toml: %O', error)
break
case SamConfigErrorCode.samConfigParseError:
getLogger().error(`Error getting stack name or region information: ${error.message}`, error)
Expand Down
6 changes: 3 additions & 3 deletions packages/core/src/awsService/iot/commands/createCert.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,14 +115,14 @@ async function saveCredentials(
const publicKeyExists = await fileExists(publicKeyPath)

if (certExists) {
getLogger().error('Certificate path {0} already exists', certPath)
getLogger().error('Certificate path %s already exists', certPath)
void vscode.window.showErrorMessage(
localize('AWS.iot.createCert.error', 'Failed to create certificate. Path {0} already exists.', certPath)
)
return false
}
if (privateKeyExists) {
getLogger().error('Key path {0} already exists', privateKeyPath)
getLogger().error('Key path %s already exists', privateKeyPath)
void vscode.window.showErrorMessage(
localize(
'AWS.iot.createCert.error',
Expand All @@ -133,7 +133,7 @@ async function saveCredentials(
return false
}
if (publicKeyExists) {
getLogger().error('Key path {0} already exists', publicKeyPath)
getLogger().error('Key path %s already exists', publicKeyPath)
void vscode.window.showErrorMessage(
localize(
'AWS.iot.createCert.error',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ export class RedshiftNode extends AWSTreeNodeBase implements LoadMoreNode {
newServerlessToken = response.nextToken ?? ''
}
} catch (error) {
getLogger().error("Serverless workgroup operation isn't supported or failed:", error)
getLogger().error("Serverless workgroup operation isn't supported or failed: %O", error)
// Continue without interrupting the provisioned cluster loading
}
}
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/awsService/s3/commands/uploadFile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -415,7 +415,7 @@ export async function promptUserForBucket(
try {
allBuckets = await s3client.listAllBuckets()
} catch (e) {
getLogger().error('Failed to list buckets from client', e)
getLogger().error('Failed to list buckets from client %O', e)
void vscode.window.showErrorMessage(
localize('AWS.message.error.promptUserForBucket.listBuckets', 'Failed to list buckets from client')
)
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/codewhisperer/activation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -618,6 +618,6 @@ export async function enableDefaultConfigCloud9() {
await editorSettings.update('acceptSuggestionOnEnter', 'on', vscode.ConfigurationTarget.Global)
await editorSettings.update('snippetSuggestions', 'top', vscode.ConfigurationTarget.Global)
} catch (error) {
getLogger().error('amazonq: Failed to update user settings', error)
getLogger().error('amazonq: Failed to update user settings %O', error)
}
}
2 changes: 1 addition & 1 deletion packages/core/src/codewhisperer/client/agent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,6 @@ export function initializeNetworkAgent(): void {
}
} catch (error) {
// Log any errors in the patching logic
getLogger().error('Failed to patch http agent', error)
getLogger().error('Failed to patch http agent %O', error)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ export async function startSecurityScan(

logger.verbose(`Security scan completed.`)
} catch (error) {
getLogger().error('Security scan failed.', error)
getLogger().error('Security scan failed. %O', error)
if (error instanceof CodeScanStoppedError) {
codeScanTelemetryEntry.result = 'Cancelled'
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ export async function validateSQLMetadataFile(fileContents: string, message: any
`CodeTransformation: Parsed .sct file with source DB: ${sourceDB}, target DB: ${targetDB}, source host name: ${sourceServerName}, and schema names: ${Array.from(schemaNames)}`
)
} catch (err: any) {
getLogger().error('CodeTransformation: Error parsing .sct file.', err)
getLogger().error('CodeTransformation: Error parsing .sct file. %O', err)
transformByQState.getChatMessenger()?.sendUnrecoverableErrorResponse('error-parsing-sct-file', message.tabID)
return false
}
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/codewhisperer/util/zipUtil.ts
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,7 @@ export class ZipUtil {
language: this._language,
}
} catch (error) {
getLogger().error('Zip error caused by:', error)
getLogger().error('Zip error caused by: %O', error)
throw error
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,7 @@ export class SamInvokeWebview extends VueWebview {
selectedFile: this.getFileName(fileLocations[0].fsPath),
}
} catch (e) {
getLogger().error('readFileSync: Failed to read file at path %O', fileLocations[0].fsPath, e)
getLogger().error('readFileSync: Failed to read file at path %s %O', fileLocations[0].fsPath, e)
throw ToolkitError.chain(e, 'Failed to read selected file')
}
}
Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/lambda/vue/remoteInvoke/invokeLambda.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ export class RemoteInvokeWebview extends VueWebview {
selectedFile: this.getFileName(fileLocations[0].fsPath),
}
} catch (e) {
getLogger().error('readFileSync: Failed to read file at path %O', fileLocations[0].fsPath, e)
getLogger().error('readFileSync: Failed to read file at path %s %O', fileLocations[0].fsPath, e)
throw ToolkitError.chain(e, 'Failed to read selected file')
}
}
Expand All @@ -151,7 +151,7 @@ export class RemoteInvokeWebview extends VueWebview {
selectedFile: this.getFileName(fileLocation.fsPath),
}
} catch (e) {
getLogger().error('readFileSync: Failed to read file at path %O', fileLocation.fsPath, e)
getLogger().error('readFileSync: Failed to read file at path %s %O', fileLocation.fsPath, e)
throw ToolkitError.chain(e, 'Failed to read selected file')
}
}
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/lambda/wizards/samDeployWizard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -992,7 +992,7 @@ async function populateS3QuickPick(
})
}
} catch (e) {
getLogger().error('Recent bucket JSON not parseable.', e)
getLogger().error('Recent bucket JSON not parseable: %O', e)
}

if (isCloud9() && recent !== cloud9Bucket) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ export class ToolkitLoginWebview extends CommonAuthWebview {
await setContext('aws.explorer.showAuthView', false)
await this.showResourceExplorer()
} catch (e) {
getLogger().error('Failed submitting credentials', e)
getLogger().error('Failed submitting credentials %O', e)
return { id: this.id, text: e as string }
}
}
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/shared/env/resolveEnv.ts
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,7 @@ async function doResolveUnixShellEnv(timeout: Timeout): Promise<typeof process.e
getLogger().debug(`getUnixShellEnvironment#result:${env}`)
resolve(env)
} catch (err) {
getLogger().error('getUnixShellEnvironment#errorCaught', err)
getLogger().error('getUnixShellEnvironment#errorCaught %O', err)
reject(err)
}
})
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/shared/filesystemUtilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,7 @@ export async function setDefaultDownloadPath(downloadPath: string) {
await globals.globalState.update('aws.downloadPath', path.dirname(downloadPath))
}
} catch (err) {
getLogger().error('Error while setting "aws.downloadPath"', err as Error)
getLogger().error('Error while setting "aws.downloadPath": %O', err as Error)
}
}

Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/shared/sam/activation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ async function activateCodeLensRegistry(context: ExtContext) {
getIdeProperties().codelenses
)
)
getLogger().error('Failed to activate codelens registry', e)
getLogger().error('Failed to activate codelens registry %O', e)
// This prevents us from breaking for any reason later if it fails to load. Since
// Noop watcher is always empty, we will get back empty arrays with no issues.
globals.codelensRootRegistry = new NoopWatcher() as unknown as CodelensRootRegistry
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/shared/sam/cli/samCliLocalInvoke.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ export class DefaultSamLocalInvokeCommand implements SamLocalInvokeCommand {
const childProcess = new ChildProcess(params.command, params.args, {
spawnOptions: await addTelemetryEnvVar(options),
})
getLogger().info('AWS.running.command', 'Command: {0}', `${childProcess}`)
getLogger().info('AWS.running.command: Command: %O', childProcess)
// "sam local invoke", "sam local start-api", etc.
const samCommandName = `sam ${params.args[0]} ${params.args[1]}`

Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/shared/ui/picker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -310,7 +310,7 @@ export class IteratingQuickPickController<TResponse> {
// give quickpick item an error message
// we should not blow away the existing items, they should still be viable
const err = e as Error
getLogger().error('Error while loading items for IteratingQuickPickController:', err)
getLogger().error('Error while loading items for IteratingQuickPickController: %O', err)
resolve({
value: [
{
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/shared/utilities/zipStream.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ export class ZipStream {
while (!finished) {
finished = await new Promise((resolve) => {
setTimeout(() => {
getLogger().verbose('success is', this._numberOfFilesSucceeded, '/', this._numberOfFilesToStream)
getLogger().verbose(`success is ${this._numberOfFilesSucceeded}/${this._numberOfFilesToStream}`)
onProgress?.(Math.floor((100 * this._numberOfFilesSucceeded) / this._numberOfFilesToStream))
resolve(this._numberOfFilesToStream <= this._numberOfFilesSucceeded)
}, 1000)
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/ssmDocument/commands/deleteDocument.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ export async function deleteDocument(node: DocumentItemNodeWriteable) {
} catch (err) {
result = 'Failed'
const error = err as Error
logger.error('Error on deleting document: %0', error)
logger.error('Error on deleting document: %O', error)
void showViewLogsMessage(
localize(
'AWS.message.error.ssmDocument.deleteDocument.could_not_delete',
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/ssmDocument/commands/openDocumentItem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ export async function openDocumentItem(node: DocumentItemNode, awsContext: AwsCo
} catch (err) {
result = 'Failed'
const error = err as Error
logger.error('Error on opening document: %0', error)
logger.error('Error on opening document: %O', error)
void showViewLogsMessage(
localize(
'AWS.message.error.ssmDocument.openDocument.could_not_open',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ export async function updateDocumentVersion(node: DocumentItemNodeWriteable, aws
node.documentName
)
)
logger.error('Error on updating document version: %0', error)
logger.error('Error on updating document version: %O', error)
} finally {
telemetry.ssm_updateDocumentVersion.emit({ result: result })
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ describe('samProject', () => {
// simulate error when no samconfig.toml file in directory
const result = await getStackName(projectRoot)
assert.deepStrictEqual(result, {})
assertLogsContain('No stack name or region information available in samconfig.toml', true, 'info')
assertLogsContain('No stack name or region information available in samconfig.toml: %O', true, 'info')
})

it('returns empty object give error parsing samconfig file', async () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ describe('RemoteInvokeWebview', () => {
new Error('Failed to read selected file')
)
assert.strictEqual(loggerErrorStub.calledOnce, true)
assert.strictEqual(loggerErrorStub.firstCall.args[0], 'readFileSync: Failed to read file at path %O')
assert.strictEqual(loggerErrorStub.firstCall.args[0], 'readFileSync: Failed to read file at path %s %O')
assert.strictEqual(loggerErrorStub.firstCall.args[1], fileUri.fsPath)
assert(loggerErrorStub.firstCall.args[2] instanceof Error)
} finally {
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/test/lambda/vue/samInvokeBackend.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -446,7 +446,7 @@ describe('SamInvokeWebview', () => {
new Error('Failed to read selected file')
)
assert.strictEqual(loggerErrorStub.calledOnce, true)
assert.strictEqual(loggerErrorStub.firstCall.args[0], 'readFileSync: Failed to read file at path %O')
assert.strictEqual(loggerErrorStub.firstCall.args[0], 'readFileSync: Failed to read file at path %s %O')
assert.strictEqual(loggerErrorStub.firstCall.args[1], fileUri.fsPath)
assert(loggerErrorStub.firstCall.args[2] instanceof Error)
} finally {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -223,8 +223,8 @@ describe('generateDeployedNode', () => {
lambdaDeployedNodeInput.resourceTreeEntity
)

assert(loggerErrorStub.calledOnceWith('Error getting Lambda configuration'))
assert(loggerErrorStub.neverCalledWith('Error getting Lambda V3 configuration'))
assert(loggerErrorStub.calledOnceWith('Error getting Lambda configuration %O'))
assert(loggerErrorStub.neverCalledWith('Error getting Lambda V3 configuration %O'))
assert(deployedResourceNodes.length === 1)

// Check placeholder propertries
Expand Down Expand Up @@ -375,10 +375,10 @@ describe('generateDeployedNode', () => {
unsupportTypeInput.resourceTreeEntity
)

assert(loggerInfoStub.calledOnceWith('Details are missing or are incomplete for:'))
assert(loggerInfoStub.calledOnceWith('Details are missing or are incomplete for: %O'))

// Check deployedResourceNodes array propertries
assert(loggerErrorStub.neverCalledWith('Error getting Lambda V3 configuration'))
assert(loggerErrorStub.neverCalledWith('Error getting Lambda V3 configuration %O'))

// Check deployedResourceNodes array propertries
assert(deployedResourceNodes.length === 1)
Expand Down
2 changes: 2 additions & 0 deletions plugins/eslint-plugin-aws-toolkits/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import NoOnlyInTests from './lib/rules/no-only-in-tests'
import NoStringExecForChildProcess from './lib/rules/no-string-exec-for-child-process'
import NoConsoleLog from './lib/rules/no-console-log'
import noJsonStringifyInLog from './lib/rules/no-json-stringify-in-log'
import noPrintfMismatch from './lib/rules/no-printf-mismatch'

const rules = {
'no-await-on-vscode-msg': NoAwaitOnVscodeMsg,
Expand All @@ -19,6 +20,7 @@ const rules = {
'no-string-exec-for-child-process': NoStringExecForChildProcess,
'no-console-log': NoConsoleLog,
'no-json-stringify-in-log': noJsonStringifyInLog,
'no-printf-mismatch': noPrintfMismatch,
}

export { rules }
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ function isTemplateWithStringifyCall(node: TSESTree.CallExpressionArgument): boo
* Check if node is representing syntax of the form getLogger().f(msg) for some f and msg or
* if it is doing so indirectly via a logger variable.
*/
function isLoggerCall(node: TSESTree.CallExpression): boolean {
export function isLoggerCall(node: TSESTree.CallExpression): boolean {
return (
node.callee.type === AST_NODE_TYPES.MemberExpression &&
(isGetLoggerCall(node.callee.object) || isDisguisedGetLoggerCall(node.callee.object)) &&
Expand Down
62 changes: 62 additions & 0 deletions plugins/eslint-plugin-aws-toolkits/lib/rules/no-printf-mismatch.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/*!
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
import { AST_NODE_TYPES, ESLintUtils, TSESTree } from '@typescript-eslint/utils'
import { isLoggerCall } from './no-json-stringify-in-log'
import { Rule } from 'eslint'

/**
* reuse solution done in logger itself: https://github.com/winstonjs/winston/blob/195e55c7e7fc58914ae4967ea7b832c9e0ced930/lib/winston/logger.js#L27
*/
function countSubTokens(literalNode: TSESTree.StringLiteral) {
const formatRegExp = /%[scdjifoO%]/g
return literalNode.value.match(formatRegExp)?.length || 0
}
/**
* Form the error message using templates or actual values.
* Allows us to avoid copy pasting message into test file.
*/
export function formErrorMsg(substitutionTokens: string | number, numArgs: string | number): string {
return `printf-style (console.log) call has ${substitutionTokens} format specifiers, but ${numArgs} arguments.`
}

export default ESLintUtils.RuleCreator.withoutDocs({
meta: {
docs: {
description: 'ensure string substitution args and templates match',
recommended: 'recommended',
},
messages: {
errMsg: formErrorMsg('{{ substitutionTokens }}', '{{ args }}'),
},
type: 'problem',
fixable: 'code',
schema: [],
},
defaultOptions: [],
create(context) {
return {
CallExpression(node: TSESTree.CallExpression) {
if (
isLoggerCall(node) &&
node.arguments[0].type === AST_NODE_TYPES.Literal &&
typeof node.arguments[0].value === 'string'
) {
const numSubTokens = countSubTokens(node.arguments[0])
const numExtraArgs = node.arguments.length - 1
if (numSubTokens !== numExtraArgs) {
return context.report({
node: node,
data: {
substitutionTokens: numSubTokens,
args: numExtraArgs,
},
messageId: 'errMsg',
})
}
}
},
}
},
}) as unknown as Rule.RuleModule
Loading

0 comments on commit 50cbeee

Please sign in to comment.