diff --git a/src/index.ts b/src/index.ts index 121fe98ce..9049114e6 100644 --- a/src/index.ts +++ b/src/index.ts @@ -30,7 +30,7 @@ export { export { HTTPResponseAck } from './receivers/HTTPResponseAck'; export { - SocketModeFunctions, + defaultProcessEventErrorHandler, SocketModeReceiverProcessEventErrorHandlerArgs, } from './receivers/SocketModeFunctions'; diff --git a/src/receivers/BufferedIncomingMessage.ts b/src/receivers/BufferedIncomingMessage.ts index 63a59db59..7439332c9 100644 --- a/src/receivers/BufferedIncomingMessage.ts +++ b/src/receivers/BufferedIncomingMessage.ts @@ -1,4 +1,4 @@ -import type { IncomingMessage } from 'http'; +import type { IncomingMessage } from 'node:http'; export interface BufferedIncomingMessage extends IncomingMessage { rawBody: Buffer; diff --git a/src/receivers/SocketModeFunctions.ts b/src/receivers/SocketModeFunctions.ts index 008db1723..ea8fc16fe 100644 --- a/src/receivers/SocketModeFunctions.ts +++ b/src/receivers/SocketModeFunctions.ts @@ -1,32 +1,22 @@ -/* eslint-disable import/prefer-default-export */ import type { Logger } from '@slack/logger'; -import { type CodedError, ErrorCode } from '../errors'; +import { type CodedError, ErrorCode, isCodedError } from '../errors'; import type { ReceiverEvent } from '../types'; -export class SocketModeFunctions { - // ------------------------------------------ - // Error handlers for event processing - // ------------------------------------------ - - // The default processEventErrorHandler implementation: - // Developers can customize this behavior by passing processEventErrorHandler to the constructor - public static async defaultProcessEventErrorHandler( - args: SocketModeReceiverProcessEventErrorHandlerArgs, - ): Promise { - const { error, logger, event } = args; - // TODO: more details like envelop_id, payload type etc. here - // To make them available, we need to enhance underlying SocketModeClient - // to return more properties to 'slack_event' listeners - logger.error(`An unhandled error occurred while Bolt processed (type: ${event.body?.type}, error: ${error})`); - logger.debug(`Error details: ${error}, retry num: ${event.retryNum}, retry reason: ${event.retryReason}`); - const errorCode = (error as CodedError).code; - if (errorCode === ErrorCode.AuthorizationError) { - // The `authorize` function threw an exception, which means there is no valid installation data. - // In this case, we can tell the Slack server-side to stop retries. - return true; - } - return false; +export async function defaultProcessEventErrorHandler( + args: SocketModeReceiverProcessEventErrorHandlerArgs, +): Promise { + const { error, logger, event } = args; + // TODO: more details like envelop_id, payload type etc. here + // To make them available, we need to enhance underlying SocketModeClient + // to return more properties to 'slack_event' listeners + logger.error(`An unhandled error occurred while Bolt processed (type: ${event.body?.type}, error: ${error})`); + logger.debug(`Error details: ${error}, retry num: ${event.retryNum}, retry reason: ${event.retryReason}`); + if (isCodedError(error) && error.code === ErrorCode.AuthorizationError) { + // The `authorize` function threw an exception, which means there is no valid installation data. + // In this case, we can tell the Slack server-side to stop retries. + return true; } + return false; } // The arguments for the processEventErrorHandler, diff --git a/src/receivers/SocketModeReceiver.ts b/src/receivers/SocketModeReceiver.ts index e67e1fc8e..57302fab1 100644 --- a/src/receivers/SocketModeReceiver.ts +++ b/src/receivers/SocketModeReceiver.ts @@ -1,6 +1,5 @@ -import { type Server, type ServerResponse, createServer } from 'http'; -/* eslint-disable @typescript-eslint/no-explicit-any */ -import { URL } from 'url'; +import { type Server, type ServerResponse, createServer } from 'node:http'; +import { URL } from 'node:url'; import { ConsoleLogger, LogLevel, type Logger } from '@slack/logger'; import { type CallbackOptions, @@ -20,7 +19,7 @@ import type { StringIndexed } from '../types/helpers'; import type { ParamsIncomingMessage } from './ParamsIncomingMessage'; import { type SocketModeReceiverProcessEventErrorHandlerArgs, - SocketModeFunctions as socketModeFunc, + defaultProcessEventErrorHandler, } from './SocketModeFunctions'; import { type ReceiverRoutes, buildReceiverRoutes } from './custom-routes'; import { verifyRedirectOpts } from './verify-redirect-opts'; @@ -39,6 +38,7 @@ export interface SocketModeReceiverOptions { installerOptions?: InstallerOptions; appToken: string; // App Level Token customRoutes?: CustomRoute[]; + // biome-ignore lint/suspicious/noExplicitAny: user-provided custom properties can be anything customPropertiesExtractor?: (args: any) => StringIndexed; processEventErrorHandler?: (args: SocketModeReceiverProcessEventErrorHandlerArgs) => Promise; } @@ -104,7 +104,7 @@ export default class SocketModeReceiver implements Receiver { installerOptions = {}, customRoutes = [], customPropertiesExtractor = (_args) => ({}), - processEventErrorHandler = socketModeFunc.defaultProcessEventErrorHandler, + processEventErrorHandler = defaultProcessEventErrorHandler, }: SocketModeReceiverOptions) { this.client = new SocketModeClient({ appToken, @@ -158,7 +158,7 @@ export default class SocketModeReceiver implements Receiver { const installPath = installerOptions.installPath === undefined ? '/slack/install' : installerOptions.installPath; this.httpServerPort = installerOptions.port === undefined ? 3000 : installerOptions.port; this.httpServer = createServer(async (req, res) => { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + // biome-ignore lint/style/noNonNullAssertion: method should always be defined for an HTTP request right? const method = req.method!.toUpperCase(); // Handle OAuth-related requests @@ -173,7 +173,7 @@ export default class SocketModeReceiver implements Receiver { // Installation has been initiated const redirectUriPath = installerOptions.redirectUriPath === undefined ? '/slack/oauth_redirect' : installerOptions.redirectUriPath; - if (req.url && req.url.startsWith(redirectUriPath)) { + if (req.url?.startsWith(redirectUriPath)) { const { stateVerification, callbackOptions } = installerOptions; if (stateVerification === false) { // if stateVerification is disabled make install options available to handler @@ -185,7 +185,7 @@ export default class SocketModeReceiver implements Receiver { return; } // Visiting the installation endpoint - if (req.url && req.url.startsWith(installPath)) { + if (req.url?.startsWith(installPath)) { const { installPathOptions } = installerOptions; await this.installer.handleInstallPath(req, res, installPathOptions, installUrlOptions); return; diff --git a/test/unit/.mocharc.json b/test/unit/.mocharc.json index 3ab3d57f8..b01a8ac44 100644 --- a/test/unit/.mocharc.json +++ b/test/unit/.mocharc.json @@ -5,6 +5,7 @@ ], "spec": [ "test/unit/App/*.spec.ts", + "test/unit/receivers/SocketModeFunctions.spec.ts", "test/unit/*.spec.ts" ], "timeout": 3000 diff --git a/test/unit/helpers/events.ts b/test/unit/helpers/events.ts index 87819aef8..b3333b118 100644 --- a/test/unit/helpers/events.ts +++ b/test/unit/helpers/events.ts @@ -23,7 +23,6 @@ import type { BlockSuggestion, SlashCommand, AllMiddlewareArgs, - AnyMiddlewareArgs, } from '../../../src/types'; import { createFakeLogger } from '.'; import sinon, { type SinonSpy } from 'sinon'; diff --git a/test/unit/receivers/SocketModeFunctions.spec.ts b/test/unit/receivers/SocketModeFunctions.spec.ts index c7de047a3..9694abfad 100644 --- a/test/unit/receivers/SocketModeFunctions.spec.ts +++ b/test/unit/receivers/SocketModeFunctions.spec.ts @@ -1,9 +1,8 @@ -import 'mocha'; import { assert } from 'chai'; -import { AuthorizationError, ReceiverMultipleAckError } from '../errors'; -import { createFakeLogger } from '../test-helpers'; -import type { ReceiverEvent } from '../types'; -import { SocketModeFunctions as func } from './SocketModeFunctions'; +import { AuthorizationError, ReceiverMultipleAckError } from '../../../src/errors'; +import { createFakeLogger } from '../helpers'; +import type { ReceiverEvent } from '../../../src/types'; +import { defaultProcessEventErrorHandler } from '../../../src/receivers/SocketModeFunctions'; describe('SocketModeFunctions', async () => { describe('Error handlers for event processing', async () => { @@ -12,10 +11,10 @@ describe('SocketModeFunctions', async () => { describe('defaultProcessEventErrorHandler', async () => { it('should return false if passed any Error other than AuthorizationError', async () => { const event: ReceiverEvent = { - ack: async () => {}, + ack: async () => { }, body: {}, }; - const shouldBeAcked = await func.defaultProcessEventErrorHandler({ + const shouldBeAcked = await defaultProcessEventErrorHandler({ error: new ReceiverMultipleAckError(), logger, event, @@ -24,10 +23,10 @@ describe('SocketModeFunctions', async () => { }); it('should return true if passed an AuthorizationError', async () => { const event: ReceiverEvent = { - ack: async () => {}, + ack: async () => { }, body: {}, }; - const shouldBeAcked = await func.defaultProcessEventErrorHandler({ + const shouldBeAcked = await defaultProcessEventErrorHandler({ error: new AuthorizationError('msg', new Error()), logger, event,