-
Notifications
You must be signed in to change notification settings - Fork 662
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
Add Support for FileInstallationStore #1279
Add Support for FileInstallationStore #1279
Conversation
if (this.historicalDataEnabled) { | ||
const currentUTC = Date.now(); | ||
writeToFile(`${installationDir}/app-${currentUTC}`, installationData); | ||
writeToFile(`${installationDir}/user-${user.id}-${currentUTC}`, installationData); |
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 mentioned this in the summary, but this is where it's applicable, so carrying it over:
The Python FileStoreInstallation goes a step further by saving the same information into both installer-latest / installer-[currentUTC] and the user-[user_id]-* records. I omitted the former records because it wasn't clear to me how the duplication was value-adding, but am open to introducing it if folks feel it's important or if I've misunderstood its purpose.
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.
Specifically talking about this file based implementation, not having all the historical data is also fine as most developers won't use it for production.
The reason why we store all installations in Python / Java is that having the data can be helpful for troubleshooting / system operations in production. When you do investigation about an issue related to a user who installed the app multiple times, checking each installation's timestamp, the difference on its scopes, and the existence of enterprise_id may help.
If we think this file based one is a kind of reference implementation, having this consideration may be worth. Not a strong opinion here, though. Just having a comment about this context there is also 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.
Totally get it and am with you regarding the importance. 👍🏼
I think what I'm not understanding is the difference between these two records, other than the extra formatting done to the first, and why having both is necessary or beneficial.
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.
Should we consider setting saving historical data default to false? Users could set it to true if they want to save historical data.
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 don't have a strong opinion on this. Happy to go with whatever others deem the best default.
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 what I'm not understanding is the difference between these two records, other than the extra formatting done to the first, and why having both is necessary or beneficial.
Even after revoking all the user installations, bot token must still exist. This is one of the major improvements in GBP (Granular Bot Permissions). This is the reason why Python / Java SDK have bots table separately.
If there is a better alternative way, we can go with a different approach in Node SDK first and implement the same in others, though. Having is_user_token_revoked
flag in the installation table plus trying to fetch a bot token even when all installations are marked as deleted may work. Do we think this is a better approach? 🤔
Should we consider setting saving historical data default to false?
In Python / Java SDK, the historical data support is enabled by default. For consistency and encouraging a similar approach for production app design, I prefer turning it on but I am open to change this default for Node SDK (and possibly for others in the future major versions).
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.
@misscoded @stevengill For enabling the historical data support by default or not, I'm happy to leave the decision to you all. I've already shared my thoughts on it in the above comment. If we want to change the defaults for Python / Java, we may want to hold off doing so until the next major releases (we won't do at least this year for both).
For having bots and user installations, as I mentioned above, having something similar (or user_token_revocation flag) should be necessary to keep a bot token even after the user(s) who installed the app are deactivated or all the user tokens are revoked. For these two store implementations, I think it's okay not to support the use case.
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 makes sense to have historical data enabled as the default, especially to maintain parity between the three languages. So we're good on that point! I'll keep it as is.
I'd like to revisit the other point you bring up when I circle back around to sort out the lack of support for user tokens as a whole, unless you think we shouldn't move forward with introducing this until that's sorted. I think I'm still fuzzy on the details.
} | ||
|
||
public async fetchInstallation(query: InstallationQuery<boolean>, logger?: Logger): Promise<Installation> { | ||
const { enterpriseId, teamId } = query; |
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.
MemoryInstallationStore
doesn't take into consideration user_id
, so I omitted it here, but now that I'm reading over it in this context..
Given that user.id
has been introduced in storeInstallation
above (edit: and deleteInstallation
below), my gut feeling is that it needs to be introduced here, as well, but confirmation of this from others would be useful.
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.
How about having TODO comments on user token scope installation support?
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.
@seratch so to support user tokens, it seems we would want to fetch /user-{user_id}-latest
from the installationDirectory. How do you determine if you want to return this over app-latest
? Do you look for the existence of user_id
in the query
and /user-{user_id}-latest
on the file system and return it over app-latest
? How about if you were explicitly looking for a user token existing, in that scenario if /user-{user_id}-latest
didn't exist, you would want to throw an error instead of getting app-latest
which might contain a user token belonging to a different user.
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.
@stevengill Performing an additional query would be necessary for it. Here are the ways we do in Python / Java.
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.
@seratch this is very helpful! So the idea being do two fetchinstallation calls in authorize (or do this logic in fetchInstallatino itself). Roughly, in this JS implementation, the first fetchInstallation call would grab app-latest
. We would want to strip out user_id
from query so it doesn't try to grab a user-{user_id}-latest
instead. The second call to fetchInstallation we would pass in the userId and it would look for user-{user_id}-latest
. It would pull out the user portion of the installation Object and attach it to the original installationObject we get back from app-latest
. It covers some of the edge cases I was worried about. Nice solution.
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.
To limit the scope, I'm in favor of leaving a TODO to address the user token support at a later time if others are.
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 haven't verified if it works fine but at first glance, the changes look good to me. Let a few comments.
if (this.historicalDataEnabled) { | ||
const currentUTC = Date.now(); | ||
writeToFile(`${installationDir}/app-${currentUTC}`, installationData); | ||
writeToFile(`${installationDir}/user-${user.id}-${currentUTC}`, installationData); |
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.
Specifically talking about this file based implementation, not having all the historical data is also fine as most developers won't use it for production.
The reason why we store all installations in Python / Java is that having the data can be helpful for troubleshooting / system operations in production. When you do investigation about an issue related to a user who installed the app multiple times, checking each installation's timestamp, the difference on its scopes, and the existence of enterprise_id may help.
If we think this file based one is a kind of reference implementation, having this consideration may be worth. Not a strong opinion here, though. Just having a comment about this context there is also fine.
} | ||
|
||
public async fetchInstallation(query: InstallationQuery<boolean>, logger?: Logger): Promise<Installation> { | ||
const { enterpriseId, teamId } = query; |
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.
How about having TODO comments on user token scope installation support?
This pull request seems to hav conflicts with the latest revision in main branch. Resolving it before you finalize this work would make things easier. |
@misscoded If we are comfortable to have this change in the next minor (v2.2 - the main update is the token rotation support), you can merge this pull request. Otherwise, you can move this PR and its issue to v2.3 milestone! |
I think we're good, just waiting on Steve to give the final go ahead on what's here. |
Summary
Fixes #1278:
Exportability for Choice
Though
MemoryInstallationStore
remains the default if not otherwise specified, to allow developers the choice between usingMemoryInstallationStore
andFileInstallationStore
, both are now exported for direct use in their projects (set to theinstallationStore
constructor option):Options include:
baseDir
,clientId
, andhistoricalDataEnabled
. Developers can go with the defaults, or override to suit their needs.Installation Directory + File Pattern
For the most part, implementation has stayed true to what is found in Python, but some differences are detailed below:
enterprise_id
andteam_id
are used to create the installation directory. If both present, the pattern isenterprise_id-team_id
. If only one of the two are present, only that ID is used for the directory. This differs slightly from Python's use ofnone
in the directory name if one is not passed.storeInstallation
is called, ifhistoricalDataEnabled
is enabled (which it is by default), 4 records will be created: anapp-latest
,user-[user_id]-latest
,app-[currentUTC]
anduser-[user_id]-[currentUTC]
. Every subsequent install will overwrite the data in latest and save two additionalcurrentUTC
records. If set tofalse
by the developer, only*-latest
records will be saved.installer-latest
/installer-[currentUTC]
and theuser-[user_id]-*
records. I omitted the former records because it wasn't clear to me how the duplication was value-adding, but am open to introducing it if folks feel it's important or if I've misunderstood its purpose.to_bot()
method to shape the data prior to writing it to file, but for a similar reason to the above, I forwent that. Also equally happy to introduce it if it's the better (or necessary) approach.Directory/File Restructuring
/stores
directory. Since we now have two stores, I've pulled out theMemoryInstallationStore
class into astores
directory, alongside the newFileInstallationStore
. These two are then made available in a dedicatedindex.js
so that theimport
is cleaner (both importable directly from/stores
).Requirements (place an
x
in each[ ]
)