Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions static/app/utils/discover/fieldRenderers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1425,8 +1425,9 @@ function getDashboardUrl(
linkedDashboard => linkedDashboard.field === field
);
if (dashboardLink && dashboardLink.dashboardId !== '-1') {
const newTemporaryFilters: GlobalFilter[] =
dashboardFilters[DashboardFilterKeys.GLOBAL_FILTER] ?? [];
const newTemporaryFilters: GlobalFilter[] = [
...(dashboardFilters[DashboardFilterKeys.GLOBAL_FILTER] ?? []),
].filter(filter => Boolean(filter.value));

// Format the value as a proper filter condition string
const mutableSearch = new MutableSearch('');
Expand Down
12 changes: 6 additions & 6 deletions static/app/views/dashboards/filtersBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,10 @@ import {globalFilterKeysAreEqual} from 'sentry/views/dashboards/globalFilter/uti
import {useDatasetSearchBarData} from 'sentry/views/dashboards/hooks/useDatasetSearchBarData';
import {useHasDrillDownFlows} from 'sentry/views/dashboards/hooks/useHasDrillDownFlows';
import {useInvalidateStarredDashboards} from 'sentry/views/dashboards/hooks/useInvalidateStarredDashboards';
import {getDashboardFiltersFromURL} from 'sentry/views/dashboards/utils';
import {
getCombinedDashboardFilters,
getDashboardFiltersFromURL,
} from 'sentry/views/dashboards/utils';

import {checkUserHasEditAccess} from './utils/checkUserHasEditAccess';
import ReleasesSelectControl from './releasesSelectControl';
Expand Down Expand Up @@ -89,11 +92,8 @@ export default function FiltersBar({
filters?.[DashboardFilterKeys.GLOBAL_FILTER] ??
[];

if (hasDrillDownFlowsFeature) {
return [
...globalFilters,
...(dashboardFiltersFromURL?.[DashboardFilterKeys.TEMPORARY_FILTERS] ?? []),
];
if (hasDrillDownFlowsFeature && dashboardFiltersFromURL) {
return getCombinedDashboardFilters(dashboardFiltersFromURL);
}

return globalFilters;
Expand Down
71 changes: 59 additions & 12 deletions static/app/views/dashboards/prebuiltDashboardRenderer.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import {useMemo} from 'react';

import LoadingContainer from 'sentry/components/loading/loadingContainer';
import {defined} from 'sentry/utils';
import {useApiQuery} from 'sentry/utils/queryClient';
import {useLocation} from 'sentry/utils/useLocation';
import useOrganization from 'sentry/utils/useOrganization';
import useRouter from 'sentry/utils/useRouter';
import DashboardDetail from 'sentry/views/dashboards/detail';
import {DashboardState, type DashboardDetails} from 'sentry/views/dashboards/types';
Expand Down Expand Up @@ -59,19 +63,62 @@ export function PrebuiltDashboardRenderer({prebuiltId}: PrebuiltDashboardRendere

const usePopulateLinkedDashboards = (dashboard: PrebuiltDashboard) => {
const {widgets} = dashboard;
const linkedDashboardsWithStaticDashboardIds = widgets
.flatMap(widget => {
return widget.queries
.flatMap(query => query.linkedDashboards ?? [])
.filter(defined);
})
.filter(linkedDashboard => linkedDashboard.staticDashboardId !== undefined);
const organization = useOrganization();

const prebuiltIds = useMemo(
() =>
widgets
.flatMap(widget => {
return widget.queries
.flatMap(query => query.linkedDashboards ?? [])
.filter(defined);
})
.map(d => d.staticDashboardId)
.filter(defined),
[widgets]
);

const hasLinkedDashboards = prebuiltIds.length > 0;
const path = `/organizations/${organization.slug}/dashboards/`;

const {data, isLoading} = useApiQuery<DashboardDetails[]>(
[
path,
{
query: {prebuiltId: prebuiltIds.sort()},
},
],
{
enabled: hasLinkedDashboards,
staleTime: 0,
retry: false,
}
);

if (!linkedDashboardsWithStaticDashboardIds.length) {
return {dashboard, isLoading: false};
}
return useMemo(() => {
if (!hasLinkedDashboards || !data) {
return {dashboard, isLoading: false};
}

// TODO we should fetch the real dashboard id here, this requires BROWSE-128
const populatedDashboard = {
...dashboard,
widgets: widgets.map(widget => ({
...widget,
queries: widget.queries.map(query => ({
...query,
linkedDashboards: query.linkedDashboards?.map(linkedDashboard => {
if (!linkedDashboard.staticDashboardId) {
return linkedDashboard;
}
const dashboardId = data.find(
d => d.prebuiltId === linkedDashboard.staticDashboardId
)?.id;
return dashboardId ? {...linkedDashboard, dashboardId} : linkedDashboard;
}),
})),
})),
};

return {dashboard, isLoading: false};
return {dashboard: populatedDashboard, isLoading};
}, [dashboard, widgets, data, hasLinkedDashboards, isLoading]);
};
28 changes: 25 additions & 3 deletions static/app/views/dashboards/utils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ import {decodeList, decodeScalar} from 'sentry/utils/queryString';
import type {
DashboardDetails,
DashboardFilters,
GlobalFilter,
Widget,
WidgetQuery,
} from 'sentry/views/dashboards/types';
Expand Down Expand Up @@ -607,11 +608,11 @@ export function dashboardFiltersToString(
}
}

const globalFilters = dashboardFilters?.[DashboardFilterKeys.GLOBAL_FILTER];
const combinedFilters = getCombinedDashboardFilters(dashboardFilters);
// If widgetType is provided, concatenate global filters that apply
if (widgetType && globalFilters) {
if (widgetType && combinedFilters) {
dashboardFilterConditions +=
globalFilters
combinedFilters
.filter(globalFilter => globalFilter.dataset === widgetType)
.map(globalFilter => globalFilter.value)
.join(' ') ?? '';
Expand All @@ -620,6 +621,27 @@ export function dashboardFiltersToString(
return dashboardFilterConditions;
}

// Combines global and temporary filters into a single array, deduplicating by dataset and key prioritizing the temporary filter.
export function getCombinedDashboardFilters(
dashboardFilters: DashboardFilters | null | undefined
): GlobalFilter[] {
const finalFilters = [...(dashboardFilters?.[DashboardFilterKeys.GLOBAL_FILTER] ?? [])];
const temporaryFilters = [
...(dashboardFilters?.[DashboardFilterKeys.TEMPORARY_FILTERS] ?? []),
];
finalFilters.forEach((filter, idx) => {
// if a temporary filter exists for the same dataset and key, override it and delete it from the temporary filters to avoid duplicates
const temporaryFilter = temporaryFilters.find(
tf => tf.dataset === filter.dataset && tf.tag.key === filter.tag.key
);
if (temporaryFilter) {
finalFilters[idx] = {...filter, value: temporaryFilter.value};
temporaryFilters.splice(temporaryFilters.indexOf(temporaryFilter), 1);
}
});
return [...finalFilters, ...temporaryFilters];
}

export function connectDashboardCharts(groupName: string) {
connect?.(groupName);
}
Expand Down
Loading