mirror of
https://github.com/reduxjs/redux-devtools.git
synced 2025-07-27 00:19:55 +03:00
feat(rtk-query): add timings to apiStats sections
This commit is contained in:
parent
136fd02ff8
commit
c3b6887e82
|
@ -122,7 +122,8 @@ class RtkQueryInspector<S, A extends Action<unknown>> extends PureComponent<
|
||||||
const currentQueryInfo =
|
const currentQueryInfo =
|
||||||
this.selectors.selectCurrentQueryInfo(selectorsSource);
|
this.selectors.selectCurrentQueryInfo(selectorsSource);
|
||||||
|
|
||||||
const currentRtkApi = getApiStateOf(currentQueryInfo, apiStates);
|
const currentRtkApi =
|
||||||
|
this.selectors.selectApiOfCurrentQuery(selectorsSource);
|
||||||
const currentQuerySubscriptions = getQuerySubscriptionsOf(
|
const currentQuerySubscriptions = getQuerySubscriptionsOf(
|
||||||
currentQueryInfo,
|
currentQueryInfo,
|
||||||
apiStates
|
apiStates
|
||||||
|
|
|
@ -1,6 +1,13 @@
|
||||||
import { Action, createSelector, Selector } from '@reduxjs/toolkit';
|
import { Action, createSelector, Selector } from '@reduxjs/toolkit';
|
||||||
import { RtkQueryInspectorProps } from './containers/RtkQueryInspector';
|
import { RtkQueryInspectorProps } from './containers/RtkQueryInspector';
|
||||||
import { ApiStats, QueryInfo, RtkQueryTag, SelectorsSource } from './types';
|
import {
|
||||||
|
ApiStats,
|
||||||
|
QueryInfo,
|
||||||
|
RtkQueryApiState,
|
||||||
|
RtkQueryTag,
|
||||||
|
SelectorsSource,
|
||||||
|
RtkQueryProvided,
|
||||||
|
} from './types';
|
||||||
import { Comparator, queryComparators } from './utils/comparators';
|
import { Comparator, queryComparators } from './utils/comparators';
|
||||||
import { FilterList, queryListFilters } from './utils/filters';
|
import { FilterList, queryListFilters } from './utils/filters';
|
||||||
import { escapeRegExpSpecialCharacter } from './utils/regexp';
|
import { escapeRegExpSpecialCharacter } from './utils/regexp';
|
||||||
|
@ -52,6 +59,10 @@ export interface InspectorSelectors<S> {
|
||||||
readonly selectSearchQueryRegex: InspectorSelector<S, RegExp | null>;
|
readonly selectSearchQueryRegex: InspectorSelector<S, RegExp | null>;
|
||||||
readonly selectCurrentQueryTags: InspectorSelector<S, RtkQueryTag[]>;
|
readonly selectCurrentQueryTags: InspectorSelector<S, RtkQueryTag[]>;
|
||||||
readonly selectApiStatsOfCurrentQuery: InspectorSelector<S, ApiStats | null>;
|
readonly selectApiStatsOfCurrentQuery: InspectorSelector<S, ApiStats | null>;
|
||||||
|
readonly selectApiOfCurrentQuery: InspectorSelector<
|
||||||
|
S,
|
||||||
|
RtkQueryApiState | null
|
||||||
|
>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createInspectorSelectors<S>(): InspectorSelectors<S> {
|
export function createInspectorSelectors<S>(): InspectorSelectors<S> {
|
||||||
|
@ -131,17 +142,33 @@ export function createInspectorSelectors<S>(): InspectorSelectors<S> {
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
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 | RtkQueryProvided
|
||||||
|
> = (selectorsSource: SelectorsSource<S>) => {
|
||||||
|
return selectApiOfCurrentQuery(selectorsSource)?.provided ?? null;
|
||||||
|
};
|
||||||
|
|
||||||
const selectCurrentQueryTags = createSelector(
|
const selectCurrentQueryTags = createSelector(
|
||||||
selectApiStates,
|
[selectCurrentQueryInfo, selectProvidedOfCurrentQuery],
|
||||||
selectCurrentQueryInfo,
|
getQueryTagsOf
|
||||||
(apiState, currentQueryInfo) => getQueryTagsOf(currentQueryInfo, apiState)
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const selectApiStatsOfCurrentQuery = createSelector(
|
const selectApiStatsOfCurrentQuery = createSelector(
|
||||||
selectApiStates,
|
selectApiOfCurrentQuery,
|
||||||
selectCurrentQueryInfo,
|
generateApiStatsOfCurrentQuery
|
||||||
(apiState, currentQueryInfo) =>
|
|
||||||
generateApiStatsOfCurrentQuery(currentQueryInfo, apiState)
|
|
||||||
);
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -153,5 +180,6 @@ export function createInspectorSelectors<S>(): InspectorSelectors<S> {
|
||||||
selectCurrentQueryInfo,
|
selectCurrentQueryInfo,
|
||||||
selectCurrentQueryTags,
|
selectCurrentQueryTags,
|
||||||
selectApiStatsOfCurrentQuery,
|
selectApiStatsOfCurrentQuery,
|
||||||
|
selectApiOfCurrentQuery,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -109,13 +109,24 @@ export type QueryTally = {
|
||||||
} &
|
} &
|
||||||
Tally;
|
Tally;
|
||||||
|
|
||||||
|
export interface QueryTimings {
|
||||||
|
readonly oldestFetch: { key: string; at: string } | null;
|
||||||
|
readonly latestFetch: { key: string; at: string } | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ApiTimings {
|
||||||
|
readonly queries: QueryTimings;
|
||||||
|
readonly mutations: QueryTimings;
|
||||||
|
}
|
||||||
|
|
||||||
export interface ApiStats {
|
export interface ApiStats {
|
||||||
readonly tally: {
|
readonly timings: ApiTimings;
|
||||||
|
readonly tally: Readonly<{
|
||||||
subscriptions: Tally;
|
subscriptions: Tally;
|
||||||
queries: QueryTally;
|
queries: QueryTally;
|
||||||
tagTypes: Tally;
|
tagTypes: Tally;
|
||||||
mutations: QueryTally;
|
mutations: QueryTally;
|
||||||
};
|
}>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface QueryPreviewTabProps extends StyleUtils {
|
export interface QueryPreviewTabProps extends StyleUtils {
|
||||||
|
|
|
@ -11,6 +11,9 @@ import {
|
||||||
MutationInfo,
|
MutationInfo,
|
||||||
ApiStats,
|
ApiStats,
|
||||||
QueryTally,
|
QueryTally,
|
||||||
|
RtkQueryProvided,
|
||||||
|
ApiTimings,
|
||||||
|
QueryTimings,
|
||||||
} from '../types';
|
} from '../types';
|
||||||
import { missingTagId } from '../monitor-config';
|
import { missingTagId } from '../monitor-config';
|
||||||
import { Comparator } from './comparators';
|
import { Comparator } from './comparators';
|
||||||
|
@ -192,18 +195,75 @@ function tallySubscriptions(
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function computeQueryApiTimings(
|
||||||
|
queriesOrMutations:
|
||||||
|
| RtkQueryApiState['queries']
|
||||||
|
| RtkQueryApiState['mutations']
|
||||||
|
): QueryTimings {
|
||||||
|
let latestFetch = null;
|
||||||
|
let latestFetchedQueryKey: string | null = null;
|
||||||
|
let latestFetchedQueryTiming = -1;
|
||||||
|
let oldestFetch = null;
|
||||||
|
let oldestFetchedQueryKey: string | null = null;
|
||||||
|
let oldestFetchedQueryTiming = Number.MAX_SAFE_INTEGER;
|
||||||
|
|
||||||
|
const queryKeys = Object.keys(queriesOrMutations);
|
||||||
|
|
||||||
|
for (let i = 0, len = queryKeys.length; i < len; i++) {
|
||||||
|
const queryKey = queryKeys[i];
|
||||||
|
const query = queriesOrMutations[queryKey];
|
||||||
|
|
||||||
|
const fulfilledTimeStamp = query?.fulfilledTimeStamp;
|
||||||
|
|
||||||
|
if (typeof fulfilledTimeStamp === 'number') {
|
||||||
|
if (fulfilledTimeStamp > latestFetchedQueryTiming) {
|
||||||
|
latestFetchedQueryKey = queryKey;
|
||||||
|
latestFetchedQueryTiming = fulfilledTimeStamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fulfilledTimeStamp < oldestFetchedQueryTiming) {
|
||||||
|
oldestFetchedQueryKey = queryKey;
|
||||||
|
oldestFetchedQueryTiming = fulfilledTimeStamp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (latestFetchedQueryKey) {
|
||||||
|
latestFetch = {
|
||||||
|
key: latestFetchedQueryKey,
|
||||||
|
at: new Date(latestFetchedQueryTiming).toISOString(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (oldestFetchedQueryKey) {
|
||||||
|
oldestFetch = {
|
||||||
|
key: oldestFetchedQueryKey,
|
||||||
|
at: new Date(oldestFetchedQueryTiming).toISOString(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
latestFetch,
|
||||||
|
oldestFetch,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function computeApiTimings(api: RtkQueryApiState): ApiTimings {
|
||||||
|
return {
|
||||||
|
queries: computeQueryApiTimings(api.queries),
|
||||||
|
mutations: computeQueryApiTimings(api.mutations),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export function generateApiStatsOfCurrentQuery(
|
export function generateApiStatsOfCurrentQuery(
|
||||||
queryInfo: QueryInfo | null,
|
api: RtkQueryApiState | null
|
||||||
apiStates: ReturnType<typeof getApiStatesOf>
|
|
||||||
): ApiStats | null {
|
): ApiStats | null {
|
||||||
if (!apiStates || !queryInfo) {
|
if (!api) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { reducerPath } = queryInfo;
|
|
||||||
const api = apiStates[reducerPath];
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
timings: computeApiTimings(api),
|
||||||
tally: {
|
tally: {
|
||||||
subscriptions: tallySubscriptions(api.subscriptions),
|
subscriptions: tallySubscriptions(api.subscriptions),
|
||||||
queries: computeQueryTallyOf(api.queries),
|
queries: computeQueryTallyOf(api.queries),
|
||||||
|
@ -268,14 +328,12 @@ export function getProvidedOf(
|
||||||
|
|
||||||
export function getQueryTagsOf(
|
export function getQueryTagsOf(
|
||||||
queryInfo: QueryInfo | null,
|
queryInfo: QueryInfo | null,
|
||||||
apiStates: ReturnType<typeof getApiStatesOf>
|
provided: RtkQueryProvided | null
|
||||||
): RtkQueryTag[] {
|
): RtkQueryTag[] {
|
||||||
if (!apiStates || !queryInfo) {
|
if (!queryInfo || !provided) {
|
||||||
return emptyArray;
|
return emptyArray;
|
||||||
}
|
}
|
||||||
|
|
||||||
const provided = apiStates[queryInfo.reducerPath].provided;
|
|
||||||
|
|
||||||
const tagTypes = Object.keys(provided);
|
const tagTypes = Object.keys(provided);
|
||||||
|
|
||||||
if (tagTypes.length < 1) {
|
if (tagTypes.length < 1) {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user