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

feat(TransactionFeedV2): Add RTK-Query, add api slice for Transaction Feed V2 #6134

Merged
merged 16 commits into from
Oct 11, 2024

Conversation

sviderock
Copy link
Contributor

@sviderock sviderock commented Oct 6, 2024

Description

1st PR for RET-1207. It adds RTK-Query and api slice for the new Transaction Feed V2.

All the reducers up to this point were always manually maintained/changed by us. For this purpose, we have Redux migrations which are covered with tests to ensure compatibility between different versions of Redux state. With introduction of RTK-Query we introduce library-managed api reducers, which must not be manually tweaked as library can change how it stores its structure (api reducer is a cache for API endpoint).

For this purpose, it is mandatory to omit api reducers completely from the flow of typescript-json-schema generation. This implemenation moves existing reducers to a separate file as generating schema with api reducers throws an error of unsupported type from @reduxjs/tookit/query/react.

This new Transaction Feed V2 (that is gonna be implemented over a few PRs) is intended to completely replace existing TransactionFeed, queryHelper and corresponding test files.
It will be closed behind the feature gate from Statsig for the initial release.

Test plan

With this implementation, all the existing tests are working and there is no issues with persistence as the api reducers are completely omitted from the persistence flow. Transactions persistence for Transaction Feed V2 will be implemented in the follow-up PRs.

Related issues

Backwards compatibility

Yes

Network scalability

If a new NetworkId and/or Network are added in the future, the changes in this PR will:

  • Continue to work without code changes, OR trigger a compilation error (guaranteeing we find it when a new network is added)

Copy link

codecov bot commented Oct 7, 2024

Codecov Report

Attention: Patch coverage is 92.38579% with 15 lines in your changes missing coverage. Please review.

Project coverage is 88.75%. Comparing base (1aa94d6) to head (2dad608).
Report is 6 commits behind head on main.

Files with missing lines Patch % Lines
src/transactions/feed/TransactionFeedV2.tsx 88.28% 14 Missing and 1 partial ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main    #6134      +/-   ##
==========================================
- Coverage   88.75%   88.75%   -0.01%     
==========================================
  Files         727      727              
  Lines       30806    30752      -54     
  Branches     5323     5624     +301     
==========================================
- Hits        27343    27295      -48     
+ Misses       3419     3259     -160     
- Partials       44      198     +154     
Files with missing lines Coverage Δ
src/redux/api.ts 100.00% <100.00%> (ø)
src/redux/apiReducersList.ts 100.00% <100.00%> (ø)
src/redux/migrations.ts 97.05% <100.00%> (+<0.01%) ⬆️
src/redux/reducers.ts 83.33% <100.00%> (-10.96%) ⬇️
src/redux/reducersList.ts 100.00% <100.00%> (ø)
src/redux/store.ts 80.00% <100.00%> (+1.31%) ⬆️
src/transactions/NoActivity.tsx 100.00% <ø> (ø)
src/transactions/api.ts 100.00% <100.00%> (ø)
src/transactions/apiTestHelpers.ts 100.00% <100.00%> (ø)
src/transactions/reducer.ts 85.21% <100.00%> (ø)
... and 5 more

... and 88 files with indirect coverage changes


Continue to review full report in Codecov by Sentry.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 1aa94d6...2dad608. Read the comment docs.

Copy link
Member

@jeanregisser jeanregisser left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great! 👍

