-
Notifications
You must be signed in to change notification settings - Fork 399
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix #759 #1109 #1110 by adding custom properties in ReceiverEvent and Context objets #1177
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
node_modules/ | ||
package-lock.json |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
MIT License | ||
|
||
Copyright (c) 2021 Slack Technologies, LLC | ||
|
||
Permission is hereby granted, free of charge, to any person obtaining a copy | ||
of this software and associated documentation files (the "Software"), to deal | ||
in the Software without restriction, including without limitation the rights | ||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
copies of the Software, and to permit persons to whom the Software is | ||
furnished to do so, subject to the following conditions: | ||
|
||
The above copyright notice and this permission notice shall be included in all | ||
copies or substantial portions of the Software. | ||
|
||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
SOFTWARE. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
const { App, HTTPReceiver, ExpressReceiver } = require('@slack/bolt'); | ||
|
||
const useExpress = false; | ||
|
||
let receiver; | ||
receiver = new HTTPReceiver({ | ||
signingSecret: process.env.SLACK_SIGNING_SECRET, | ||
customPropertiesExtractor: (req) => { | ||
return { | ||
"headers": req.headers, | ||
"foo": "bar", | ||
}; | ||
} | ||
}); | ||
if (useExpress) { | ||
receiver = new ExpressReceiver({ | ||
signingSecret: process.env.SLACK_SIGNING_SECRET, | ||
customPropertiesExtractor: (req) => { | ||
return { | ||
"headers": req.headers, | ||
"foo": "bar", | ||
}; | ||
} | ||
}); | ||
} | ||
|
||
const app = new App({ | ||
token: process.env.SLACK_BOT_TOKEN, | ||
receiver, | ||
}); | ||
|
||
app.use(async ({ logger, context, next }) => { | ||
logger.info(context); | ||
await next(); | ||
}); | ||
|
||
(async () => { | ||
// Start your app | ||
await app.start(process.env.PORT || 3000); | ||
|
||
console.log('⚡️ Bolt app is running!'); | ||
})(); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
#!/bin/bash | ||
|
||
current_dir=`dirname $0` | ||
cd ${current_dir} | ||
npm unlink @slack/bolt \ | ||
&& npm i \ | ||
&& cd ../.. \ | ||
&& npm link \ | ||
&& cd - \ | ||
&& npm i \ | ||
&& npm link @slack/bolt |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
{ | ||
"name": "bolt-js-custom-properties-app", | ||
"version": "1.0.0", | ||
"description": "Having custom request properties in ⚡️ Bolt for JavaScript", | ||
"main": "app.js", | ||
"scripts": { | ||
"ngrok": "ngrok http 3000", | ||
"start": "node app.js", | ||
"test": "echo \"Error: no test specified\" && exit 1" | ||
}, | ||
"license": "MIT", | ||
"dependencies": {} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -43,14 +43,14 @@ | |
"dependencies": { | ||
"@slack/logger": "^3.0.0", | ||
"@slack/oauth": "^2.3.0", | ||
"@slack/socket-mode": "^1.1.0", | ||
"@slack/socket-mode": "^1.2.0", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is required for extracting retry_num and retry_attempt value from payload |
||
"@slack/types": "^2.2.0", | ||
"@slack/web-api": "^6.4.0", | ||
"@types/express": "^4.16.1", | ||
"@types/node": ">=12", | ||
"@types/promise.allsettled": "^1.0.3", | ||
"@types/tsscmp": "^1.0.0", | ||
"axios": "^0.21.2", | ||
"axios": "^0.21.4", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. npm audit fix |
||
"express": "^4.16.4", | ||
"please-upgrade-node": "^3.2.0", | ||
"promise.allsettled": "^1.0.2", | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -55,8 +55,9 @@ import { | |
WorkflowStepEdit, | ||
} from './types'; | ||
import { IncomingEventType, getTypeAndConversation, assertNever } from './helpers'; | ||
import { CodedError, asCodedError, AppInitializationError, MultipleListenerError, ErrorCode } from './errors'; | ||
import { AllMiddlewareArgs } from './types/middleware'; | ||
import { CodedError, asCodedError, AppInitializationError, MultipleListenerError, ErrorCode, InvalidCustomPropertyError } from './errors'; | ||
import { AllMiddlewareArgs, contextBuiltinKeys } from './types/middleware'; | ||
import { StringIndexed } from './types/helpers'; | ||
// eslint-disable-next-line import/order | ||
import allSettled = require('promise.allsettled'); // eslint-disable-line @typescript-eslint/no-require-imports | ||
// eslint-disable-next-line @typescript-eslint/no-require-imports, import/no-commonjs | ||
|
@@ -768,7 +769,20 @@ export default class App { | |
authorizeResult.enterpriseId = source.enterpriseId; | ||
} | ||
|
||
const context: Context = { ...authorizeResult }; | ||
if (typeof event.customProperties !== 'undefined') { | ||
const customProps: StringIndexed = event.customProperties; | ||
const builtinKeyDetected = contextBuiltinKeys.find((key) => key in customProps); | ||
if (typeof builtinKeyDetected !== 'undefined') { | ||
throw new InvalidCustomPropertyError('customProperties cannot have the same names with the built-in ones'); | ||
seratch marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
} | ||
|
||
const context: Context = { | ||
...authorizeResult, | ||
...event.customProperties, | ||
retryNum: event.retryNum, | ||
retryReason: event.retryReason, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What is the purpose of explicitly adding the retryNum and retryReason keys to the context object here? It looks like these two keys are part of the "built in" keys, so the check above should There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These two properties are built-in ones but they are supposed to be sent as part of the receiver event data, not from the authorize function. Thus, we set these this way.
Yes, your understanding here is correct. |
||
}; | ||
|
||
// Factory for say() utility | ||
const createSay = (channelId: string): SayFn => { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -168,6 +168,10 @@ export default class AwsLambdaReceiver implements Receiver { | |
storedResponse = response; | ||
} | ||
}, | ||
retryNum: this.getHeaderValue(awsEvent.headers, 'X-Slack-Retry-Num') as number | undefined, | ||
retryReason: this.getHeaderValue(awsEvent.headers, 'X-Slack-Retry-Reason'), | ||
// TODO | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. TODO There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed |
||
customProperties: {}, | ||
}; | ||
|
||
// Send the event to the app for processing | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
import { IncomingMessage } from 'http'; | ||
|
||
export function extractRetryNum(req: IncomingMessage): number | undefined { | ||
let retryNum; | ||
const retryNumHeaderValue = req.headers['x-slack-retry-num']; | ||
if (retryNumHeaderValue === undefined) { | ||
retryNum = undefined; | ||
} else if (typeof retryNumHeaderValue === 'string') { | ||
retryNum = parseInt(retryNumHeaderValue, 10); | ||
} else if (Array.isArray(retryNumHeaderValue) && retryNumHeaderValue.length > 0) { | ||
retryNum = parseInt(retryNumHeaderValue[0], 10); | ||
} | ||
return retryNum; | ||
} | ||
|
||
export function extractRetryReason(req: IncomingMessage): string | undefined { | ||
let retryReason; | ||
const retryReasonHeaderValue = req.headers['x-slack-retry-reason']; | ||
if (retryReasonHeaderValue === undefined) { | ||
retryReason = undefined; | ||
} else if (typeof retryReasonHeaderValue === 'string') { | ||
retryReason = retryReasonHeaderValue; | ||
} else if (Array.isArray(retryReasonHeaderValue) && retryReasonHeaderValue.length > 0) { | ||
// eslint-disable-next-line prefer-destructuring | ||
retryReason = retryReasonHeaderValue[0]; | ||
} | ||
return retryReason; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know this name is a bit verbose. Does anyone have a great idea for the naming?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's fine!