updated metric data requests

This commit is contained in:
Nate Kelley 2025-03-10 13:58:50 -06:00
parent b3c4bad371
commit 613d4bcfe6
No known key found for this signature in database
GPG Key ID: FD90372AB8D98B4F
19 changed files with 84 additions and 99 deletions

29
web/package-lock.json generated
View File

@ -88,6 +88,7 @@
"@storybook/react": "^8.6.4", "@storybook/react": "^8.6.4",
"@storybook/test": "^8.6.4", "@storybook/test": "^8.6.4",
"@tailwindcss/postcss": "4.0.12", "@tailwindcss/postcss": "4.0.12",
"@testing-library/react": "^16.2.0",
"@types/canvas-confetti": "^1.9.0", "@types/canvas-confetti": "^1.9.0",
"@types/js-cookie": "^3.0.6", "@types/js-cookie": "^3.0.6",
"@types/lodash": "^4.17.16", "@types/lodash": "^4.17.16",
@ -6745,6 +6746,34 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/@testing-library/react": {
"version": "16.2.0",
"resolved": "https://registry.npmjs.org/@testing-library/react/-/react-16.2.0.tgz",
"integrity": "sha512-2cSskAvA1QNtKc8Y9VJQRv0tm3hLVgxRGDB+KYhIaPQJ1I+RHbhIXcM+zClKXzMes/wshsMVzf4B9vS4IZpqDQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.12.5"
},
"engines": {
"node": ">=18"
},
"peerDependencies": {
"@testing-library/dom": "^10.0.0",
"@types/react": "^18.0.0 || ^19.0.0",
"@types/react-dom": "^18.0.0 || ^19.0.0",
"react": "^18.0.0 || ^19.0.0",
"react-dom": "^18.0.0 || ^19.0.0"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@testing-library/user-event": { "node_modules/@testing-library/user-event": {
"version": "14.5.2", "version": "14.5.2",
"resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-14.5.2.tgz", "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-14.5.2.tgz",

View File

@ -96,6 +96,7 @@
"@storybook/react": "^8.6.4", "@storybook/react": "^8.6.4",
"@storybook/test": "^8.6.4", "@storybook/test": "^8.6.4",
"@tailwindcss/postcss": "4.0.12", "@tailwindcss/postcss": "4.0.12",
"@testing-library/react": "^16.2.0",
"@types/canvas-confetti": "^1.9.0", "@types/canvas-confetti": "^1.9.0",
"@types/js-cookie": "^3.0.6", "@types/js-cookie": "^3.0.6",
"@types/lodash": "^4.17.16", "@types/lodash": "^4.17.16",

View File

@ -2,8 +2,7 @@ import { DataMetadata, IDataResult } from './interfaces';
export type BusterMetricData = { export type BusterMetricData = {
data: IDataResult | null; data: IDataResult | null;
dataFromRerun?: IDataResult; dataFromRerun?: IDataResult; //this is actually only used in the UI. maybe move this to ?
data_metadata: DataMetadata; data_metadata: DataMetadata;
code: string | null;
metricId: string; metricId: string;
}; };

View File

@ -1,7 +1,13 @@
import { useQuery } from '@tanstack/react-query'; import { useQuery } from '@tanstack/react-query';
import { QueryClient } from '@tanstack/react-query'; import { QueryClient } from '@tanstack/react-query';
import { useMemoizedFn } from '@/hooks'; import { useMemoizedFn } from '@/hooks';
import { getMetric, getMetric_server, listMetrics, listMetrics_server } from './requests'; import {
getMetric,
getMetric_server,
getMetricData,
listMetrics,
listMetrics_server
} from './requests';
import type { GetMetricParams, ListMetricsParams } from './interfaces'; import type { GetMetricParams, ListMetricsParams } from './interfaces';
import { upgradeMetricToIMetric } from '@/lib/chat'; import { upgradeMetricToIMetric } from '@/lib/chat';
import { queryKeys } from '@/api/query_keys'; import { queryKeys } from '@/api/query_keys';
@ -61,3 +67,15 @@ export const prefetchGetMetricsList = async (
return queryClient; return queryClient;
}; };
export const useGetMetricData = (params: { id: string }) => {
const queryFn = useMemoizedFn(() => {
return getMetricData(params);
});
return useQuery({
...queryKeys.metricsGetData(params.id),
queryFn,
enabled: !!params.id
});
};

