Browse Source

feat(dashboard-filters): Use UTC in dashboard filters (#37064)

Nar Saynorath 2 years ago
parent
commit
7672ee07eb

+ 5 - 2
static/app/actionCreators/dashboards.tsx

@@ -40,7 +40,7 @@ export function createDashboard(
   newDashboard: DashboardDetails,
   duplicate?: boolean
 ): Promise<DashboardDetails> {
-  const {title, widgets, projects, environment, period, start, end, filters} =
+  const {title, widgets, projects, environment, period, start, end, filters, utc} =
     newDashboard;
 
   const promise: Promise<DashboardDetails> = api.requestPromise(
@@ -57,6 +57,7 @@ export function createDashboard(
         start,
         end,
         filters,
+        utc,
       },
       query: {
         project: projects,
@@ -123,7 +124,8 @@ export function updateDashboard(
   orgId: string,
   dashboard: DashboardDetails
 ): Promise<DashboardDetails> {
-  const {title, widgets, projects, environment, period, start, end, filters} = dashboard;
+  const {title, widgets, projects, environment, period, start, end, filters, utc} =
+    dashboard;
   const data = {
     title,
     widgets: widgets.map(widget => omit(widget, ['tempId'])),
@@ -133,6 +135,7 @@ export function updateDashboard(
     start,
     end,
     filters,
+    utc,
   };
 
   const promise: Promise<DashboardDetails> = api.requestPromise(

+ 1 - 1
static/app/components/organizations/pageFilters/parse.tsx

@@ -71,7 +71,7 @@ function getStatsPeriodValue(maybe: ParamValue) {
  *
  * [0]: https://gist.github.com/asafge/0b13c5066d06ae9a4446
  */
-function normalizeDateTimeString(input: Date | SingleParamValue) {
+export function normalizeDateTimeString(input: Date | SingleParamValue) {
   if (!input) {
     return undefined;
   }

+ 1 - 0
static/app/views/dashboardsV2/orgDashboards.tsx

@@ -106,6 +106,7 @@ class OrgDashboards extends AsyncComponent<Props, State> {
             statsPeriod: data.period,
             start: data.start,
             end: data.end,
+            utc: data.utc,
           },
         });
       }

+ 1 - 0
static/app/views/dashboardsV2/types.tsx

@@ -99,6 +99,7 @@ export type DashboardDetails = {
   environment?: string[];
   period?: string;
   start?: string;
+  utc?: boolean;
 };
 
 export enum DashboardState {

+ 26 - 20
static/app/views/dashboardsV2/utils.tsx

@@ -22,6 +22,7 @@ import {
   SIX_HOURS,
   TWENTY_FOUR_HOURS,
 } from 'sentry/components/charts/utils';
+import {normalizeDateTimeString} from 'sentry/components/organizations/pageFilters/parse';
 import {Organization, PageFilters} from 'sentry/types';
 import {defined} from 'sentry/utils';
 import {getUtcDateString, parsePeriodToHours} from 'sentry/utils/dates';
@@ -407,20 +408,20 @@ export function hasUnsavedFilterChanges(
   location: Location,
   newDashboardFilters: DashboardFilters
 ) {
-  return !isEqual(
-    {
-      projects: initialDashboard.projects,
-      environment: initialDashboard.environment,
-      period: initialDashboard.period,
-      start: initialDashboard.start,
-      end: initialDashboard.end,
-      filters: initialDashboard.filters,
-    },
-    {
-      ...getCurrentPageFilters(location),
-      filters: newDashboardFilters,
-    }
-  );
+  const savedFilters = {
+    projects: initialDashboard.projects,
+    environment: initialDashboard.environment,
+    period: initialDashboard.period,
+    start: normalizeDateTimeString(initialDashboard.start),
+    end: normalizeDateTimeString(initialDashboard.end),
+    filters: initialDashboard.filters,
+    utc: initialDashboard.utc,
+  };
+  const currentFilters = {
+    ...getCurrentPageFilters(location),
+    filters: newDashboardFilters,
+  };
+  return !isEqual(savedFilters, currentFilters);
 }
 
 export function getSavedPageFilters(dashboard: DashboardDetails) {
@@ -428,8 +429,9 @@ export function getSavedPageFilters(dashboard: DashboardDetails) {
     project: dashboard.projects,
     environment: dashboard.environment,
     statsPeriod: dashboard.period,
-    start: dashboard.start,
-    end: dashboard.end,
+    start: normalizeDateTimeString(dashboard.start),
+    end: normalizeDateTimeString(dashboard.end),
+    utc: dashboard.utc,
   };
 }
 
@@ -442,8 +444,11 @@ export function resetPageFilters(dashboard: DashboardDetails, location: Location
 
 export function getCurrentPageFilters(
   location: Location
-): Pick<DashboardDetails, 'projects' | 'environment' | 'period' | 'start' | 'end'> {
-  const {project, environment, statsPeriod, start, end} = location.query ?? {};
+): Pick<
+  DashboardDetails,
+  'projects' | 'environment' | 'period' | 'start' | 'end' | 'utc'
+> {
+  const {project, environment, statsPeriod, start, end, utc} = location.query ?? {};
   return {
     // Ensure projects and environment are sent as arrays, or undefined in the request
     // location.query will return a string if there's only one value
@@ -456,7 +461,8 @@ export function getCurrentPageFilters(
     environment:
       typeof environment === 'string' ? [environment] : environment ?? undefined,
     period: statsPeriod as string | undefined,
-    start: start as string | undefined,
-    end: end as string | undefined,
+    start: defined(start) ? normalizeDateTimeString(start as string) : undefined,
+    end: defined(end) ? normalizeDateTimeString(end as string) : undefined,
+    utc: defined(utc) ? utc === 'true' : undefined,
   };
 }

+ 51 - 0
tests/js/spec/views/dashboardsV2/gridLayout/detail.spec.jsx

@@ -1086,6 +1086,57 @@ describe('Dashboards > Detail', function () {
       );
     });
 
+    it('can save absolute time range in existing dashboard', async () => {
+      MockApiClient.addMockResponse({
+        url: '/organizations/org-slug/releases/',
+        body: [],
+      });
+      const testData = initializeOrg({
+        organization: TestStubs.Organization({
+          features: [
+            'global-views',
+            'dashboards-basic',
+            'dashboards-edit',
+            'discover-query',
+            'dashboard-grid-layout',
+            'dashboards-top-level-filter',
+          ],
+        }),
+        router: {
+          location: {
+            ...TestStubs.location(),
+            query: {
+              start: '2022-07-14T07:00:00',
+              end: '2022-07-19T23:59:59',
+              utc: 'true',
+            },
+          },
+        },
+      });
+      render(
+        <ViewEditDashboard
+          organization={testData.organization}
+          params={{orgId: 'org-slug', dashboardId: '1'}}
+          router={testData.router}
+          location={testData.router.location}
+        />,
+        {context: testData.routerContext, organization: testData.organization}
+      );
+
+      userEvent.click(await screen.findByText('Save'));
+
+      expect(mockPut).toHaveBeenCalledWith(
+        '/organizations/org-slug/dashboards/1/',
+        expect.objectContaining({
+          data: expect.objectContaining({
+            start: '2022-07-14T07:00:00.000',
+            end: '2022-07-19T23:59:59.000',
+            utc: true,
+          }),
+        })
+      );
+    });
+
     it('can clear dashboard filters in existing dashboard', async () => {
       MockApiClient.addMockResponse({
         url: '/organizations/org-slug/releases/',