@@ -25,6 +27,16 @@ const resetStateOnInvalidStoredAccount = jest.spyOn(

const loggerErrorSpy = jest.spyOn(Logger, 'error')

const getNonApiReducers = <R = Omit<RootState, ApiReducersKeys>>(state: RootState): R => {
Copy link
Member

@jeanregisser jeanregisser Oct 8, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: prefer function instead of const arrow functions, as it's easier to spot it's a function when parsing the file visually. Plus it's a few less chars :D

Suggested change
const getNonApiReducers = <R = Omit<RootState, ApiReducersKeys>>(state: RootState): R => {
function getNonApiReducers<R = Omit<RootState, ApiReducersKeys>>(state: RootState): R {

I know we haven't been super consistent with this in this codebase though.

@@ -25,6 +27,16 @@ const resetStateOnInvalidStoredAccount = jest.spyOn(

const loggerErrorSpy = jest.spyOn(Logger, 'error')

const getNonApiReducers = <R = Omit<RootState, ApiReducersKeys>>(state: RootState): R => {
const apiReducersKeys: string[] = ['transactionFeedV2Api'] satisfies ApiReducersKeys[]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we do this instead? to avoid having to update multiple files when we update them.

Suggested change
const apiReducersKeys: string[] = ['transactionFeedV2Api'] satisfies ApiReducersKeys[]
const apiReducersKeys: string[] = Object.keys(apiReducersList)

Comment on lines 32 to 37
return Object.entries(state).reduce((acc, [reducerKey, value]) => {
const key = reducerKey as keyof R
if (apiReducersKeys.includes(reducerKey)) return acc
acc[key] = value as unknown as any
return acc
}, {} as R)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: probably ok here because it's short, but I try to avoid reduce as it can often lead to hard to read code.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jeanregisser there are also a few .reduce usages in the follow-up PRs as well. Though the reason is not as applicable in this particular case, but I'm trying to reduce the amount of iterations the paginated data gets processed over. Considering, the Transaction Feed screen is one of the main screens with potentially lots of data I thought it made sense to try to minimize iterations so it is as optimized as possible.

I might be overthinking this so please let me know if that's the case. I've tried to make all the .reduce usages (the ones you'll see later in other PRs) fully descriptive but please let me know if there's anything that can be improved in that regard!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I think we should optimize for readability first, and avoid reduce in general. Given the number of elements we're dealing with, iterating once, twice or more times usually won't have much real world impact.
And we can usually refactor to iterate once with a for loop, if performance is measured to be a problem.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jeanregisser Got it, I'll tweak code in the other PRs to follow this approach!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jeanregisser refactored to for loop

import { RecursivePartial } from 'test/utils'

// https://medium.com/@johnmcdowell0801/testing-rtk-query-with-jest-cdfa5aaf3dc1
export function setupApiStore<
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we use/extend our existing mockStore instead?

wallet/test/utils.ts

Lines 76 to 82 in 1aa94d6

const mockStore = configureMockStore<RootState>()
/* Create a mock store with some reasonable default values */
export type RecursivePartial<T> = { [P in keyof T]?: RecursivePartial<T[P]> }
export function createMockStore(overrides: RecursivePartial<RootState> = {}) {
return mockStore(getMockStoreData(overrides))
}

So it's not specific to this reducer.

This way we'll respect one of the Redux testing principle:

Prefer writing integration tests with everything working together.

https://redux.js.org/usage/writing-tests

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jeanregisser Initially, I've tried to use this store and I couldn't get it to work.

To test RTK-Query it is required to have api middlewares in the mocked store. configureMockStore accepts an array of middlewares as an argument and I've tried to pass it was triggering for all the other tests to fail with error saying something similar to: "Actions must not be dispatched but passed as a plain object". Considering, that RTK-Query was released in 2021 and the last update for redux-mock-store was in 2018, it seems like there were no changes made to the library to make is able to work with RTK-Query.

I was looking for the solution and found this article (I've also put it in a comment there so there's some reference) that pointed me in a direction to try to use the helper function Redux team uses themselves to create a testable store. I've slightly modified it but it seemed to do the job.

P.S. Here's the quote from the article explaining the potential reason of why redux-mock-store doesn't work with RTK-Query:
"Typically we might reach for a tool like redux-mock-store but this library doesn’t work when testing RTK Query because dispatching RTK Query actions relies on there being a reducer for the API service and redux-mock-store doesn’t allow the use of reducers."

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jeanregisser I've now added all the data from getMockStoreData to it so it's identical. Sorry, definitely missed this initially!
The two mocked stores should be identical now in terms of preloaded data.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if we could/should skip these, given it looks like we're mostly testing the behavior of the library.
Which we can probably trust it's working as expected.

Instead we could focus on the higher level testing with the transaction feed tests which will exercise these anyway and we'll get coverage, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jeanregisser I totally agree. I've only added those to hide the codecov report warnings. Will remove!

...extraReducers,
}),
middleware: (gdm) =>
gdm({ serializableCheck: false, immutableCheck: false }).concat(api.middleware),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what's gdm?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jeanregisser Ah, that's getDefaultMiddleware. I've copied most of this function from this helper function and missed to rename this abbreviation. Will fix!

/**
* Even though payload is types as RootState - redux-persist can evaluate it as undefined.
*/
return action.payload?.[reducerPath]
Copy link
Member

@jeanregisser jeanregisser Oct 8, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So this means we'll persist and restore the API slice, right?

If that's correct, how will we handle changes to the API response that would require a client side migration?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jeanregisser Sorry, I will remove the whole extractRehydrationInfo part!

When I initially added RTK-Query, I was trying a lot of variations to try to keep api persisted so I've added this handler but forgot to remove it once it was no longer needed.

@sviderock sviderock changed the title feat(feed): Add RTK-Query, add api slice for Transaction Feed V2 feat(feed): Add RTK-Query, add api slice for Transaction Feed Oct 9, 2024
@sviderock sviderock changed the title feat(feed): Add RTK-Query, add api slice for Transaction Feed feat(feed): Add RTK-Query, add api slice for Transaction Feed V2 Oct 9, 2024
Copy link
Member

@jeanregisser jeanregisser left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚀

knip.ts Outdated
'src/utils/inputValidation.ts',
'src/utils/country.json',
'src/redux/reducersForSchemaGeneration.ts',
'src/transactions/apiTestHelpers.ts',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We'll use it in a follow up PR?

Copy link
Contributor Author

@sviderock sviderock Oct 9, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jeanregisser yes, already removed it in the next PR! Only added here to fix the knip error.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Maybe we should add a little comment about why we're doing this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jeanregisser yes, will add!

@sviderock sviderock added this pull request to the merge queue Oct 10, 2024
@github-merge-queue github-merge-queue bot removed this pull request from the merge queue due to failed status checks Oct 10, 2024
@sviderock sviderock added this pull request to the merge queue Oct 11, 2024
@github-merge-queue github-merge-queue bot removed this pull request from the merge queue due to failed status checks Oct 11, 2024
### Description
2nd PR for RET-1207. Implements the following list of basic Transactions
Feed functionality:
- Adds fetching of all the pages for the wallet address up to the point
when there are no transaction to fetch anymore (not showing "no
transactions" toast yet, will be added in the follow-up PR).
- Cursor for the next page is the timestamp of the last transaction from
the current page. If next page includes the same transaction - it gets
deduplicated.
- Re-uses pending transactions from `pendingStandByTransactionsSelector`
- Polls first page every 10 seconds
- Sorts transactions to show approvals at the top if the corresponding
transaction has identical timestamp

More comments for different parts of the pagination flow are added as
comments in the file in the [next
PR](#6136).

### Test plan
10 out of 16 tests from `TransactionFeed.test.ts` were re-used and
adjusted to the new data fetching flow. Other tests will be added in the
follow-up PRs purely for the sake of trying to keep the PRs smaller.

### Related issues

- Relates to RET-1207

### Backwards compatibility
Yes

### Network scalability

If a new NetworkId and/or Network are added in the future, the changes
in this PR will:

- [x] Continue to work without code changes, OR trigger a compilation
error (guaranteeing we find it when a new network is added)
@sviderock sviderock added this pull request to the merge queue Oct 11, 2024
Merged via the queue into main with commit 720ae00 Oct 11, 2024
15 checks passed
@sviderock sviderock deleted the slava/add-rtk-query branch October 11, 2024 12:10
@sviderock sviderock changed the title feat(feed): Add RTK-Query, add api slice for Transaction Feed V2 feat(TransactionFeedV2): Add RTK-Query, add api slice for Transaction Feed V2 Oct 15, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants