diff --git a/src/App.spec.ts b/src/App.spec.ts index c17ee817a..6329a90a6 100644 --- a/src/App.spec.ts +++ b/src/App.spec.ts @@ -92,7 +92,7 @@ describe('App', () => { try { new App({ signingSecret: '' }); // eslint-disable-line @typescript-eslint/no-unused-expressions assert.fail(); - } catch (error) { + } catch (error: any) { // Assert assert.propertyVal(error, 'code', ErrorCode.AppInitializationError); } @@ -107,7 +107,7 @@ describe('App', () => { // eslint-disable-line @typescript-eslint/no-unused-expressions new App({ token: '', authorize: authorizeCallback, signingSecret: '' }); assert.fail(); - } catch (error) { + } catch (error: any) { // Assert assert.propertyVal(error, 'code', ErrorCode.AppInitializationError); assert(authorizeCallback.notCalled); @@ -123,7 +123,7 @@ describe('App', () => { // eslint-disable-line @typescript-eslint/no-unused-expressions new App({ token: '', clientId: '', clientSecret: '', stateSecret: '', signingSecret: '' }); assert.fail(); - } catch (error) { + } catch (error: any) { // Assert assert.propertyVal(error, 'code', ErrorCode.AppInitializationError); assert(authorizeCallback.notCalled); @@ -139,7 +139,7 @@ describe('App', () => { // eslint-disable-line @typescript-eslint/no-unused-expressions new App({ authorize: authorizeCallback, clientId: '', clientSecret: '', stateSecret: '', signingSecret: '' }); assert.fail(); - } catch (error) { + } catch (error: any) { // Assert assert.propertyVal(error, 'code', ErrorCode.AppInitializationError); assert(authorizeCallback.notCalled); @@ -165,7 +165,7 @@ describe('App', () => { try { new App({ authorize: noopAuthorize }); // eslint-disable-line @typescript-eslint/no-unused-expressions assert.fail(); - } catch (error) { + } catch (error: any) { // Assert assert.propertyVal(error, 'code', ErrorCode.AppInitializationError); } @@ -180,7 +180,7 @@ describe('App', () => { // eslint-disable-line @typescript-eslint/no-unused-expressions new App({ token: '', signingSecret: '', socketMode: true, receiver: fakeReceiver }); assert.fail(); - } catch (error) { + } catch (error: any) { // Assert assert.propertyVal(error, 'code', ErrorCode.AppInitializationError); } @@ -476,7 +476,7 @@ describe('App', () => { app.use(async ({ next }) => { try { await next!(); - } catch (err) { + } catch (err: any) { caughtError = err; } }); @@ -560,7 +560,7 @@ describe('App', () => { try { await fakeReceiver.sendEvent(dummyReceiverEvent); - } catch (err) { + } catch (err: any) { actualError = err; } diff --git a/src/App.ts b/src/App.ts index 9f04babf5..7403fa6d3 100644 --- a/src/App.ts +++ b/src/App.ts @@ -676,9 +676,10 @@ export default class App { authorizeResult = await this.authorize(source as AuthorizeSourceData, bodyArg); } } catch (error) { + const e = error as any; this.logger.warn('Authorization of incoming event did not succeed. No listeners will be called.'); - error.code = 'slack_bolt_authorization_error'; - return this.handleError(error); + e.code = 'slack_bolt_authorization_error'; + return this.handleError(e); } // Try to set teamId from AuthorizeResult before using one from source @@ -854,7 +855,8 @@ export default class App { }, ); } catch (error) { - return this.handleError(error); + const e = error as any; + return this.handleError(e); } } diff --git a/src/conversation-store.spec.ts b/src/conversation-store.spec.ts index e3e989ba8..5ed3f3551 100644 --- a/src/conversation-store.spec.ts +++ b/src/conversation-store.spec.ts @@ -163,7 +163,7 @@ describe('MemoryStore', () => { try { await store.get('CONVERSATION_ID'); assert.fail(); - } catch (error) { + } catch (error: any) { // Assert assert.instanceOf(error, Error); assert.notInstanceOf(error, AssertionError); @@ -184,7 +184,7 @@ describe('MemoryStore', () => { try { await store.get(dummyConversationId); assert.fail(); - } catch (error) { + } catch (error: any) { // Assert assert.instanceOf(error, Error); assert.notInstanceOf(error, AssertionError); diff --git a/src/conversation-store.ts b/src/conversation-store.ts index f5a4d0a14..4cd142154 100644 --- a/src/conversation-store.ts +++ b/src/conversation-store.ts @@ -64,9 +64,10 @@ export function conversationContext( context.conversation = await store.get(conversationId); logger.debug(`Conversation context loaded for ID: ${conversationId}`); } catch (error) { - if (error.message !== undefined && error.message !== 'Conversation not found') { + const e = error as any; + if (e.message !== undefined && e.message !== 'Conversation not found') { // The conversation data can be expired - error: Conversation expired - logger.debug(`Conversation context failed loading for ID: ${conversationId}, error: ${error.message}`); + logger.debug(`Conversation context failed loading for ID: ${conversationId}, error: ${e.message}`); } } } else { diff --git a/src/middleware/builtin.spec.ts b/src/middleware/builtin.spec.ts index 5f05a834c..329e54c8b 100644 --- a/src/middleware/builtin.spec.ts +++ b/src/middleware/builtin.spec.ts @@ -181,7 +181,7 @@ describe('directMention()', () => { try { await middleware(fakeArgs); - } catch (err) { + } catch (err: any) { error = err; } @@ -308,7 +308,7 @@ describe('ignoreSelf()', () => { let error; try { await middleware(fakeArgs); - } catch (err) { + } catch (err: any) { error = err; } diff --git a/src/receivers/ExpressReceiver.spec.ts b/src/receivers/ExpressReceiver.spec.ts index 7fc36a19c..1ecda7336 100644 --- a/src/receivers/ExpressReceiver.spec.ts +++ b/src/receivers/ExpressReceiver.spec.ts @@ -166,7 +166,7 @@ describe('ExpressReceiver', function () { let caughtError: Error | undefined; try { await receiver.start(port); - } catch (error) { + } catch (error: any) { caughtError = error; } @@ -188,7 +188,7 @@ describe('ExpressReceiver', function () { await receiver.start(port); try { await receiver.start(port); - } catch (error) { + } catch (error: any) { caughtError = error; } @@ -230,7 +230,7 @@ describe('ExpressReceiver', function () { let caughtError: Error | undefined; try { await receiver.stop(); - } catch (error) { + } catch (error: any) { caughtError = error; } diff --git a/src/receivers/HTTPReceiver.spec.ts b/src/receivers/HTTPReceiver.spec.ts index 02f2e3573..685e8eb37 100644 --- a/src/receivers/HTTPReceiver.spec.ts +++ b/src/receivers/HTTPReceiver.spec.ts @@ -71,6 +71,8 @@ describe('HTTPReceiver', function () { }); assert.isNotNull(receiver); }); + }); + describe('request handling', function () { it('should invoke installer generateInstallUrl if a request comes into the install path', async function () { // Arrange const installProviderStub = sinon.createStubInstance(InstallProvider); @@ -113,7 +115,7 @@ describe('HTTPReceiver', function () { assert(installProviderStub.generateInstallUrl.calledWith(sinon.match({ metadata, scopes, userScopes }))); assert.isTrue(writeHead.calledWith(200)); }); - it('should rediect installers if directInstallEnabled is true', async function () { + it('should redirect installers if directInstallEnabled is true', async function () { // Arrange const installProviderStub = sinon.createStubInstance(InstallProvider); const overrides = mergeOverrides( @@ -156,7 +158,52 @@ describe('HTTPReceiver', function () { assert(installProviderStub.generateInstallUrl.calledWith(sinon.match({ metadata, scopes, userScopes }))); assert.isTrue(writeHead.calledWith(302, sinon.match.object)); }); - it('should return a 404 if a request comes into neither the install path nor the redirect URI path', async function () { + it('should invoke installer handler if a request comes into the redirect URI path', async function () { + // Arrange + const installProviderStub = sinon.createStubInstance(InstallProvider, { + handleCallback: sinon.stub().resolves() as unknown as Promise, + }); + const overrides = mergeOverrides( + withHttpCreateServer(this.fakeCreateServer), + withHttpsCreateServer(sinon.fake.throws('Should not be used.')), + ); + const HTTPReceiver = await importHTTPReceiver(overrides); + + const metadata = 'this is bat country'; + const scopes = ['channels:read']; + const userScopes = ['chat:write']; + const callbackOptions = {}; + const receiver = new HTTPReceiver({ + logger: noopLogger, + clientId: 'my-clientId', + clientSecret: 'my-client-secret', + signingSecret: 'secret', + stateSecret: 'state-secret', + scopes, + installerOptions: { + authVersion: 'v2', + redirectUriPath: '/heyo', + callbackOptions, + metadata, + userScopes, + }, + }); + assert.isNotNull(receiver); + receiver.installer = installProviderStub as unknown as InstallProvider; + const fakeReq: IncomingMessage = sinon.createStubInstance(IncomingMessage) as IncomingMessage; + fakeReq.url = '/heyo'; + fakeReq.headers = { host: 'localhost' }; + fakeReq.method = 'GET'; + const fakeRes: ServerResponse & {} = sinon.createStubInstance(ServerResponse) as unknown as ServerResponse; + const writeHead = sinon.fake(); + const end = sinon.fake(); + fakeRes.writeHead = writeHead; + fakeRes.end = end; + /* eslint-disable-next-line @typescript-eslint/await-thenable */ + await receiver.requestListener(fakeReq, fakeRes); + assert(installProviderStub.handleCallback.calledWith(fakeReq, fakeRes, callbackOptions)); + }); + it('should throw if a request comes into neither the install path nor the redirect URI path', async function () { // Arrange const installProviderStub = sinon.createStubInstance(InstallProvider); const overrides = mergeOverrides( diff --git a/src/receivers/HTTPReceiver.ts b/src/receivers/HTTPReceiver.ts index 851169dc4..f0ea153bf 100644 --- a/src/receivers/HTTPReceiver.ts +++ b/src/receivers/HTTPReceiver.ts @@ -166,13 +166,14 @@ export default class HTTPReceiver implements Receiver { try { this.requestListener(req, res); } catch (error) { - if (error.code === ErrorCode.HTTPReceiverDeferredRequestError) { + const e = error as any; + if (e.code === ErrorCode.HTTPReceiverDeferredRequestError) { this.logger.info('An unhandled request was ignored'); res.writeHead(404); res.end(); } else { this.logger.error('An unexpected error was encountered'); - this.logger.debug(`Error details: ${error}`); + this.logger.debug(`Error details: ${e}`); res.writeHead(500); res.end(); } @@ -276,7 +277,8 @@ export default class HTTPReceiver implements Receiver { try { bufferedReq = await verifySlackAuthenticity({ signingSecret: this.signingSecret }, req); } catch (err) { - this.logger.warn(`Request verification failed: ${err.message}`); + const e = err as any; + this.logger.warn(`Request verification failed: ${e.message}`); res.writeHead(401); res.end(); return; @@ -289,7 +291,8 @@ export default class HTTPReceiver implements Receiver { try { body = parseBody(bufferedReq); } catch (err) { - this.logger.warn(`Malformed request body: ${err.message}`); + const e = err as any; + this.logger.warn(`Malformed request body: ${e.message}`); res.writeHead(400); res.end(); return; @@ -370,8 +373,9 @@ export default class HTTPReceiver implements Receiver { this.logger.debug('stored response sent'); } } catch (err) { + const e = err as any; this.logger.error('An unhandled error occurred while Bolt processed an event'); - this.logger.debug(`Error details: ${err}, storedResponse: ${storedResponse}`); + this.logger.debug(`Error details: ${e}, storedResponse: ${storedResponse}`); res.writeHead(500); res.end(); } @@ -412,8 +416,9 @@ export default class HTTPReceiver implements Receiver { res.end(body); } } catch (err) { + const e = err as any; this.logger.error('An unhandled error occurred while Bolt processed a request to the installation path'); - this.logger.debug(`Error details: ${err}`); + this.logger.debug(`Error details: ${e}`); } })(); } diff --git a/src/receivers/SocketModeReceiver.ts b/src/receivers/SocketModeReceiver.ts index 3a206a74f..89e2facc3 100644 --- a/src/receivers/SocketModeReceiver.ts +++ b/src/receivers/SocketModeReceiver.ts @@ -123,7 +123,8 @@ export default class SocketModeReceiver implements Receiver { res.end(renderHtmlForInstallPath(url)); } } catch (err) { - throw new Error(err); + const e = err as any; + throw new Error(e); } } else { this.logger.error(`Tried to reach ${req.url} which isn't a valid route.`);