mirror of
https://github.com/thatmattlove/hyperglass.git
synced 2026-01-17 08:48:05 +00:00
fixes #267: fix response caching
This commit is contained in:
parent
3b0abd5ba8
commit
b617df24d1
8 changed files with 39 additions and 28 deletions
|
|
@ -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")
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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' }}
|
||||||
>
|
>
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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] });
|
||||||
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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).
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue