Browse Source

feat(ui): Clamp release period to 90 days (#32769)

Matej Minar 3 years ago
parent
commit
ca5468120d

+ 30 - 16
static/app/views/releases/detail/overview/index.tsx

@@ -459,22 +459,36 @@ class ReleaseOverview extends AsyncView<Props> {
                             end={end ?? null}
                             utc={utc ?? null}
                             onUpdate={this.handleDateChange}
-                            relativeOptions={{
-                              [RELEASE_PERIOD_KEY]: (
-                                <Fragment>
-                                  {t('Entire Release Period')} (
-                                  <DateTime
-                                    date={releaseBounds.releaseStart}
-                                    timeAndDate
-                                  />{' '}
-                                  -{' '}
-                                  <DateTime date={releaseBounds.releaseEnd} timeAndDate />
-                                  )
-                                </Fragment>
-                              ),
-                              ...DEFAULT_RELATIVE_PERIODS,
-                            }}
-                            defaultPeriod={RELEASE_PERIOD_KEY}
+                            relativeOptions={
+                              releaseBounds.type !== 'ancient'
+                                ? {
+                                    [RELEASE_PERIOD_KEY]: (
+                                      <Fragment>
+                                        {releaseBounds.type === 'clamped'
+                                          ? t('Clamped Release Period')
+                                          : t('Entire Release Period')}{' '}
+                                        (
+                                        <DateTime
+                                          date={releaseBounds.releaseStart}
+                                          timeAndDate
+                                        />{' '}
+                                        -{' '}
+                                        <DateTime
+                                          date={releaseBounds.releaseEnd}
+                                          timeAndDate
+                                        />
+                                        )
+                                      </Fragment>
+                                    ),
+                                    ...DEFAULT_RELATIVE_PERIODS,
+                                  }
+                                : DEFAULT_RELATIVE_PERIODS
+                            }
+                            defaultPeriod={
+                              releaseBounds.type !== 'ancient'
+                                ? RELEASE_PERIOD_KEY
+                                : '90d'
+                            }
                             defaultAbsolute={{
                               start: moment(releaseBounds.releaseStart)
                                 .subtract(1, 'hour')

+ 26 - 21
static/app/views/releases/utils/index.tsx

@@ -10,6 +10,7 @@ import {PAGE_URL_PARAM, URL_PARAM} from 'sentry/constants/pageFilters';
 import {desktop, mobile, PlatformKey} from 'sentry/data/platformCategories';
 import {t, tct} from 'sentry/locale';
 import {Release, ReleaseStatus} from 'sentry/types';
+import {defined} from 'sentry/utils';
 import {Theme} from 'sentry/utils/theme';
 import {MutableSearch} from 'sentry/utils/tokenizeSearch';
 import {IssueSortOptions} from 'sentry/views/issueList/utils';
@@ -127,41 +128,45 @@ export const getReleaseHandledIssuesUrl = (
 export const isReleaseArchived = (release: Release) =>
   release.status === ReleaseStatus.Archived;
 
-export type ReleaseBounds = {releaseEnd?: string | null; releaseStart?: string | null};
+export type ReleaseBounds = {
+  type: 'normal' | 'clamped' | 'ancient';
+  releaseEnd?: string | null;
+  releaseStart?: string | null;
+};
 
 export function getReleaseBounds(release?: Release): ReleaseBounds {
+  const retentionBound = moment().subtract(90, 'days');
   const {lastEvent, currentProjectMeta, dateCreated} = release || {};
   const {sessionsUpperBound} = currentProjectMeta || {};
 
-  const releaseStart = moment(dateCreated).startOf('minute').utc().format();
-  const releaseEnd = moment(
+  let type: ReleaseBounds['type'] = 'normal';
+  let releaseStart = moment(dateCreated).startOf('minute');
+  let releaseEnd = moment(
     (moment(sessionsUpperBound).isAfter(lastEvent) ? sessionsUpperBound : lastEvent) ??
       undefined
-  )
-    .endOf('minute')
-    .utc()
-    .format();
+  ).endOf('minute');
 
   if (moment(releaseStart).isSame(releaseEnd, 'minute')) {
-    return {
-      releaseStart,
-      releaseEnd: moment(releaseEnd).add(1, 'minutes').utc().format(),
-    };
+    releaseEnd = moment(releaseEnd).add(1, 'minutes');
   }
 
-  const thousandDaysAfterReleaseStart = moment(releaseStart).add('999', 'days');
-  if (thousandDaysAfterReleaseStart.isBefore(releaseEnd)) {
-    // if the release spans for more than thousand days, we need to clamp it
-    // (otherwise we would hit the backend limit for the amount of data buckets)
-    return {
-      releaseStart,
-      releaseEnd: thousandDaysAfterReleaseStart.utc().format(),
-    };
+  if (releaseStart.isBefore(retentionBound)) {
+    releaseStart = retentionBound;
+    type = 'clamped';
+
+    if (
+      releaseEnd.isBefore(releaseStart) ||
+      (!defined(sessionsUpperBound) && !defined(lastEvent))
+    ) {
+      releaseEnd = moment();
+      type = 'ancient';
+    }
   }
 
   return {
-    releaseStart,
-    releaseEnd,
+    type,
+    releaseStart: releaseStart.utc().format(),
+    releaseEnd: releaseEnd.utc().format(),
   };
 }
 

+ 40 - 5
tests/js/spec/views/releases/utils/index.spec.tsx

@@ -8,6 +8,7 @@ describe('releases/utils', () => {
       expect(getReleaseBounds(TestStubs.Release())).toEqual({
         releaseStart: '2020-03-23T01:02:00Z',
         releaseEnd: '2020-03-24T02:04:59Z',
+        type: 'normal',
       });
     });
 
@@ -21,6 +22,7 @@ describe('releases/utils', () => {
       ).toEqual({
         releaseStart: '2020-03-23T01:02:00Z',
         releaseEnd: '2020-03-24T03:04:59Z',
+        type: 'normal',
       });
     });
 
@@ -28,6 +30,7 @@ describe('releases/utils', () => {
       expect(getReleaseBounds(TestStubs.Release({lastEvent: null}))).toEqual({
         releaseStart: '2020-03-23T01:02:00Z',
         releaseEnd: '2017-10-17T02:41:59Z',
+        type: 'normal',
       });
     });
 
@@ -42,20 +45,52 @@ describe('releases/utils', () => {
       ).toEqual({
         releaseStart: '2020-03-23T01:02:00Z',
         releaseEnd: '2020-03-23T01:03:59Z',
+        type: 'normal',
       });
     });
 
-    it('clamps releases lasting longer than 1000 days', () => {
+    it('clamps active releases lasting longer than 90 days', () => {
       expect(
         getReleaseBounds(
           TestStubs.Release({
-            dateCreated: '2020-03-23T01:02:30Z',
-            lastEvent: '2023-03-23T01:02:30Z',
+            dateCreated: '2017-05-17T02:41:20Z',
+            lastEvent: '2017-10-12T02:41:20Z',
           })
         )
       ).toEqual({
-        releaseStart: '2020-03-23T01:02:00Z',
-        releaseEnd: '2022-12-17T01:02:00Z',
+        releaseStart: '2017-07-19T02:41:20Z',
+        releaseEnd: '2017-10-12T02:41:59Z',
+        type: 'clamped',
+      });
+    });
+
+    it('defaults ancient releases to last 90 days', () => {
+      expect(
+        getReleaseBounds(
+          TestStubs.Release({
+            dateCreated: '2010-05-17T02:41:20Z',
+            lastEvent: '2011-10-17T02:41:20Z',
+          })
+        )
+      ).toEqual({
+        releaseStart: '2017-07-19T02:41:20Z',
+        releaseEnd: '2017-10-17T02:41:20Z',
+        type: 'ancient',
+      });
+    });
+
+    it('handles no lastEvent for ancient releases', () => {
+      expect(
+        getReleaseBounds(
+          TestStubs.Release({
+            dateCreated: '2010-05-17T02:41:20Z',
+            lastEvent: null,
+          })
+        )
+      ).toEqual({
+        releaseStart: '2017-07-19T02:41:20Z',
+        releaseEnd: '2017-10-17T02:41:20Z',
+        type: 'ancient',
       });
     });
   });