Skip to content
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

[HOLD for payment 2024-07-10] [HOLD for payment 2024-07-02] [$500] Remove exported ReportUtils.getReport() #40316

Closed
Tracked by #27262
tgolen opened this issue Apr 16, 2024 · 47 comments
Assignees
Labels
Awaiting Payment Auto-added when associated PR is deployed to production Bug Something is broken. Auto assigns a BugZero manager. Engineering External Added to denote the issue can be worked on by a contributor Reviewing Has a PR in review Weekly KSv2

Comments

@tgolen
Copy link
Contributor

tgolen commented Apr 16, 2024

This is coming from #27262. You can read the issue description there to get the context behind the problem being solved and the mess being cleaned up.

Problem

ReportActionUtils.getReport() is called from many view components and other action files which is an anti-pattern.

Why this is important to fix

It maintains a more pure and exact flow of data through the react application. If the view is using report action data, then it needs to subscribe to the data in Onyx so that it's guaranteed that the data will never be stale or out-of-date.

Solution

Upwork Automation - Do Not Edit
  • Upwork Job URL: https://www.upwork.com/jobs/~014f8c2e6e8d2c1390
  • Upwork Job ID: 1780353316902400000
  • Last Price Increase: 2024-06-12
  • Automatic offers:
    • s77rt | Reviewer | 0
    • gijoe0295 | Contributor | 0
    • nkdengineer | Contributor | 102706170
Issue OwnerCurrent Issue Owner: @MitchExpensify
@tgolen tgolen self-assigned this Apr 16, 2024
@tgolen tgolen added External Added to denote the issue can be worked on by a contributor Engineering Bug Something is broken. Auto assigns a BugZero manager. labels Apr 16, 2024
@melvin-bot melvin-bot bot changed the title Remove exported ReportUtils.getReport() [$250] Remove exported ReportUtils.getReport() Apr 16, 2024
Copy link

melvin-bot bot commented Apr 16, 2024

Job added to Upwork: https://www.upwork.com/jobs/~014f8c2e6e8d2c1390

@melvin-bot melvin-bot bot added the Help Wanted Apply this label when an issue is open to proposals by contributors label Apr 16, 2024
Copy link

melvin-bot bot commented Apr 16, 2024

Triggered auto assignment to Contributor-plus team member for initial proposal review - @s77rt (External)

Copy link

melvin-bot bot commented Apr 16, 2024

Triggered auto assignment to @MitchExpensify (Bug), see https://stackoverflow.com/c/expensify/questions/14418 for more details. Please add this bug to a GH project, as outlined in the SO.

@gijoe0295
Copy link
Contributor

gijoe0295 commented Apr 16, 2024

Proposal

Please re-state the problem that we are trying to solve in this issue.

Remove exported ReportUtils.getReport()

What is the root cause of that problem?

This is a polish issue.

What changes do you think we should make in order to solve the problem?

There are currently 28 places using getReport(), we should either use withOnyx (12 places) or Onyx.connect instead. Note that we should keep it inside ReportUtils file, just no longer export it. Then add a test to EnforceActionExportRestrictions to make sure we won't use it in the future.

let allReports: OnyxCollection<Report>;
Onyx.connect({
    key: ONYXKEYS.COLLECTION.REPORT,
    waitForCollectionCallback: true,
    callback: (value) => (allReports = value),
});
withOnyx(
  report: {
      key: ({chatReportID}) => `${ONYXKEYS.COLLECTION.REPORT}${chatReportID}`,
  },
)

What alternative solutions did you explore? (Optional)

NA

@nkdengineer
Copy link
Contributor

nkdengineer commented Apr 17, 2024

Proposal

Please re-state the problem that we are trying to solve in this issue.

Remove deprecated ReportUtils.getReport() method

What is the root cause of that problem?

This is refactor issue

What changes do you think we should make in order to solve the problem?

  1. For component we should get report via withOnyx of if the component already subscribe the report collection, we will get the report from this
report: {
    key: ({reportID}) =>  `${ONYXKEYS.COLLECTION.REPORT}${reportID}`
}
  1. For the libs action, we should use Onyx.connect to get allReports and then use this to get the report
let allReports: OnyxCollection<Report>;
Onyx.connect({
    key: ONYXKEYS.COLLECTION.REPORT,
    waitForCollectionCallback: true,
    callback: (value) => (allReports = value),
});

OPTION: We can create a local function in action file to get the report with reportID and use this in other place of this file that we want.

  1. Add a test here to verify that we do not export getReport function in ReportUtils
it('does not export getReport', () => {
    // @ts-expect-error the test is asserting that it's undefined, so the TS error is normal
    expect(ReportUtils.getReport).toBeUndefined();
});

