mirror of
https://github.com/SigNoz/signoz.git
synced 2026-02-03 08:33:26 +00:00
chore: variable store set up (#10145)
Some checks failed
build-staging / prepare (push) Has been cancelled
build-staging / js-build (push) Has been cancelled
build-staging / go-build (push) Has been cancelled
build-staging / staging (push) Has been cancelled
Release Drafter / update_release_draft (push) Has been cancelled
Some checks failed
build-staging / prepare (push) Has been cancelled
build-staging / js-build (push) Has been cancelled
build-staging / go-build (push) Has been cancelled
build-staging / staging (push) Has been cancelled
Release Drafter / update_release_draft (push) Has been cancelled
* chore: update CODEOWNERS file for dashboards * chore: variable store set up
This commit is contained in:
@@ -105,6 +105,7 @@
|
|||||||
"i18next": "^21.6.12",
|
"i18next": "^21.6.12",
|
||||||
"i18next-browser-languagedetector": "^6.1.3",
|
"i18next-browser-languagedetector": "^6.1.3",
|
||||||
"i18next-http-backend": "^1.3.2",
|
"i18next-http-backend": "^1.3.2",
|
||||||
|
"immer": "11.1.3",
|
||||||
"jest": "^27.5.1",
|
"jest": "^27.5.1",
|
||||||
"js-base64": "^3.7.2",
|
"js-base64": "^3.7.2",
|
||||||
"less": "^4.1.2",
|
"less": "^4.1.2",
|
||||||
|
|||||||
19
frontend/src/hooks/dashboard/useDashboardVariables.ts
Normal file
19
frontend/src/hooks/dashboard/useDashboardVariables.ts
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import { useSyncExternalStore } from 'react';
|
||||||
|
|
||||||
|
import {
|
||||||
|
dashboardVariablesStore,
|
||||||
|
IDashboardVariables,
|
||||||
|
} from '../../providers/Dashboard/store/dashboardVariablesStore';
|
||||||
|
|
||||||
|
export const useDashboardVariables = (): {
|
||||||
|
dashboardVariables: IDashboardVariables;
|
||||||
|
} => {
|
||||||
|
const dashboardVariables = useSyncExternalStore(
|
||||||
|
dashboardVariablesStore.subscribe,
|
||||||
|
dashboardVariablesStore.getSnapshot,
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
dashboardVariables,
|
||||||
|
};
|
||||||
|
};
|
||||||
30
frontend/src/hooks/dashboard/useDashboardVariablesByType.ts
Normal file
30
frontend/src/hooks/dashboard/useDashboardVariablesByType.ts
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
import { useMemo } from 'react';
|
||||||
|
import {
|
||||||
|
IDashboardVariable,
|
||||||
|
TVariableQueryType,
|
||||||
|
} from 'types/api/dashboard/getAll';
|
||||||
|
|
||||||
|
import { useDashboardVariables } from './useDashboardVariables';
|
||||||
|
|
||||||
|
export function useDashboardVariablesByType(
|
||||||
|
variableType: TVariableQueryType,
|
||||||
|
returnType: 'values',
|
||||||
|
): IDashboardVariable[];
|
||||||
|
export function useDashboardVariablesByType(
|
||||||
|
variableType: TVariableQueryType,
|
||||||
|
returnType?: 'entries',
|
||||||
|
): [string, IDashboardVariable][];
|
||||||
|
export function useDashboardVariablesByType(
|
||||||
|
variableType: TVariableQueryType,
|
||||||
|
returnType?: 'values' | 'entries',
|
||||||
|
): IDashboardVariable[] | [string, IDashboardVariable][] {
|
||||||
|
const { dashboardVariables } = useDashboardVariables();
|
||||||
|
|
||||||
|
return useMemo(() => {
|
||||||
|
const entries = Object.entries(dashboardVariables || {}).filter(
|
||||||
|
(entry): entry is [string, IDashboardVariable] =>
|
||||||
|
Boolean(entry[1].name) && entry[1].type === variableType,
|
||||||
|
);
|
||||||
|
return returnType === 'values' ? entries.map(([, value]) => value) : entries;
|
||||||
|
}, [dashboardVariables, variableType, returnType]);
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
import { useMemo } from 'react';
|
||||||
|
import { PANEL_GROUP_TYPES } from 'constants/queryBuilder';
|
||||||
|
import { createDynamicVariableToWidgetsMap } from 'hooks/dashboard/utils';
|
||||||
|
import { useDashboard } from 'providers/Dashboard/Dashboard';
|
||||||
|
import { Widgets } from 'types/api/dashboard/getAll';
|
||||||
|
|
||||||
|
import { useDashboardVariablesByType } from './useDashboardVariablesByType';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hook to get a map of dynamic variable IDs to widget IDs that use them.
|
||||||
|
* This is useful for determining which widgets need to be refreshed when a dynamic variable changes.
|
||||||
|
*/
|
||||||
|
export function useWidgetsByDynamicVariableId(): Record<string, string[]> {
|
||||||
|
const dynamicVariables = useDashboardVariablesByType('DYNAMIC', 'values');
|
||||||
|
const { selectedDashboard } = useDashboard();
|
||||||
|
|
||||||
|
return useMemo(() => {
|
||||||
|
const widgets =
|
||||||
|
selectedDashboard?.data?.widgets?.filter(
|
||||||
|
(widget) => widget.panelTypes !== PANEL_GROUP_TYPES.ROW,
|
||||||
|
) || [];
|
||||||
|
|
||||||
|
return createDynamicVariableToWidgetsMap(
|
||||||
|
dynamicVariables,
|
||||||
|
widgets as Widgets[],
|
||||||
|
);
|
||||||
|
}, [selectedDashboard, dynamicVariables]);
|
||||||
|
}
|
||||||
@@ -45,6 +45,8 @@ import APIError from 'types/api/error';
|
|||||||
import { GlobalReducer } from 'types/reducer/globalTime';
|
import { GlobalReducer } from 'types/reducer/globalTime';
|
||||||
import { v4 as generateUUID } from 'uuid';
|
import { v4 as generateUUID } from 'uuid';
|
||||||
|
|
||||||
|
import { useDashboardVariables } from '../../hooks/dashboard/useDashboardVariables';
|
||||||
|
import { updateDashboardVariablesStore } from './store/dashboardVariablesStore';
|
||||||
import {
|
import {
|
||||||
DashboardSortOrder,
|
DashboardSortOrder,
|
||||||
IDashboardContext,
|
IDashboardContext,
|
||||||
@@ -196,6 +198,16 @@ export function DashboardProvider({
|
|||||||
: isDashboardWidgetPage?.params.dashboardId) || '';
|
: isDashboardWidgetPage?.params.dashboardId) || '';
|
||||||
|
|
||||||
const [selectedDashboard, setSelectedDashboard] = useState<Dashboard>();
|
const [selectedDashboard, setSelectedDashboard] = useState<Dashboard>();
|
||||||
|
const dashboardVariables = useDashboardVariables();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const existingVariables = dashboardVariables;
|
||||||
|
const updatedVariables = selectedDashboard?.data.variables || {};
|
||||||
|
|
||||||
|
if (!isEqual(existingVariables, updatedVariables)) {
|
||||||
|
updateDashboardVariablesStore(updatedVariables);
|
||||||
|
}
|
||||||
|
}, [selectedDashboard]);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
currentDashboard,
|
currentDashboard,
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import ROUTES from 'constants/routes';
|
|||||||
import { DashboardProvider, useDashboard } from 'providers/Dashboard/Dashboard';
|
import { DashboardProvider, useDashboard } from 'providers/Dashboard/Dashboard';
|
||||||
import { IDashboardVariable } from 'types/api/dashboard/getAll';
|
import { IDashboardVariable } from 'types/api/dashboard/getAll';
|
||||||
|
|
||||||
|
import { useDashboardVariables } from '../../../hooks/dashboard/useDashboardVariables';
|
||||||
import { initializeDefaultVariables } from '../initializeDefaultVariables';
|
import { initializeDefaultVariables } from '../initializeDefaultVariables';
|
||||||
import { normalizeUrlValueForVariable } from '../normalizeUrlValue';
|
import { normalizeUrlValueForVariable } from '../normalizeUrlValue';
|
||||||
|
|
||||||
@@ -55,6 +56,7 @@ jest.mock('uuid', () => ({ v4: jest.fn(() => 'mock-uuid') }));
|
|||||||
|
|
||||||
function TestComponent(): JSX.Element {
|
function TestComponent(): JSX.Element {
|
||||||
const { dashboardResponse, dashboardId, selectedDashboard } = useDashboard();
|
const { dashboardResponse, dashboardId, selectedDashboard } = useDashboard();
|
||||||
|
const { dashboardVariables } = useDashboardVariables();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
@@ -65,9 +67,7 @@ function TestComponent(): JSX.Element {
|
|||||||
{dashboardResponse.isFetching.toString()}
|
{dashboardResponse.isFetching.toString()}
|
||||||
</div>
|
</div>
|
||||||
<div data-testid="dashboard-variables">
|
<div data-testid="dashboard-variables">
|
||||||
{selectedDashboard?.data?.variables
|
{dashboardVariables ? JSON.stringify(dashboardVariables) : 'null'}
|
||||||
? JSON.stringify(selectedDashboard.data.variables)
|
|
||||||
: 'null'}
|
|
||||||
</div>
|
</div>
|
||||||
<div data-testid="dashboard-data">
|
<div data-testid="dashboard-data">
|
||||||
{selectedDashboard?.data?.title || 'No Title'}
|
{selectedDashboard?.data?.title || 'No Title'}
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
import { IDashboardVariable } from 'types/api/dashboard/getAll';
|
||||||
|
|
||||||
|
import createStore from './store';
|
||||||
|
|
||||||
|
// export type IDashboardVariables = DashboardData['variables'];
|
||||||
|
export type IDashboardVariables = Record<string, IDashboardVariable>;
|
||||||
|
|
||||||
|
export const dashboardVariablesStore = createStore<IDashboardVariables>({});
|
||||||
|
|
||||||
|
export function updateDashboardVariablesStore(
|
||||||
|
variables: Partial<IDashboardVariables>,
|
||||||
|
): void {
|
||||||
|
dashboardVariablesStore.update((currentVariables) => ({
|
||||||
|
...currentVariables,
|
||||||
|
...variables,
|
||||||
|
}));
|
||||||
|
}
|
||||||
44
frontend/src/providers/Dashboard/store/store.ts
Normal file
44
frontend/src/providers/Dashboard/store/store.ts
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
import { produce } from 'immer';
|
||||||
|
type ListenerFn = () => void;
|
||||||
|
|
||||||
|
export default function createStore<T>(
|
||||||
|
init: T,
|
||||||
|
): {
|
||||||
|
set: (setter: any) => void;
|
||||||
|
update: (updater: (draft: T) => void) => void;
|
||||||
|
subscribe: (listener: ListenerFn) => () => void;
|
||||||
|
getSnapshot: () => T;
|
||||||
|
} {
|
||||||
|
let listeners: ListenerFn[] = [];
|
||||||
|
let state = init;
|
||||||
|
|
||||||
|
function emitChange(): void {
|
||||||
|
for (const listener of listeners) {
|
||||||
|
listener();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function set(setter: any): void {
|
||||||
|
state = produce(state, setter);
|
||||||
|
emitChange();
|
||||||
|
}
|
||||||
|
|
||||||
|
function update(updater: (draft: T) => void): void {
|
||||||
|
state = produce(state, updater);
|
||||||
|
emitChange();
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
set,
|
||||||
|
update,
|
||||||
|
subscribe(listener: ListenerFn): () => void {
|
||||||
|
listeners = [...listeners, listener];
|
||||||
|
return (): void => {
|
||||||
|
listeners = listeners.filter((l) => l !== listener);
|
||||||
|
};
|
||||||
|
},
|
||||||
|
getSnapshot(): T {
|
||||||
|
return state;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -12030,6 +12030,11 @@ immediate@~3.0.5:
|
|||||||
resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.0.6.tgz#9db1dbd0faf8de6fbe0f5dd5e56bb606280de69b"
|
resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.0.6.tgz#9db1dbd0faf8de6fbe0f5dd5e56bb606280de69b"
|
||||||
integrity sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==
|
integrity sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==
|
||||||
|
|
||||||
|
immer@11.1.3:
|
||||||
|
version "11.1.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/immer/-/immer-11.1.3.tgz#78681e1deb6cec39753acf04eb16d7576c04f4d6"
|
||||||
|
integrity sha512-6jQTc5z0KJFtr1UgFpIL3N9XSC3saRaI9PwWtzM2pSqkNGtiNkYY2OSwkOGDK2XcTRcLb1pi/aNkKZz0nxVH4Q==
|
||||||
|
|
||||||
immer@^9.0.6:
|
immer@^9.0.6:
|
||||||
version "9.0.21"
|
version "9.0.21"
|
||||||
resolved "https://registry.yarnpkg.com/immer/-/immer-9.0.21.tgz#1e025ea31a40f24fb064f1fef23e931496330176"
|
resolved "https://registry.yarnpkg.com/immer/-/immer-9.0.21.tgz#1e025ea31a40f24fb064f1fef23e931496330176"
|
||||||
|
|||||||
Reference in New Issue
Block a user