Skip to content

Commit

Permalink
breaking change: removed SocketModeFunctions export (class with singl…
Browse files Browse the repository at this point in the history
…e static method), instead export the method directly. linting, TODOs, removing a cast in errors.
  • Loading branch information
filmaj committed Sep 20, 2024
1 parent b0f4cb3 commit 6825eee
Show file tree
Hide file tree
Showing 7 changed files with 34 additions and 45 deletions.
2 changes: 1 addition & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export {
export { HTTPResponseAck } from './receivers/HTTPResponseAck';

export {
SocketModeFunctions,
defaultProcessEventErrorHandler,
SocketModeReceiverProcessEventErrorHandlerArgs,
} from './receivers/SocketModeFunctions';

Expand Down
2 changes: 1 addition & 1 deletion src/receivers/BufferedIncomingMessage.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { IncomingMessage } from 'http';
import type { IncomingMessage } from 'node:http';

export interface BufferedIncomingMessage extends IncomingMessage {
rawBody: Buffer;
Expand Down
40 changes: 15 additions & 25 deletions src/receivers/SocketModeFunctions.ts
Original file line number Diff line number Diff line change
@@ -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<boolean> {
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<boolean> {
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,
Expand Down
16 changes: 8 additions & 8 deletions src/receivers/SocketModeReceiver.ts
Original file line number Diff line number Diff line change
@@ -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,
Expand All @@ -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';
Expand All @@ -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<boolean>;
}
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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;
Expand Down
1 change: 1 addition & 0 deletions test/unit/.mocharc.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
],
"spec": [
"test/unit/App/*.spec.ts",
"test/unit/receivers/SocketModeFunctions.spec.ts",
"test/unit/*.spec.ts"
],
"timeout": 3000
Expand Down
1 change: 0 additions & 1 deletion test/unit/helpers/events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ import type {
BlockSuggestion,
SlashCommand,
AllMiddlewareArgs,
AnyMiddlewareArgs,
} from '../../../src/types';
import { createFakeLogger } from '.';
import sinon, { type SinonSpy } from 'sinon';
Expand Down
17 changes: 8 additions & 9 deletions test/unit/receivers/SocketModeFunctions.spec.ts
Original file line number Diff line number Diff line change
@@ -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 () => {
Expand All @@ -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,
Expand All @@ -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,
Expand Down

0 comments on commit 6825eee

Please sign in to comment.