describe('ReportUtils', () => {

What alternative solutions did you explore? (Optional)

NA

@paultsimura
Copy link
Contributor

@tgolen have we already found a way to synchronize all the Onyx reads?

Using Onyx.connect sometimes leads to unexpected bugs:
https://expensify.slack.com/archives/C01GTK53T8Q/p1711017867335759
https://expensify.slack.com/archives/C01GTK53T8Q/p1709843924926719

@tgolen
Copy link
Contributor Author

tgolen commented Apr 17, 2024

If most of the code used withOnyx() to access the report then I believe the problem goes away. Right? This would be the preferred method. I think those threads illustrate the exact problem of why getReport() is not a good pattern.

@paultsimura
Copy link
Contributor

If most of the code used withOnyx() to access the report then I believe the problem goes away

I'm not quite sure how this should help. The first thread was about Onyx.connect inside OptionListUtils not being executed before OptionListUtils.getReportOption is called, therefore allReports were empty.

For the record, OptionListUtils.getReportOption was called from within a component that uses withOnyx to fetch the report.

@tgolen
Copy link
Contributor Author

tgolen commented Apr 17, 2024

For the record, OptionListUtils.getReportOption was called from within a component that uses withOnyx to fetch the report.

Yes, this is what I mean. If the report was already fetched in the component, then the report should be passed directly to OptionListUtils.getReportOption(). It shouldn't be necessary for OptionListUtils.getReportOption() to try to access allReports to get the report and it wouldn't matter if allReports was empty or not.

Keep in mind, I am only speaking to the theory behind it. It's been a long time since I've dug into the OptionsListUtils code, so I don't currently know how possible it is to actually do this.

@paultsimura
Copy link
Contributor

If the report was already fetched in the component, then the report should be passed directly to OptionListUtils.getReportOption()

Passing objects around as parameters just because we cannot reliably fetch them from any place feels like a heavier anti-pattern than the one we are trying to solve by removing ReportUtils.getReport, IMHO.

Also, what do you think about this suggestion by @hurali97?

@tgolen
Copy link
Contributor Author

tgolen commented Apr 17, 2024

what do you think about #35234 (comment) suggestion by @hurali97?

I think it reflects what I would expect for large collections. This is why I think waitForCollectionCallback is preferred and I think a better solution than building a singleton would be to do either or both of:

  • Use waitForCollectionCallback everywhere
  • Improve getCollectionDataAndSendAsObject to be more performant

I'm not flatly against a singleton, but I think that is a last resort and I have always been concerned that it's the "easy solution" which covers up deeper problems. I'd rather fix the deeper problems, and only when those are completely exhausted, consider using a singleton.

@s77rt
Copy link
Contributor

s77rt commented Apr 17, 2024

@tgolen

Use Onyx.connect() to load the data in other action files

Why would that be needed? Or what's the difference between calling ReportUtils.getReport (which already uses Onyx.connect) vs. calling Onyx.connect in each action file. The latter will only increase memory usage

@s77rt
Copy link
Contributor

s77rt commented Apr 17, 2024

@gijoe0295 @nkdengineer Thank you for your proposals. Given this is a straightforward feature/refactor request we should go with the first proposal (@gijoe0295's proposal)

🎀 👀 🎀 C+ reviewed
Link to proposal

Copy link

melvin-bot bot commented Apr 17, 2024

Current assignee @tgolen is eligible for the choreEngineerContributorManagement assigner, not assigning anyone new.

@tgolen
Copy link
Contributor Author

tgolen commented Apr 18, 2024

Before accepting any proposals... @gijoe0295 did you take a look at the references to see if it's easy (or even possible) to get the report using withOnyx() in the components that need it? This was one of the bigger concerns I had about it. Some references might be easy, but others, it might not be very straightforward how to get the report using withOnyx().

@tgolen
Copy link
Contributor Author

tgolen commented Apr 18, 2024

The latter will only increase memory usage

Regarding memory usage, I don't think there is that big of an impact. They should all be references to the same objects, so memory is negligible. If I'm wrong about that, please profile it and show the memory problem exists and is significant enough to be concerned about.

what's the difference between calling ReportUtils.getReport (which already uses Onyx.connect) vs. calling Onyx.connect in each action file.

You're right that there is no technical difference between the two. The difference for me is a code quality and maintenance issue. As soon as ReportUtils.getReport() is exported, even if you and I agree it should only be used in other action files, there are going to be 10 random engineers that see the exported function and use it in front-end view components. If you know of a different way of solving for this problem, I am open to suggestions. I don't mind if library files share these utility lookup methods, but I feel VERY STRONGLY that view components should not be using them.

@gijoe0295
Copy link
Contributor

gijoe0295 commented Apr 18, 2024

@tgolen I investigated and verified that we can use withOnyx for all existing components. Some places might use conditions or computed values to get the report but those conditions/values can be evaluated inside withOnyx's key callback.

We can optionally migrate to useOnyx that was just implemented in #34339 and recommended to use in this thread although it is new and not time-proven. Wdyt?

@tgolen
Copy link
Contributor Author

tgolen commented Apr 18, 2024

Great, thank you for confirming that. Using useOnyx() is fine as well 👍

@nkdengineer
Copy link
Contributor

@s77rt @tgolen Please help to take a look at this PR. @war-in Please add the issue link and ready for review so bot can assign @s77rt as C+ in this PR.

@melvin-bot melvin-bot bot added Weekly KSv2 Awaiting Payment Auto-added when associated PR is deployed to production and removed Weekly KSv2 labels Jun 21, 2024
@melvin-bot melvin-bot bot changed the title [$500] Remove exported ReportUtils.getReport() [HOLD for payment 2024-07-02] [$500] Remove exported ReportUtils.getReport() Jun 25, 2024
Copy link

melvin-bot bot commented Jun 25, 2024

Reviewing label has been removed, please complete the "BugZero Checklist".

@melvin-bot melvin-bot bot removed the Reviewing Has a PR in review label Jun 25, 2024
Copy link

melvin-bot bot commented Jun 25, 2024

The solution for this issue has been 🚀 deployed to production 🚀 in version 9.0.1-19 and is now subject to a 7-day regression period 📆. Here is the list of pull requests that resolve this issue:

If no regressions arise, payment will be issued on 2024-07-02. 🎊

For reference, here are some details about the assignees on this issue:

Copy link

melvin-bot bot commented Jun 25, 2024

BugZero Checklist: The PR fixing this issue has been merged! The following checklist (instructions) will need to be completed before the issue can be closed:

  • [@s77rt] The PR that introduced the bug has been identified. Link to the PR:
  • [@s77rt] The offending PR has been commented on, pointing out the bug it caused and why, so the author and reviewers can learn from the mistake. Link to comment:
  • [@s77rt] A discussion in #expensify-bugs has been started about whether any other steps should be taken (e.g. updating the PR review checklist) in order to catch this type of bug sooner. Link to discussion:
  • [@s77rt] Determine if we should create a regression test for this bug.
  • [@s77rt] If we decide to create a regression test for the bug, please propose the regression test steps to ensure the same bug will not reach production again.
  • [@MitchExpensify] Link the GH issue for creating/updating the regression test once above steps have been agreed upon:

@MitchExpensify
Copy link
Contributor

MitchExpensify commented Jun 26, 2024

Payment summary:

@s77rt
Copy link
Contributor

s77rt commented Jun 27, 2024

Checklist ^ does not apply here. Not a bug

@MitchExpensify
Copy link
Contributor

Paid and contract ended for @s77rt

Please accept this offer @nkdengineer so I can do the same for you: https://www.upwork.com/nx/wm/offer/102706170, thanks!

@s77rt
Copy link
Contributor

s77rt commented Jul 2, 2024

@MitchExpensify Payment due is $250. -50% for regression #43632 (comment). I have refunded $250.

@melvin-bot melvin-bot bot added Daily KSv2 and removed Weekly KSv2 labels Jul 2, 2024
@nkdengineer
Copy link
Contributor

@MitchExpensify I accepted the offer too, thanks

@melvin-bot melvin-bot bot added Weekly KSv2 and removed Daily KSv2 labels Jul 3, 2024
@melvin-bot melvin-bot bot changed the title [HOLD for payment 2024-07-02] [$500] Remove exported ReportUtils.getReport() [HOLD for payment 2024-07-10] [HOLD for payment 2024-07-02] [$500] Remove exported ReportUtils.getReport() Jul 3, 2024
Copy link

melvin-bot bot commented Jul 3, 2024

The solution for this issue has been 🚀 deployed to production 🚀 in version 9.0.3-7 and is now subject to a 7-day regression period 📆. Here is the list of pull requests that resolve this issue:

If no regressions arise, payment will be issued on 2024-07-10. 🎊

For reference, here are some details about the assignees on this issue:

Copy link

melvin-bot bot commented Jul 3, 2024

BugZero Checklist: The PR fixing this issue has been merged! The following checklist (instructions) will need to be completed before the issue can be closed:

  • [@s77rt] The PR that introduced the bug has been identified. Link to the PR:
  • [@s77rt] The offending PR has been commented on, pointing out the bug it caused and why, so the author and reviewers can learn from the mistake. Link to comment:
  • [@s77rt] A discussion in #expensify-bugs has been started about whether any other steps should be taken (e.g. updating the PR review checklist) in order to catch this type of bug sooner. Link to discussion:
  • [@s77rt] Determine if we should create a regression test for this bug.
  • [@s77rt] If we decide to create a regression test for the bug, please propose the regression test steps to ensure the same bug will not reach production again.
  • [@MitchExpensify] Link the GH issue for creating/updating the regression test once above steps have been agreed upon:

@MitchExpensify
Copy link
Contributor

Paid! Thanks @nkdengineer

@melvin-bot melvin-bot bot added Reviewing Has a PR in review Weekly KSv2 and removed Weekly KSv2 labels Aug 16, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Awaiting Payment Auto-added when associated PR is deployed to production Bug Something is broken. Auto assigns a BugZero manager. Engineering External Added to denote the issue can be worked on by a contributor Reviewing Has a PR in review Weekly KSv2
Projects
None yet
Development

No branches or pull requests

7 participants