View File

@ -1,7 +1,11 @@
import { mainApi } from '../instances'; import { mainApi } from '../instances';
import { serverFetch } from '@/api/createServerInstance'; import { serverFetch } from '@/api/createServerInstance';
import type { GetMetricParams, ListMetricsParams } from './interfaces'; import type { GetMetricParams, ListMetricsParams } from './interfaces';
import type { BusterMetric, BusterMetricListItem } from '@/api/asset_interfaces/metric'; import type {
BusterMetric,
BusterMetricData,
BusterMetricListItem
} from '@/api/asset_interfaces/metric';
export const getMetric = async ({ id, password }: GetMetricParams) => { export const getMetric = async ({ id, password }: GetMetricParams) => {
return mainApi return mainApi
@ -24,3 +28,7 @@ export const listMetrics = async (params: ListMetricsParams) => {
export const listMetrics_server = async (params: ListMetricsParams) => { export const listMetrics_server = async (params: ListMetricsParams) => {
return await serverFetch<BusterMetricListItem[]>('/metrics/list', { params }); return await serverFetch<BusterMetricListItem[]>('/metrics/list', { params });
}; };
export const getMetricData = async (params: { id: string }) => {
return mainApi.get<BusterMetricData>(`/metrics/data`, { params }).then((res) => res.data);
};

View File

@ -1,10 +0,0 @@
import type { BusterChartConfigProps, DataMetadata, IDataResult } from '../../asset_interfaces';
import type { EventBase } from '../base_interfaces';
export type MetricEvent_fetchingData = {
metric_id: string;
data: IDataResult;
data_metadata: DataMetadata;
chart_config: BusterChartConfigProps;
code: string | null;
} & EventBase;

View File

@ -5,7 +5,6 @@ import {
MetricSubscribeRequest, MetricSubscribeRequest,
MetricUpdateRequest, MetricUpdateRequest,
MetricDeleteRequest, MetricDeleteRequest,
MetricGetDataByMessageIdRequest,
MetricSearchRequest, MetricSearchRequest,
MetricDuplicateRequest MetricDuplicateRequest
} from '@/api/request_interfaces/metrics'; } from '@/api/request_interfaces/metrics';
@ -41,14 +40,6 @@ export type MetricUpdateMetric = BusterSocketRequestBase<'/metrics/update', Metr
*/ */
export type MetricDelete = BusterSocketRequestBase<'/metrics/delete', MetricDeleteRequest>; export type MetricDelete = BusterSocketRequestBase<'/metrics/delete', MetricDeleteRequest>;
/**
* Request payload for retrieving metric data by message ID
*/
export type MetricGetDataByMessageId = BusterSocketRequestBase<
'/metrics/data',
MetricGetDataByMessageIdRequest
>;
/** /**
* Request payload for searching metrics * Request payload for searching metrics
*/ */
@ -69,5 +60,4 @@ export type MetricEmits =
| MetricUpdateMetric | MetricUpdateMetric
| MetricSubscribeToMetric | MetricSubscribeToMetric
| MetricDelete | MetricDelete
| MetricGetDataByMessageId
| MetricSearch; | MetricSearch;

View File

@ -1,5 +1,4 @@
import { BusterMetric, BusterMetricListItem } from '@/api/asset_interfaces/metric'; import { BusterMetric, BusterMetricListItem } from '@/api/asset_interfaces/metric';
import { MetricEvent_fetchingData } from './eventsInterfaces';
export enum MetricResponses { export enum MetricResponses {
'/metrics/list:getMetricList' = '/metrics/list:getMetricList', '/metrics/list:getMetricList' = '/metrics/list:getMetricList',
@ -29,12 +28,6 @@ export type Metric_Unsubscribe = {
onError?: (d: unknown) => void; onError?: (d: unknown) => void;
}; };
export type MetricGet_fetchingData = {
route: '/metrics/get:fetchingData';
callback: (d: MetricEvent_fetchingData) => void;
onError?: (d: unknown) => void;
};
export type MetricList_updateMetricList = { export type MetricList_updateMetricList = {
route: '/metrics/list:updateMetricList'; route: '/metrics/list:updateMetricList';
callback: (d: BusterMetricListItem[]) => void; callback: (d: BusterMetricListItem[]) => void;
@ -61,5 +54,4 @@ export type MetricResponseTypes =
| MetricGet_updateMetricState | MetricGet_updateMetricState
| MetricList_updateMetricList | MetricList_updateMetricList
| MetricDelete_deleteMetricState | MetricDelete_deleteMetricState
| MetricUpdate_updateMetricState | MetricUpdate_updateMetricState;
| MetricGet_fetchingData;

View File

@ -41,9 +41,16 @@ export const metricsGetDataByMessageId = (messageId: string) =>
staleTime: 60 * 60 * 1000 // 1 hour staleTime: 60 * 60 * 1000 // 1 hour
}); });
export const metricsGetData = (id: string) =>
queryOptions<BusterMetricData>({
queryKey: ['metrics', 'data', id] as const,
staleTime: 3 * 60 * 60 * 1000 // 3 hours,
});
export const metricsQueryKeys = { export const metricsQueryKeys = {
metricsGetMetric, metricsGetMetric,
useMetricsGetMetric, useMetricsGetMetric,
metricsGetList, metricsGetList,
metricsGetDataByMessageId metricsGetDataByMessageId,
metricsGetData
}; };

View File

@ -70,14 +70,6 @@ export type MetricDeleteRequest = {
ids: string[]; ids: string[];
}; };
/**
* Request payload for retrieving metric data by message ID
*/
export type MetricGetDataByMessageIdRequest = {
/** Message ID to retrieve metric data for */
id: string;
};
/** /**
* Request payload for searching metrics * Request payload for searching metrics
*/ */

