fixes #267: fix response caching

This commit is contained in:
thatmattlove 2024-06-30 22:22:44 -04:00
parent 3b0abd5ba8
commit b617df24d1
8 changed files with 39 additions and 28 deletions

View file

@ -1,6 +1,7 @@
"""API Routes.""" """API Routes."""
# Standard Library # Standard Library
import json
import time import time
import typing as t import typing as t
from datetime import UTC, datetime from datetime import UTC, datetime
@ -119,7 +120,10 @@ async def query(_state: HyperglassState, request: Request, data: Query) -> Query
json_output = is_type(output, OutputDataModel) json_output = is_type(output, OutputDataModel)
if json_output: if json_output:
raw_output = output.export_dict() # Export structured output as JSON string to guarantee value
# is serializable, then convert it back to a dict.
as_json = output.export_json()
raw_output = json.loads(as_json)
else: else:
raw_output = str(output) raw_output = str(output)
@ -138,8 +142,6 @@ async def query(_state: HyperglassState, request: Request, data: Query) -> Query
response_format = "text/plain" response_format = "text/plain"
if json_output: if json_output:
if cache_response.get("level") != "success":
cache.delete(cache_key)
response_format = "application/json" response_format = "application/json"
_log.info("Execution completed") _log.info("Execution completed")

View file

