UNPKG

@redux-devtools/rtk-query-monitor

Version:
288 lines (254 loc) 8.24 kB
import { Action, createSelector, Selector } from '@reduxjs/toolkit'; import { RtkQueryInspectorProps } from './containers/RtkQueryInspector'; import { ApiStats, QueryInfo, RtkQueryApiState, RtkQueryTag, SelectorsSource, RtkQueryProvidedTagsState, QueryPreviewTabs, RtkResourceInfo, RtkQuery262ProvidedState, } from './types'; import { Comparator, queryComparators } from './utils/comparators'; import { FilterList, queryListFilters } from './utils/filters'; import { emptyRecord } from './utils/object'; import { escapeRegExpSpecialCharacter } from './utils/regexp'; import { getApiStatesOf, extractAllApiQueries, flipComparator, getQueryTagsOf, generateApiStatsOfCurrentQuery, getActionsOfCurrentQuery, extractAllApiMutations, } from './utils/rtk-query'; type InspectorSelector<S, Output> = Selector<SelectorsSource<S>, Output>; export function computeSelectorSource<S, A extends Action<string>>( props: RtkQueryInspectorProps<S, A>, previous: SelectorsSource<S> | null = null, ): SelectorsSource<S> { const { computedStates, currentStateIndex, monitorState, actionsById } = props; const userState = computedStates.length > 0 ? computedStates[currentStateIndex].state : null; if ( !previous || previous.userState !== userState || previous.monitorState !== monitorState || previous.actionsById !== actionsById ) { return { userState, monitorState, currentStateIndex, actionsById, }; } return previous; } export interface InspectorSelectors<S> { readonly selectQueryComparator: InspectorSelector<S, Comparator<QueryInfo>>; readonly selectApiStates: InspectorSelector< S, ReturnType<typeof getApiStatesOf> >; readonly selectAllQueries: InspectorSelector< S, ReturnType<typeof extractAllApiQueries> >; readonly selectAllVisbileQueries: InspectorSelector<S, RtkResourceInfo[]>; readonly selectCurrentQueryInfo: InspectorSelector<S, RtkResourceInfo | null>; readonly selectSearchQueryRegex: InspectorSelector<S, RegExp | null>; readonly selectCurrentQueryTags: InspectorSelector<S, RtkQueryTag[]>; readonly selectApiStatsOfCurrentQuery: InspectorSelector<S, ApiStats | null>; readonly selectApiOfCurrentQuery: InspectorSelector< S, RtkQueryApiState | null >; readonly selectTabCounters: InspectorSelector< S, Record<QueryPreviewTabs, number> >; readonly selectSubscriptionsOfCurrentQuery: InspectorSelector< S, RtkQueryApiState['subscriptions'][string] >; readonly selectActionsOfCurrentQuery: InspectorSelector< S, ReturnType<typeof getActionsOfCurrentQuery> >; } export function createInspectorSelectors<S>(): InspectorSelectors<S> { const selectQueryComparator = ({ monitorState, }: SelectorsSource<S>): Comparator<RtkResourceInfo> => { return queryComparators[monitorState.queryForm.values.queryComparator]; }; const selectQueryListFilter = ({ monitorState, }: SelectorsSource<S>): FilterList<RtkResourceInfo> => { return queryListFilters[monitorState.queryForm.values.queryFilter]; }; const selectActionsById = ({ actionsById }: SelectorsSource<S>) => actionsById; const selectApiStates = createSelector( ({ userState }: SelectorsSource<S>) => userState, getApiStatesOf, ); const selectAllQueries = createSelector( selectApiStates, extractAllApiQueries, ); const selectAllMutations = createSelector( selectApiStates, extractAllApiMutations, ); const selectSearchQueryRegex = createSelector( ({ monitorState }: SelectorsSource<S>) => monitorState.queryForm.values.searchValue, ({ monitorState }: SelectorsSource<S>) => monitorState.queryForm.values.isRegexSearch, (searchValue, isRegexSearch) => { if (searchValue) { try { const regexPattern = isRegexSearch ? searchValue : escapeRegExpSpecialCharacter(searchValue); return new RegExp(regexPattern, 'i'); } catch (err) { // We notify that the search regex provided is not valid } } return null; }, ); const selectComparatorOrder = ({ monitorState }: SelectorsSource<S>) => monitorState.queryForm.values.isAscendingQueryComparatorOrder; const selectAllVisbileQueries = createSelector( [ selectQueryComparator, selectQueryListFilter, selectAllQueries, selectAllMutations, selectComparatorOrder, selectSearchQueryRegex, ], ( comparator, queryListFilter, queryList, mutationsList, isAscending, searchRegex, ) => { const filteredList = queryListFilter( searchRegex, (queryList as RtkResourceInfo[]).concat(mutationsList), ); const computedComparator = isAscending ? comparator : flipComparator(comparator); return filteredList.slice().sort(computedComparator); }, ); const selectCurrentQueryInfo = createSelector( selectAllQueries, selectAllMutations, ({ monitorState }: SelectorsSource<S>) => monitorState.selectedQueryKey, (allQueries, allMutations, selectedQueryKey) => { if (!selectedQueryKey) { return null; } let currentQueryInfo: null | RtkResourceInfo = allQueries.find( (query) => query.queryKey === selectedQueryKey.queryKey && selectedQueryKey.reducerPath === query.reducerPath, ) || null; if (!currentQueryInfo) { currentQueryInfo = allMutations.find( (mutation) => mutation.queryKey === selectedQueryKey.queryKey && selectedQueryKey.reducerPath === mutation.reducerPath, ) || null; } return currentQueryInfo; }, ); const selectApiOfCurrentQuery: InspectorSelector< S, null | RtkQueryApiState > = (selectorsSource: SelectorsSource<S>) => { const apiStates = selectApiStates(selectorsSource); const currentQueryInfo = selectCurrentQueryInfo(selectorsSource); if (!apiStates || !currentQueryInfo) { return null; } return apiStates[currentQueryInfo.reducerPath] ?? null; }; const selectProvidedOfCurrentQuery: InspectorSelector< S, null | RtkQueryProvidedTagsState | RtkQuery262ProvidedState > = (selectorsSource: SelectorsSource<S>) => { return selectApiOfCurrentQuery(selectorsSource)?.provided ?? null; }; const selectSubscriptionsOfCurrentQuery = createSelector( [selectApiOfCurrentQuery, selectCurrentQueryInfo], (apiState, queryInfo) => { if (!queryInfo || !apiState) { return emptyRecord; } return apiState.subscriptions[queryInfo.queryKey]; }, ); const selectCurrentQueryTags = createSelector( [selectCurrentQueryInfo, selectProvidedOfCurrentQuery], getQueryTagsOf, ); const selectApiStatsOfCurrentQuery = createSelector( selectApiOfCurrentQuery, (selectorsSource: SelectorsSource<S>) => selectorsSource.actionsById, (selectorsSource: SelectorsSource<S>) => selectorsSource.currentStateIndex, generateApiStatsOfCurrentQuery, ); const selectActionsOfCurrentQuery = createSelector( selectCurrentQueryInfo, selectActionsById, getActionsOfCurrentQuery, ); const selectTabCounters = createSelector( [ selectSubscriptionsOfCurrentQuery, selectActionsOfCurrentQuery, selectCurrentQueryTags, ], (subscriptions, actions, tags) => { return { [QueryPreviewTabs.data]: 0, [QueryPreviewTabs.queryTags]: tags.length, [QueryPreviewTabs.querySubscriptions]: Object.keys(subscriptions ?? {}) .length, [QueryPreviewTabs.apiConfig]: 0, [QueryPreviewTabs.queryinfo]: 0, [QueryPreviewTabs.actions]: actions.length, }; }, ); return { selectQueryComparator, selectApiStates, selectAllQueries, selectAllVisbileQueries, selectSearchQueryRegex, selectCurrentQueryInfo, selectCurrentQueryTags, selectApiStatsOfCurrentQuery, selectSubscriptionsOfCurrentQuery, selectApiOfCurrentQuery, selectTabCounters, selectActionsOfCurrentQuery, }; }