View File

@ -1,4 +1,4 @@
import type { ColumnMetaData } from '@/api/asset_interfaces'; import type { ColumnMetaData } from '@/api/asset_interfaces/metric';
import type { BusterChartConfigProps } from './chartConfigProps'; import type { BusterChartConfigProps } from './chartConfigProps';
export type BusterChartProps = { export type BusterChartProps = {

View File

@ -12,11 +12,11 @@ export const AppCodeBlock: React.FC<{
wrapperClassName?: string; wrapperClassName?: string;
children?: React.ReactNode; children?: React.ReactNode;
style?: React.CSSProperties; style?: React.CSSProperties;
showCopyButton?: boolean; showCopyButton?: boolean;
title?: string; title?: string;
buttons?: React.ReactNode; buttons?: React.ReactNode;
}> = React.memo(({ title, buttons, ...props }) => { showLoader?: boolean;
}> = React.memo(({ title, buttons, showLoader, ...props }) => {
const { const {
children, children,
className = '', className = '',

View File

@ -44,14 +44,7 @@ const MOCK_DATA: Required<BusterMetricData> = {
data: mockData(), data: mockData(),
metricId: faker.string.uuid(), metricId: faker.string.uuid(),
data_metadata: dataMetadata, data_metadata: dataMetadata,
dataFromRerun: null, dataFromRerun: null
code: `SELECT
sales,
date,
product
FROM sales_data
WHERE date BETWEEN '2024-01-01' AND '2024-01-31'
ORDER BY date ASC`
}; };
export const createMockData = (metricId: string): Required<BusterMetricData> => { export const createMockData = (metricId: string): Required<BusterMetricData> => {

View File

@ -1,6 +1,4 @@
import { queryKeys } from '@/api/query_keys'; import { useGetMetricData } from '@/api/buster_rest/metrics';
import { useSocketQueryEmitAndOnce } from '@/api/buster_socket_query';
import type { BusterMetricData } from '@/api/asset_interfaces/metric';
export const useMetricDataIndividual = ({ metricId }: { metricId: string }) => { export const useMetricDataIndividual = ({ metricId }: { metricId: string }) => {
const { const {
@ -9,25 +7,7 @@ export const useMetricDataIndividual = ({ metricId }: { metricId: string }) => {
refetch: refetchMetricData, refetch: refetchMetricData,
dataUpdatedAt: metricDataUpdatedAt, dataUpdatedAt: metricDataUpdatedAt,
error: metricDataError error: metricDataError
} = useSocketQueryEmitAndOnce({ } = useGetMetricData({ id: metricId });
emitEvent: {
route: '/metrics/data',
payload: { id: metricId }
},
responseEvent: '/metrics/get:fetchingData',
options: queryKeys.chatsMessagesFetchingData(metricId),
callback: (currentData, responseData) => {
const metricId = responseData.metric_id;
const newMetricData: BusterMetricData = {
...currentData,
metricId,
data: responseData.data ?? currentData?.data!,
data_metadata: responseData.data_metadata ?? currentData?.data_metadata!,
code: responseData.code
};
return newMetricData;
}
});
return { return {
metricData, metricData,

View File

@ -51,7 +51,6 @@ export const useSQLProvider = () => {
metricId, metricId,
data, data,
data_metadata, data_metadata,
code,
isDataFromRerun isDataFromRerun
}: { }: {
metricId: string; metricId: string;
@ -66,8 +65,7 @@ export const useSQLProvider = () => {
queryClient.setQueryData(options.queryKey, { queryClient.setQueryData(options.queryKey, {
...currentData!, ...currentData!,
[setter]: data, [setter]: data,
data_metadata, data_metadata
code
}); });
}; };
@ -80,7 +78,7 @@ export const useSQLProvider = () => {
if (!originalConfigs.current[metricId]) { if (!originalConfigs.current[metricId]) {
originalConfigs.current[metricId] = { originalConfigs.current[metricId] = {
chartConfig: metricMessage?.chart_config!, chartConfig: metricMessage?.chart_config!,
code: currentMessageData?.code!, code: metricMessage?.code!,
data: currentMessageData?.data!, data: currentMessageData?.data!,
dataMetadata: currentMessageData?.data_metadata! dataMetadata: currentMessageData?.data_metadata!
}; };

View File

@ -6,9 +6,9 @@ import { useFavoriteProvider } from './useFavoriteProvider';
import { useGetMyUserInfo } from '@/api/buster_rest/users'; import { useGetMyUserInfo } from '@/api/buster_rest/users';
import { useSupabaseContext } from '../Supabase'; import { useSupabaseContext } from '../Supabase';
import { createContext, useContextSelector } from 'use-context-selector'; import { createContext, useContextSelector } from 'use-context-selector';
import { checkIfUserIsAdmin } from './helpers';
import { useUserOrganization } from './useUserOrganization'; import { useUserOrganization } from './useUserOrganization';
import { useInviteUser } from './useInviteUser'; import { useInviteUser } from './useInviteUser';
import { checkIfUserIsAdmin } from '@/lib/user';
export const useUserConfigProvider = ({ userInfo }: { userInfo: BusterUserResponse | null }) => { export const useUserConfigProvider = ({ userInfo }: { userInfo: BusterUserResponse | null }) => {
const isAnonymousUser = useSupabaseContext((state) => state.isAnonymousUser); const isAnonymousUser = useSupabaseContext((state) => state.isAnonymousUser);

View File

@ -1,17 +0,0 @@
import { BusterOrganizationRole } from '@/api/asset_interfaces/organizations';
import { type BusterUserResponse } from '@/api/asset_interfaces/users';
export const checkIfUserIsAdmin = (userInfo?: BusterUserResponse | null): boolean => {
if (!userInfo) return false;
const userOrganization = userInfo?.organizations?.[0];
if (!userOrganization) return false;
const userRole = userOrganization.role;
return (
userRole === BusterOrganizationRole.DATA_ADMIN ||
userRole === BusterOrganizationRole.WORKSPACE_ADMIN
);
};

View File

@ -1,2 +1 @@
export * from './UserConfigProvider'; export * from './UserConfigProvider';
export * from './helpers';

View File

@ -24,5 +24,11 @@
} }
}, },
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"exclude": ["node_modules"] "exclude": [
"node_modules",
"**/*.stories.tsx",
"**/*.stories.ts",
"**/*.test.tsx",
"**/*.test.ts"
]
} }