@ -144,7 +144,7 @@ export const Title = (props: FlexProps): JSX.Element => {
variant="link" variant="link"
flexWrap="wrap" flexWrap="wrap"
flexDir="column" flexDir="column"
onClick={() => reset()} onClick={async () => await reset()}
_focus={{ boxShadow: 'none' }} _focus={{ boxShadow: 'none' }}
_hover={{ textDecoration: 'none' }} _hover={{ textDecoration: 'none' }}
> >

View file

@ -1,8 +1,8 @@
import { useCallback, useRef } from 'react';
import { Flex } from '@chakra-ui/react'; import { Flex } from '@chakra-ui/react';
import { useCallback, useRef } from 'react';
import { isSafari } from 'react-device-detect'; import { isSafari } from 'react-device-detect';
import { If, Then } from 'react-if'; import { If, Then } from 'react-if';
import { Debugger, Greeting, Footer, Header, ResetButton } from '~/components'; import { Debugger, Footer, Greeting, Header, ResetButton } from '~/components';
import { useConfig } from '~/context'; import { useConfig } from '~/context';
import { motionChakra } from '~/elements'; import { motionChakra } from '~/elements';
import { useFormState } from '~/hooks'; import { useFormState } from '~/hooks';
@ -31,7 +31,7 @@ export const Layout = (props: FlexProps): JSX.Element => {
const containerRef = useRef<HTMLDivElement>({} as HTMLDivElement); const containerRef = useRef<HTMLDivElement>({} as HTMLDivElement);
function handleReset(): void { async function handleReset() {
containerRef.current.scrollIntoView({ behavior: 'smooth', block: 'start' }); containerRef.current.scrollIntoView({ behavior: 'smooth', block: 'start' });
setStatus('form'); setStatus('form');
reset(); reset();

View file

@ -40,7 +40,9 @@ export const Path = (props: PathProps): JSX.Element => {
<ModalHeader>{`Path to ${displayTarget}`}</ModalHeader> <ModalHeader>{`Path to ${displayTarget}`}</ModalHeader>
<ModalCloseButton /> <ModalCloseButton />
<ModalBody> <ModalBody>
{response !== null ? <Chart data={output} /> : <Skeleton w="500px" h="300px" />} <Skeleton isLoaded={response != null}>
<Chart data={output} />
</Skeleton>
</ModalBody> </ModalBody>
</ModalContent> </ModalContent>
</Modal> </Modal>

View file

@ -1,23 +1,23 @@
import { forwardRef } from 'react';
import { import {
Modal,
Popover,
ModalBody,
IconButton, IconButton,
PopoverBody, Modal,
ModalOverlay, ModalBody,
ModalContent,
PopoverArrow,
PopoverTrigger,
PopoverContent,
ModalCloseButton, ModalCloseButton,
ModalContent,
ModalOverlay,
Popover,
PopoverArrow,
PopoverBody,
PopoverCloseButton, PopoverCloseButton,
PopoverContent,
PopoverTrigger,
} from '@chakra-ui/react'; } from '@chakra-ui/react';
import { forwardRef } from 'react';
import { useFormContext } from 'react-hook-form'; import { useFormContext } from 'react-hook-form';
import { If, Then, Else } from 'react-if'; import { Else, If, Then } from 'react-if';
import { ResolvedTarget } from '~/components'; import { ResolvedTarget } from '~/components';
import { DynamicIcon } from '~/elements'; import { DynamicIcon } from '~/elements';
import { useFormState, useMobile, useColorValue } from '~/hooks'; import { useColorValue, useFormState, useMobile } from '~/hooks';
import type { IconButtonProps } from '@chakra-ui/react'; import type { IconButtonProps } from '@chakra-ui/react';
@ -114,7 +114,7 @@ export const SubmitButton = (props: SubmitButtonProps): JSX.Element => {
const { reset } = useFormContext(); const { reset } = useFormContext();
function handleClose(): void { async function handleClose() {
reset(); reset();
resetForm(); resetForm();
resolvedClose(); resolvedClose();

View file

@ -1,6 +1,6 @@
import { createContext, useContext, useMemo } from 'react';
import { ChakraProvider, localStorageManager } from '@chakra-ui/react'; import { ChakraProvider, localStorageManager } from '@chakra-ui/react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { createContext, useContext, useMemo } from 'react';
import { makeTheme } from '~/util'; import { makeTheme } from '~/util';
import type { Config } from '~/types'; import type { Config } from '~/types';
@ -12,7 +12,7 @@ interface HyperglassProviderProps {
export const HyperglassContext = createContext<Config>({} as Config); export const HyperglassContext = createContext<Config>({} as Config);
const queryClient = new QueryClient(); export const queryClient = new QueryClient();
export const HyperglassProvider = (props: HyperglassProviderProps): JSX.Element => { export const HyperglassProvider = (props: HyperglassProviderProps): JSX.Element => {
const { config, children } = props; const { config, children } = props;

View file

@ -3,6 +3,7 @@ import plur from 'plur';
import { useMemo } from 'react'; import { useMemo } from 'react';
import isEqual from 'react-fast-compare'; import isEqual from 'react-fast-compare';
import create from 'zustand'; import create from 'zustand';
import { queryClient } from '~/context';
import { all, andJoin, dedupObjectArray, withDev } from '~/util'; import { all, andJoin, dedupObjectArray, withDev } from '~/util';
import type { UseFormClearErrors, UseFormSetError } from 'react-hook-form'; import type { UseFormClearErrors, UseFormSetError } from 'react-hook-form';
@ -61,10 +62,13 @@ interface FormStateType<Opt extends SingleOption = SingleOption> {
setSelection< setSelection<
Opt extends SingleOption, Opt extends SingleOption,
K extends keyof FormSelections<Opt> = keyof FormSelections<Opt>, K extends keyof FormSelections<Opt> = keyof FormSelections<Opt>,
>(field: K, value: FormSelections[K]): void; >(
field: K,
value: FormSelections[K],
): void;
setTarget(update: Partial<Target>): void; setTarget(update: Partial<Target>): void;
getDirective(): Directive | null; getDirective(): Directive | null;
reset(): void; reset(): Promise<void>;
setFormValue<K extends keyof FormValues>(field: K, value: FormValues[K]): void; setFormValue<K extends keyof FormValues>(field: K, value: FormValues[K]): void;
locationChange( locationChange(
locations: string[], locations: string[],
@ -198,7 +202,8 @@ const formState: StateCreator<FormStateType> = (set, get) => ({
return null; return null;
}, },
reset(): void { async reset(): Promise<void> {
const { form } = get();
set({ set({
filtered: { types: [], groups: [] }, filtered: { types: [], groups: [] },
form: { queryLocation: [], queryTarget: [], queryType: '' }, form: { queryLocation: [], queryTarget: [], queryType: '' },
@ -209,6 +214,10 @@ const formState: StateCreator<FormStateType> = (set, get) => ({
target: { display: '' }, target: { display: '' },
resolvedIsOpen: false, resolvedIsOpen: false,
}); });
for (const queryLocation of form.queryLocation) {
const query = { queryLocation, queryTarget: form.queryTarget, queryType: form.queryType };
queryClient.removeQueries({ queryKey: ['/api/query', query] });
}
}, },
}); });

View file

@ -72,8 +72,6 @@ export function useLGQuery(
return useQuery<QueryResponse, Response | QueryResponse | Error, QueryResponse, LGQueryKey>({ return useQuery<QueryResponse, Response | QueryResponse | Error, QueryResponse, LGQueryKey>({
queryKey: ['/api/query', query], queryKey: ['/api/query', query],
queryFn: runQuery, queryFn: runQuery,
// Invalidate react-query's cache just shy of the configured cache timeout.
cacheTime: cache.timeout * 1000 * 0.95,
// Don't refetch when window refocuses. // Don't refetch when window refocuses.
refetchOnWindowFocus: false, refetchOnWindowFocus: false,
// Don't automatically refetch query data (queries should be on-off). // Don't automatically refetch query data (queries should be on-off).