-
Notifications
You must be signed in to change notification settings - Fork 7
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
CPLAT-9308 Codemod Utility to tell if Something is Public API #74
CPLAT-9308 Codemod Utility to tell if Something is Public API #74
Conversation
Security InsightsNo security relevant content was detected by automated scans. Action Items
Questions or Comments? Reach out on Slack: #support-infosec. |
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 wrote some comments without realizing that this wasn't ready for review yet 🤦♂ Sorry about that!
I figured I'd post the comments anyways, but take them with a grain of salt!
setUpAll(() async { | ||
final helper = SemverHelper(); | ||
await helper.fromReport( | ||
'/Users/sydneyjodon/Documents/GitHub/over_react_codemod/lib/src/boilerplate_suggestors/test.json'); |
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.
We need a version of the report tracked in Git somehow, as opposed to this hard-coded API.
To make testing easier, perhaps having a constructor that takes in the map directly would be helpful. That way, you can embed the JSON in the test, and also choose how to load the report in actual usage.
SemverHelper.fromReport(this.exportList);
// in the test for SemverHelper
final helper = SemverHelper({'exports': ...});
// in usage:
final helper = SemverHelper(jsonDecode(await File('report.json').readAsString()));
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.
Also, a test for a Suggestor would be a case where we'd want to use the mock for SemverHelper as opposed to a real instance, to make test setup easier (by not having to set up the whole exports map).
…PublicExportLocations-utility-function # Conflicts: # lib/src/boilerplate_suggestors/annotations_remover.dart # lib/src/boilerplate_suggestors/boilerplate_utilities.dart # lib/src/executables/boilerplate_upgrade.dart # test/boilerplate_suggestors/boilerplate_utilities_test.dart
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.
Just a couple early thoughts! :)
@override | ||
visitClassDeclaration(ClassDeclaration node) { | ||
super.visitClassDeclaration(node); | ||
|
||
if (!shouldMigrateAdvancedPropsAndStateClass(node)) return; | ||
if (!shouldMigrateAdvancedPropsAndStateClass(node, helper)) return; |
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 suggestion builds on code that isn't here yet (it would come from Aaron's branches), but how would you feel about integrating the helper into an isPublic
function? I think the implementation could be something like:
boilerplate_utilities.dart
// Create a helper that the utilities can see
SemverHelper semverHelper;
bool isPublic(ClassDeclaration node) {
assert(semverHelper != null);
return semverHelper.getPublicExportLocations(node) != null;
}
bool shouldMigratePropsAndStateClass(
ClassDeclaration node) {
return isAssociatedWithComponent2(node) &&
isAPropsOrStateClass(node) && !isPublic(node);
boilerplate_upgrade.dart
semverHelper = SemverHelper(jsonDecode(
await File('lib/src/boilerplate_suggestors/report.json').readAsString()));
The main thing I'm trying to prevent is passing around the same helper instance through all the suggestors and utilities, but if that seems like a good goal then I'm open to whatever implementation works best!
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.
The main thing I'm trying to prevent is passing around the same helper instance through all the suggestors and utilities, but if that seems like a good goal then I'm open to whatever implementation works best!
Passing around the instance is actually what I'd prefer, as opposed to a global variable.
Advantages:
- Eliminates the risk of different areas of the code unintentionally interfering with each other's data (including in tests)
-Make it easier to reason about where this is used, and force us to be intentional about where we pass it
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 could definitely see that, and normally I wouldn't advocate for a global variable, but my main thought is that the function which answers the question of whether a node is public or not is at the utility level, and it doesn't seem like the suggestor should need to be concerned with how it answers that question (outside of passing in the node).
Also, because this helper should receive the report for the relevant library it's running on, it seems like it makes sense for it to operate independently since any instantiation for an executable should be passing it the same report. For tests that does break down, but then the testing environment could have its own instance when needed. If you feel strongly, Greg, I think that generally global variables are bad, this is just a weird case 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 feel pretty strongly here that moving away from a top-level variable would improve the modularity of this code, as well as the predictability of how data is passed around and used.
it seems like it makes sense for it to operate independently since any instantiation for an executable should be passing it the same report
That's true, but may not always be true. For example, what if we wanted this utility to behave slightly differently for different suggestors? Or if we wanted to run multiple sets of suggestors on different directories concurrently? Having global variables would make it much more difficult to implement these changes. To be fair, this is the weakest argument of the ones I've presented, but it is something to keep in mind.
I'm happy to make a PR with those changes, especially since some work was already done to move it more toward top-level variables.
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.
Moving away from a global variable sounds good to me. Thanks for explaining your logic, Greg! And sorry for making ya change it in the first place Sydney 😅
final className = node?.name?.name; | ||
String classKey; | ||
|
||
if (className == null || _exportList == null) return null; |
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.
It seems like we may want this to be a louder failure. Have we seen the class name be null
before? I'd be curious to see what that code looks like haha
If these can be null
though, I'd expect it to throw an Exception
since we need the list and class name to really verify if the class is exported, and returning null
could be a false negative
…ortLocations-utility-function # Conflicts: # test/boilerplate_suggestors/simple_props_and_state_class_migrator_test.dart
…od' into CPLAT-9205-new-boilerplate-codemod/CPLAT-9308-getPublicExportLocations-utility-function
…PublicExportLocations-utility-function # Conflicts: # lib/src/boilerplate_suggestors/boilerplate_utilities.dart
…PublicExportLocations-utility-function # Conflicts: # lib/src/executables/boilerplate_upgrade.dart
test/boilerplate_suggestors/simple_props_and_state_class_migrator_test.dart
Outdated
Show resolved
Hide resolved
…PublicExportLocations-utility-function # Conflicts: # test/boilerplate_suggestors/boilerplate_utilities_test.dart
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.
+10, assuming that #74 (comment) is addressed in your other PR #80
Motivation
To support CPLAT-9205, we need a way to tell whether something is a public API.
Don't worry about generating the semver report in this ticket. We can either run
semver_audit
or download the appropriate report in the repo_commander script. And then the codemod can assume that it exists at a "report.json" file or somethingUse a local semver_audit report to tell whether a component class is part of the public API. What the report looks like:
Add a function (likely encapsulated in a class, or whatever makes sense):
Make it possible to call this function repeatedly re-parsing the semver report (which can be upwards of 10MB for some repos)
Changes
SemverHelper
class withgetPublicExportLocations
utility.report.json
mock to help with testing codemod suggestors in other tickets.Release Notes
Review
See CONTRIBUTING.md for more details on review types (+1 / QA +1 / +10) and code review process.
Please review: @aaronlademann-wf @joebingham-wk @greglittlefield-wf
QA Checklist
Merge Checklist
While we perform many automated checks before auto-merging, some manual checks are needed: