-
Notifications
You must be signed in to change notification settings - Fork 86
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
Changes from 13 commits
0e06735
fff449a
0168d7f
22984cb
48d5003
0fa8fc5
24b6321
f976bec
07c4f48
eb72b45
652faf7
28c343c
f2adff1
5fd96e0
fb7f7dd
2dad608
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
import { fetchBaseQuery } from '@reduxjs/toolkit/query/react' | ||
import networkConfig from 'src/web3/networkConfig' | ||
|
||
export const baseQuery = fetchBaseQuery({ | ||
baseUrl: networkConfig.blockchainApiUrl, | ||
headers: { | ||
Accept: 'application/json', | ||
}, | ||
}) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
import { transactionFeedV2Api } from 'src/transactions/api' | ||
|
||
export type ApiReducersKeys = keyof typeof apiReducersList | ||
|
||
export const apiReducersList = { | ||
[transactionFeedV2Api.reducerPath]: transactionFeedV2Api.reducer, | ||
} as const | ||
|
||
export const apiMiddlewares = [transactionFeedV2Api.middleware] |
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @jeanregisser yes, will add! |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
import { combineReducers } from 'redux' | ||
import { PersistState } from 'redux-persist' | ||
import { reducersList } from 'src/redux/reducersList' | ||
|
||
const reducerForSchemaGeneration = combineReducers(reducersList) | ||
export type RootStateForSchemaGeneration = ReturnType<typeof reducerForSchemaGeneration> & { | ||
_persist: PersistState | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
import { reducer as account } from 'src/account/reducer' | ||
import { reducer as alert } from 'src/alert/reducer' | ||
import { appReducer as app } from 'src/app/reducers' | ||
import dappsReducer from 'src/dapps/slice' | ||
import earnReducer from 'src/earn/slice' | ||
import { reducer as fiatExchanges } from 'src/fiatExchanges/reducer' | ||
import fiatConnectReducer from 'src/fiatconnect/slice' | ||
import { homeReducer as home } from 'src/home/reducers' | ||
import i18nReducer from 'src/i18n/slice' | ||
import { reducer as identity } from 'src/identity/reducer' | ||
import { reducer as imports } from 'src/import/reducer' | ||
import jumpstartReducer from 'src/jumpstart/slice' | ||
import keylessBackupReducer from 'src/keylessBackup/slice' | ||
import { reducer as localCurrency } from 'src/localCurrency/reducer' | ||
import { reducer as networkInfo } from 'src/networkInfo/reducer' | ||
import nftsReducer from 'src/nfts/slice' | ||
import pointsReducer from 'src/points/slice' | ||
import positionsReducer from 'src/positions/slice' | ||
import priceHistoryReducer from 'src/priceHistory/slice' | ||
import { recipientsReducer as recipients } from 'src/recipients/reducer' | ||
import { sendReducer as send } from 'src/send/reducers' | ||
import swapReducer from 'src/swap/slice' | ||
import tokenReducer from 'src/tokens/slice' | ||
import { reducer as transactions } from 'src/transactions/reducer' | ||
import { reducer as walletConnect } from 'src/walletConnect/reducer' | ||
import { reducer as web3 } from 'src/web3/reducer' | ||
|
||
export const reducersList = { | ||
app, | ||
i18n: i18nReducer, | ||
networkInfo, | ||
alert, | ||
send, | ||
home, | ||
transactions, | ||
web3, | ||
identity, | ||
account, | ||
recipients, | ||
localCurrency, | ||
imports, | ||
fiatExchanges, | ||
walletConnect, | ||
tokens: tokenReducer, | ||
dapps: dappsReducer, | ||
fiatConnect: fiatConnectReducer, | ||
swap: swapReducer, | ||
positions: positionsReducer, | ||
keylessBackup: keylessBackupReducer, | ||
nfts: nftsReducer, | ||
priceHistory: priceHistoryReducer, | ||
jumpstart: jumpstartReducer, | ||
points: pointsReducer, | ||
earn: earnReducer, | ||
} as const |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,9 @@ | ||
import Ajv from 'ajv' | ||
import { spawn, takeEvery } from 'redux-saga/effects' | ||
import { ApiReducersKeys, apiReducersList } from 'src/redux/apiReducersList' | ||
import * as createMigrateModule from 'src/redux/createMigrate' | ||
import { migrations } from 'src/redux/migrations' | ||
import { RootState } from 'src/redux/reducers' | ||
import { rootSaga } from 'src/redux/sagas' | ||
import { _persistConfig, setupStore } from 'src/redux/store' | ||
import * as accountCheckerModule from 'src/utils/accountChecker' | ||
|
@@ -25,6 +27,16 @@ const resetStateOnInvalidStoredAccount = jest.spyOn( | |
|
||
const loggerErrorSpy = jest.spyOn(Logger, 'error') | ||
|
||
function getNonApiReducers<R = Omit<RootState, ApiReducersKeys>>(state: RootState): R { | ||
const apiReducersKeys: string[] = Object.keys(apiReducersList) | ||
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) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @jeanregisser there are also a few I might be overthinking this so please let me know if that's the case. I've tried to make all the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok, I think we should optimize for readability first, and avoid There was a problem hiding this comment. Choose a reason for hiding this commentThe 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! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @jeanregisser refactored to for loop |
||
} | ||
|
||
beforeEach(() => { | ||
jest.clearAllMocks() | ||
// For some reason createMigrate.mockRestore doesn't work, so instead we manually reset it to the original implementation | ||
|
@@ -81,9 +93,9 @@ describe('store state', () => { | |
}) | ||
}) | ||
|
||
const data = store.getState() | ||
const data = getNonApiReducers(store.getState()) | ||
|
||
const ajv = new Ajv({ allErrors: true }) | ||
const ajv = new Ajv({ allErrors: true, allowUnionTypes: true }) | ||
const schema = require('test/RootStateSchema.json') | ||
const validate = ajv.compile(schema) | ||
const isValid = validate(data) | ||
|
@@ -98,7 +110,7 @@ describe('store state', () => { | |
{ | ||
"_persist": { | ||
"rehydrated": true, | ||
"version": 232, | ||
"version": 233, | ||
}, | ||
"account": { | ||
"acceptedTerms": false, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
import { createApi } from '@reduxjs/toolkit/query/react' | ||
import { baseQuery } from 'src/redux/api' | ||
import type { TokenTransaction } from 'src/transactions/types' | ||
|
||
type TransactionFeedV2Response = { | ||
transactions: TokenTransaction[] | ||
pageInfo: { | ||
hasNextPage: boolean | ||
} | ||
} | ||
|
||
export const transactionFeedV2Api = createApi({ | ||
reducerPath: 'transactionFeedV2Api', | ||
baseQuery, | ||
endpoints: (builder) => ({ | ||
transactionFeedV2: builder.query< | ||
TransactionFeedV2Response, | ||
{ address: string; endCursor: number } | ||
>({ | ||
query: ({ address, endCursor }) => `/wallet/${address}/transactions?endCursor=${endCursor}`, | ||
}), | ||
}), | ||
}) |
Original file line number | Diff line number | Diff line change | ||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,56 @@ | ||||||||||||||||
import type { EnhancedStore, Middleware, Reducer, UnknownAction } from '@reduxjs/toolkit' | ||||||||||||||||
import { combineReducers, configureStore } from '@reduxjs/toolkit' | ||||||||||||||||
import { ApiReducersKeys } from 'src/redux/apiReducersList' | ||||||||||||||||
import { RootState } from 'src/redux/reducers' | ||||||||||||||||
import { getMockStoreData, RecursivePartial } from 'test/utils' | ||||||||||||||||
|
||||||||||||||||
/** | ||||||||||||||||
* This function is taken from the Redux team. It creates a testable store that is compatible with RTK-Query. | ||||||||||||||||
* It is slightly modified to also include the preloaded state. | ||||||||||||||||
* https://github.com/reduxjs/redux-toolkit/blob/e7540a5594b0d880037f2ff41a83a32c629d3117/packages/toolkit/src/tests/utils/helpers.tsx#L186 | ||||||||||||||||
* | ||||||||||||||||
* For more info on why this is needed and how it works - here's an article that answers some of the questions: | ||||||||||||||||
* https://medium.com/@johnmcdowell0801/testing-rtk-query-with-jest-cdfa5aaf3dc1 | ||||||||||||||||
*/ | ||||||||||||||||
export function setupApiStore< | ||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could we use/extend our existing mockStore instead? Lines 76 to 82 in 1aa94d6
So it's not specific to this reducer. This way we'll respect one of the Redux testing principle:
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. 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 There was a problem hiding this comment. Choose a reason for hiding this commentThe 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! |
||||||||||||||||
A extends { | ||||||||||||||||
reducer: Reducer<any, any> | ||||||||||||||||
reducerPath: string | ||||||||||||||||
middleware: Middleware | ||||||||||||||||
util: { resetApiState(): any } | ||||||||||||||||
}, | ||||||||||||||||
Preloaded extends RecursivePartial<Omit<RootState, ApiReducersKeys>>, | ||||||||||||||||
R extends Record<string, Reducer<any, any>> = Record<never, never>, | ||||||||||||||||
>(api: A, preloadedState: Preloaded, extraReducers?: R) { | ||||||||||||||||
const getStore = () => | ||||||||||||||||
configureStore({ | ||||||||||||||||
preloadedState: getMockStoreData(preloadedState), | ||||||||||||||||
reducer: combineReducers({ | ||||||||||||||||
[api.reducerPath]: api.reducer, | ||||||||||||||||
...extraReducers, | ||||||||||||||||
}), | ||||||||||||||||
middleware: (getDefaultMiddleware) => { | ||||||||||||||||
return getDefaultMiddleware({ | ||||||||||||||||
serializableCheck: false, | ||||||||||||||||
immutableCheck: false, | ||||||||||||||||
}).concat(api.middleware) | ||||||||||||||||
}, | ||||||||||||||||
}) | ||||||||||||||||
|
||||||||||||||||
type StoreType = EnhancedStore< | ||||||||||||||||
{ | ||||||||||||||||
api: ReturnType<A['reducer']> | ||||||||||||||||
} & { | ||||||||||||||||
[K in keyof R]: ReturnType<R[K]> | ||||||||||||||||
}, | ||||||||||||||||
UnknownAction, | ||||||||||||||||
ReturnType<typeof getStore> extends EnhancedStore<any, any, infer M> ? M : never | ||||||||||||||||
> | ||||||||||||||||
|
||||||||||||||||
const initialStore = getStore() as StoreType | ||||||||||||||||
const refObj = { api, store: initialStore } | ||||||||||||||||
const store = getStore() as StoreType | ||||||||||||||||
refObj.store = store | ||||||||||||||||
|
||||||||||||||||
return refObj | ||||||||||||||||
} |
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'll use it in a follow up PR?
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.
@jeanregisser yes, already removed it in the next PR! Only added here to fix the knip error.