-
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 #248 by adding a new option deferInitialization #1303
Conversation
Codecov Report
@@ Coverage Diff @@
## main #1303 +/- ##
==========================================
- Coverage 73.24% 72.88% -0.36%
==========================================
Files 17 17
Lines 1439 1479 +40
Branches 431 442 +11
==========================================
+ Hits 1054 1078 +24
- Misses 300 311 +11
- Partials 85 90 +5
Continue to review full report at Codecov.
|
@@ -105,6 +105,7 @@ export interface AppOptions { | |||
socketMode?: boolean; | |||
developerMode?: boolean; | |||
tokenVerificationEnabled?: boolean; | |||
deferInitialization?: boolean; |
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.
Naming suggestions?
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.
This naming makes the most sense to me.
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 this is fine
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.
This is such a nice addition @seratch!
I tested your branch against a sample app, and noticed that when deferInitialization
set as true but still omitting await app.init();
there is no error message for devs.
Do you think we should be enforcing that if deferInitialization
is true, the Bolt App should not be starting unless the user has manually called init()?
@@ -105,6 +105,7 @@ export interface AppOptions { | |||
socketMode?: boolean; | |||
developerMode?: boolean; | |||
tokenVerificationEnabled?: boolean; | |||
deferInitialization?: boolean; |
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.
This naming makes the most sense to me.
installerOptions: this.installerOptions, | ||
}); | ||
} | ||
this.receiver = this.initReceiver( |
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.
This is so nice! 💯
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.
Agreed, I like splitting up the initialization into discrete functions!
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.
This is great, thanks for tackling this @seratch !
Not sure if we should bundle the documentation for this feature in this PR, or a separate one, but I think before merging this PR we should at least write a draft of the docs for this support. In my experience, writing up the docs alongside (or even before) the code is written up can help clarify how to structure the code too. I am happy to help write the docs if you would find that helpful - let me know, I can try to create a draft!
@@ -105,6 +105,7 @@ export interface AppOptions { | |||
socketMode?: boolean; | |||
developerMode?: boolean; | |||
tokenVerificationEnabled?: boolean; | |||
deferInitialization?: boolean; |
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 this is fine
installerOptions: this.installerOptions, | ||
}); | ||
} | ||
this.receiver = this.initReceiver( |
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.
Agreed, I like splitting up the initialization into discrete functions!
// Assert | ||
assert.instanceOf(app, MockApp); | ||
try { | ||
// call #start() before #init() |
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.
Added this test pattern
"scripts": { | ||
"ngrok": "ngrok http 3000", | ||
"start": "node app.js", | ||
"start": "node http.js", |
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.
This has been incorrect since I've renamed the source file.
|
||
try { | ||
await app.init(); | ||
await app.start(process.env.PORT || 3000); |
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.
If you don't call init()
in advance, this start method fails this way:
$ npm start
> bolt-js-custom-properties-app@1.0.0 start /Users/ksera/github/bolt-js/examples/custom-properties
> node http.js
AppInitializationError: This App instance is not yet initialized. Call `await App#init()` before starting the app.
at App.start (/Users/ksera/github/bolt-js/dist/App.js:239:19)
at /Users/ksera/github/bolt-js/examples/custom-properties/http.js:46:15
at Object.<anonymous> (/Users/ksera/github/bolt-js/examples/custom-properties/http.js:52:3)
at Module._compile (internal/modules/cjs/loader.js:1158:30)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:1178:10)
at Module.load (internal/modules/cjs/loader.js:1002:32)
at Function.Module._load (internal/modules/cjs/loader.js:901:14)
at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:74:12)
at internal/main/run_main_module.js:18:47 {
code: 'slack_bolt_app_initialization_error'
}
npm ERR! code ELIFECYCLE
npm ERR! errno 255
npm ERR! bolt-js-custom-properties-app@1.0.0 start: `node http.js`
npm ERR! Exit status 255
npm ERR!
await app.start(process.env.PORT || 3000); | ||
|
||
try { | ||
await app.init(); |
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.
Example error pattern:
> bolt-js-custom-properties-app@1.0.0 start /Users/ksera/github/bolt-js/examples/custom-properties
> node http.js
AppInitializationError: Apps used in a single workspace can be initialized with a token. Apps used in many workspaces should be initialized with oauth installer options or authorize.
Since you have not provided a token or authorize, you might be missing one or more required oauth installer options. See https://slack.dev/bolt-js/concepts#authenticating-oauth for these required fields.
at App.initAuthorizeIfNoTokenIsGiven (/Users/ksera/github/bolt-js/dist/App.js:660:19)
at App.init (/Users/ksera/github/bolt-js/dist/App.js:180:47)
at /Users/ksera/github/bolt-js/examples/custom-properties/http.js:46:15
at Object.<anonymous> (/Users/ksera/github/bolt-js/examples/custom-properties/http.js:53:3)
at Module._compile (internal/modules/cjs/loader.js:1158:30)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:1178:10)
at Module.load (internal/modules/cjs/loader.js:1002:32)
at Function.Module._load (internal/modules/cjs/loader.js:901:14)
at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:74:12)
at internal/main/run_main_module.js:18:47 {
code: 'slack_bolt_app_initialization_error'
}
npm ERR! code ELIFECYCLE
npm ERR! errno 255
npm ERR! bolt-js-custom-properties-app@1.0.0 start: `node http.js`
npm ERR! Exit status 255
@@ -514,6 +514,11 @@ export default class App { | |||
public start( | |||
...args: Parameters<HTTPReceiver['start'] | SocketModeReceiver['start']> | |||
): ReturnType<HTTPReceiver['start']> { | |||
if (!this.initialized) { | |||
throw new AppInitializationError( | |||
'This App instance is not yet initialized. Call `await App#init()` before starting the app.', |
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.
Any suggestions on the error message?
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.
This is great!
Co-authored-by: Sarah Jiang <srajiang@gmail.com>
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.
Thanks for turning around these changes! It's looking good to me :)
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.
Thanks for making the changes! Looks good to me
And most definitely I will help with the docs, added to my todo list for this week. I will work on a PR for that.
Thanks for the reviews! Let me merge this PR now. |
Summary
This pull request resolves #248 by adding
deferInitialization
option along withApp#init()
method, which enable developers to take full control of the timing to run the initialization steps that require async/await code executions.JavaScript/TypeScript does not provide any ways to run async functions inside object constructors. That's why the issue #248 has not been resolved for a long time.
This pull request proposes a new option to customize the behavior to overcome the limitation. With the new way, developers can catch the exception that can be thrown by
App#init()
method:Requirements (place an
x
in each[ ]
)