feat(rtk-query): add timings to apiStats sections

This commit is contained in:
FaberVitale 2021-06-22 17:17:57 +02:00
parent 136fd02ff8
commit c3b6887e82
4 changed files with 119 additions and 21 deletions

View File

@ -122,7 +122,8 @@ class RtkQueryInspector<S, A extends Action<unknown>> extends PureComponent<
const currentQueryInfo =
this.selectors.selectCurrentQueryInfo(selectorsSource);
const currentRtkApi = getApiStateOf(currentQueryInfo, apiStates);
const currentRtkApi =
this.selectors.selectApiOfCurrentQuery(selectorsSource);
const currentQuerySubscriptions = getQuerySubscriptionsOf(
currentQueryInfo,
apiStates

View File

@ -1,6 +1,13 @@
import { Action, createSelector, Selector } from '@reduxjs/toolkit';
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 { FilterList, queryListFilters } from './utils/filters';
import { escapeRegExpSpecialCharacter } from './utils/regexp';
@ -52,6 +59,10 @@ export interface InspectorSelectors<S> {
readonly selectSearchQueryRegex: InspectorSelector<S, RegExp | null>;
readonly selectCurrentQueryTags: InspectorSelector<S, RtkQueryTag[]>;
readonly selectApiStatsOfCurrentQuery: InspectorSelector<S, ApiStats | null>;
readonly selectApiOfCurrentQuery: InspectorSelector<
S,
RtkQueryApiState | null
>;
}
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(
selectApiStates,
selectCurrentQueryInfo,
(apiState, currentQueryInfo) => getQueryTagsOf(currentQueryInfo, apiState)
[selectCurrentQueryInfo, selectProvidedOfCurrentQuery],
getQueryTagsOf
);
const selectApiStatsOfCurrentQuery = createSelector(
selectApiStates,
selectCurrentQueryInfo,
(apiState, currentQueryInfo) =>
generateApiStatsOfCurrentQuery(currentQueryInfo, apiState)
selectApiOfCurrentQuery,
generateApiStatsOfCurrentQuery
);
return {
@ -153,5 +180,6 @@ export function createInspectorSelectors<S>(): InspectorSelectors<S> {
selectCurrentQueryInfo,
selectCurrentQueryTags,
selectApiStatsOfCurrentQuery,
selectApiOfCurrentQuery,
};
}

View File

@ -109,13 +109,24 @@ export type QueryTally = {
} &
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 {
readonly tally: {
readonly timings: ApiTimings;
readonly tally: Readonly<{
subscriptions: Tally;
queries: QueryTally;
tagTypes: Tally;
mutations: QueryTally;
};
}>;
}
export interface QueryPreviewTabProps extends StyleUtils {

View File

@ -11,6 +11,9 @@ import {
MutationInfo,
ApiStats,
QueryTally,
RtkQueryProvided,
ApiTimings,
QueryTimings,
} from '../types';
import { missingTagId } from '../monitor-config';
import { Comparator } from './comparators';
@ -192,18 +195,75 @@ function tallySubscriptions(
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(
queryInfo: QueryInfo | null,
apiStates: ReturnType<typeof getApiStatesOf>
api: RtkQueryApiState | null
): ApiStats | null {
if (!apiStates || !queryInfo) {
if (!api) {
return null;
}
const { reducerPath } = queryInfo;
const api = apiStates[reducerPath];
return {
timings: computeApiTimings(api),
tally: {
subscriptions: tallySubscriptions(api.subscriptions),
queries: computeQueryTallyOf(api.queries),
@ -268,14 +328,12 @@ export function getProvidedOf(
export function getQueryTagsOf(
queryInfo: QueryInfo | null,
apiStates: ReturnType<typeof getApiStatesOf>
provided: RtkQueryProvided | null
): RtkQueryTag[] {
if (!apiStates || !queryInfo) {
if (!queryInfo || !provided) {
return emptyArray;
}
const provided = apiStates[queryInfo.reducerPath].provided;
const tagTypes = Object.keys(provided);
if (tagTypes.length < 1) {