Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
15 changes: 9 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,11 @@ export default function FiltersBar({
filters?.[DashboardFilterKeys.GLOBAL_FILTER] ??
[];

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

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]);
};
30 changes: 27 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,14 @@ export function dashboardFiltersToString(
}
}

const globalFilters = dashboardFilters?.[DashboardFilterKeys.GLOBAL_FILTER];
const combinedFilters = getCombinedDashboardFilters(
dashboardFilters?.[DashboardFilterKeys.GLOBAL_FILTER],
dashboardFilters?.[DashboardFilterKeys.TEMPORARY_FILTERS]
);
// 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 +624,26 @@ 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(
globalFilters?: GlobalFilter[],
temporaryFilters?: GlobalFilter[]
): GlobalFilter[] {
const finalFilters = [...(globalFilters ?? [])];
const temporaryFiltersCopy = [...(temporaryFilters ?? [])];
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 = temporaryFiltersCopy.find(
tf => tf.dataset === filter.dataset && tf.tag.key === filter.tag.key
);
if (temporaryFilter) {
finalFilters[idx] = {...filter, value: temporaryFilter.value};
temporaryFiltersCopy.splice(temporaryFiltersCopy.indexOf(temporaryFilter), 1);
}
});
return [...finalFilters, ...temporaryFiltersCopy];
}

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