Просмотр исходного кода

ref(ts): Convert ReleaseSeries to TS (#22737)

Tony 4 лет назад
Родитель
Сommit
f41d9f02c4

+ 2 - 2
src/sentry/static/sentry/app/actionCreators/events.tsx

@@ -15,8 +15,8 @@ import {getPeriod} from 'app/utils/getPeriod';
 
 type Options = {
   organization: OrganizationSummary;
-  project?: number[];
-  environment?: string[];
+  project?: Readonly<number[]>;
+  environment?: Readonly<string[]>;
   period?: string;
   start?: DateString;
   end?: DateString;

+ 2 - 2
src/sentry/static/sentry/app/components/charts/eventsChart.tsx

@@ -35,7 +35,7 @@ type ChartProps = {
   legendOptions?: EChartOption.Legend;
   chartOptions?: EChartOption;
   currentSeriesName?: string;
-  releaseSeries?: Series | null;
+  releaseSeries?: Series[];
   previousTimeseriesData?: Series | null;
   previousSeriesName?: string;
   /**
@@ -340,7 +340,7 @@ type ChartDataProps = {
   results?: Series[];
   timeseriesData?: Series[];
   previousTimeseriesData?: Series | null;
-  releaseSeries?: Series;
+  releaseSeries?: Series[];
 };
 
 class EventsChart extends React.Component<Props> {

+ 2 - 2
src/sentry/static/sentry/app/components/charts/eventsRequest.tsx

@@ -94,11 +94,11 @@ type EventsRequestPartialProps = {
   /**
    * List of project ids to query
    */
-  project?: number[];
+  project?: Readonly<number[]>;
   /**
    * List of environments to query
    */
-  environment?: string[];
+  environment?: Readonly<string[]>;
   /**
    * List of fields to group with when doing a topEvents request.
    */

+ 119 - 84
src/sentry/static/sentry/app/components/charts/releaseSeries.jsx → src/sentry/static/sentry/app/components/charts/releaseSeries.tsx

@@ -1,14 +1,18 @@
 import React from 'react';
 import {withRouter} from 'react-router';
+import {WithRouterProps} from 'react-router/lib/withRouter';
+import {EChartOption} from 'echarts/lib/echarts';
+import {Query} from 'history';
 import isEqual from 'lodash/isEqual';
 import memoize from 'lodash/memoize';
 import partition from 'lodash/partition';
-import PropTypes from 'prop-types';
 
 import {addErrorMessage} from 'app/actionCreators/indicator';
+import {Client} from 'app/api';
 import MarkLine from 'app/components/charts/components/markLine';
 import {t} from 'app/locale';
-import SentryTypes from 'app/sentryTypes';
+import {DateString, Organization} from 'app/types';
+import {Series} from 'app/types/echarts';
 import {escape} from 'app/utils';
 import {getFormattedDate, getUtcDateString} from 'app/utils/dates';
 import {formatVersion} from 'app/utils/formatters';
@@ -17,9 +21,27 @@ import theme from 'app/utils/theme';
 import withApi from 'app/utils/withApi';
 import withOrganization from 'app/utils/withOrganization';
 
+type ReleaseMetaBasic = {
+  version: string;
+  date: string;
+};
+
+type ReleaseConditions = {
+  start: DateString;
+  end: DateString;
+  project: Readonly<number[]>;
+  environment: Readonly<string[]>;
+  statsPeriod?: string;
+  cursor?: string;
+};
+
 // This is not an exported action/function because releases list uses AsyncComponent
 // and this is not re-used anywhere else afaict
-async function getOrganizationReleases(api, organization, conditions = null) {
+function getOrganizationReleases(
+  api: Client,
+  organization: Organization,
+  conditions: ReleaseConditions
+) {
   const query = {};
   Object.keys(conditions).forEach(key => {
     let value = conditions[key];
@@ -35,31 +57,33 @@ async function getOrganizationReleases(api, organization, conditions = null) {
     includeAllArgs: true,
     method: 'GET',
     query,
-  });
+  }) as Promise<[ReleaseMetaBasic[], any, JQueryXHR]>;
 }
 
-class ReleaseSeries extends React.Component {
-  static propTypes = {
-    api: PropTypes.object,
-    router: PropTypes.object,
-    organization: SentryTypes.Organization,
-    projects: PropTypes.arrayOf(PropTypes.number),
-    environments: PropTypes.arrayOf(PropTypes.string),
+type Props = WithRouterProps & {
+  api: Client;
+  organization: Organization;
+  children: (s: State) => React.ReactNode;
+  projects: Readonly<number[]>;
+  environments: Readonly<string[]>;
+  start: DateString;
+  end: DateString;
+  period?: string;
+  utc?: boolean | null;
+  releases?: ReleaseMetaBasic[] | null;
+  tooltip?: EChartOption.Tooltip;
+  memoized?: boolean;
+  preserveQueryParams?: boolean;
+  emphasizeReleases?: string[];
+  queryExtra?: Query;
+};
 
-    period: PropTypes.string,
-    start: PropTypes.oneOfType([PropTypes.instanceOf(Date), PropTypes.string]),
-    end: PropTypes.oneOfType([PropTypes.instanceOf(Date), PropTypes.string]),
-    utc: PropTypes.bool,
-    // Array of releases, if empty, component will fetch releases itself
-    releases: PropTypes.arrayOf(SentryTypes.Release),
-    tooltip: SentryTypes.EChartsTooltip,
-    queryExtra: PropTypes.object,
-
-    memoized: PropTypes.bool,
-    preserveQueryParams: PropTypes.bool,
-    emphasizeReleases: PropTypes.arrayOf(PropTypes.string),
-  };
+type State = {
+  releases: ReleaseMetaBasic[] | null;
+  releaseSeries: Series[];
+};
 
+class ReleaseSeries extends React.Component<Props, State> {
   state = {
     releases: null,
     releaseSeries: [],
@@ -97,10 +121,15 @@ class ReleaseSeries extends React.Component {
     this.props.api.clear();
   }
 
+  _isMounted: boolean = false;
+
   getOrganizationReleasesMemoized = memoize(
-    async (api, conditions, organization) =>
-      await getOrganizationReleases(api, conditions, organization),
-    (_, __, conditions) => `${Object.values(conditions).map(JSON.stringify).join('-')}`
+    (api, conditions, organization) =>
+      getOrganizationReleases(api, conditions, organization),
+    (_, __, conditions) =>
+      Object.values(conditions)
+        .map(val => JSON.stringify(val))
+        .join('-')
   );
 
   async fetchData() {
@@ -114,7 +143,7 @@ class ReleaseSeries extends React.Component {
       end,
       memoized,
     } = this.props;
-    const conditions = {
+    const conditions: ReleaseConditions = {
       start,
       end,
       project: projects,
@@ -122,7 +151,7 @@ class ReleaseSeries extends React.Component {
       statsPeriod: period,
     };
     let hasMore = true;
-    const releases = [];
+    const releases: ReleaseMetaBasic[] = [];
     while (hasMore) {
       try {
         const getReleases = memoized
@@ -137,7 +166,7 @@ class ReleaseSeries extends React.Component {
         const pageLinks = xhr && xhr.getResponseHeader('Link');
         if (pageLinks) {
           const paginationObject = parseLinkHeader(pageLinks);
-          hasMore = paginationObject && paginationObject.next.results;
+          hasMore = paginationObject?.next?.results ?? false;
           conditions.cursor = paginationObject.next.cursor;
         } else {
           hasMore = false;
@@ -155,7 +184,7 @@ class ReleaseSeries extends React.Component {
       releases,
       release => !emphasizeReleases.includes(release.version)
     );
-    const releaseSeries = [];
+    const releaseSeries: Series[] = [];
     if (unemphasizedReleases.length) {
       releaseSeries.push(this.getReleaseSeries(unemphasizedReleases));
     }
@@ -187,66 +216,72 @@ class ReleaseSeries extends React.Component {
       query.project = router.location.query.project;
     }
     if (preserveQueryParams) {
-      query.environment = environments;
+      query.environment = [...environments];
       query.start = start ? getUtcDateString(start) : undefined;
-      query.end = start ? getUtcDateString(end) : undefined;
+      query.end = end ? getUtcDateString(end) : undefined;
       query.statsPeriod = period || undefined;
     }
 
-    return {
-      seriesName: 'Releases',
-      data: [],
-      markLine: MarkLine({
-        animation: false,
-        lineStyle: {
-          normal: {
-            color: theme.purple300,
-            opacity,
-            type: 'solid',
-          },
-        },
-        tooltip: tooltip || {
-          trigger: 'item',
-          formatter: ({data}) => {
-            // XXX using this.props here as this function does not get re-run
-            // unless projects are changed. Using a closure variable would result
-            // in stale values.
-            const time = getFormattedDate(data.value, 'MMM D, YYYY LT', {
-              local: !this.props.utc,
-            });
-            const version = escape(formatVersion(data.name, true));
-            return [
-              '<div class="tooltip-series">',
-              `<div><span class="tooltip-label"><strong>${t(
-                'Release'
-              )}</strong></span> ${version}</div>`,
-              '</div>',
-              '<div class="tooltip-date">',
-              time,
-              '</div>',
-              '</div>',
-              '<div class="tooltip-arrow"></div>',
-            ].join('');
-          },
+    const markLine = MarkLine({
+      animation: false,
+      lineStyle: {
+        color: theme.purple300,
+        opacity,
+        type: 'solid',
+      },
+      label: {
+        show: false,
+      },
+      data: releases.map(release => ({
+        xAxis: +new Date(release.date),
+        name: formatVersion(release.version, true),
+        value: formatVersion(release.version, true),
+        onClick: () => {
+          router.push({
+            pathname: `/organizations/${organization.slug}/releases/${release.version}/`,
+            query,
+          });
         },
         label: {
-          show: false,
+          formatter: () => formatVersion(release.version, true),
+        },
+      })),
+    });
+
+    // TODO(tonyx): This conflicts with the types declaration of `MarkLine`
+    // if we add it in the constructor. So we opt to add it here so typescript
+    // doesn't complain.
+    (markLine as any).tooltip =
+      tooltip ||
+      ({
+        trigger: 'item',
+        formatter: ({data}: EChartOption.Tooltip.Format) => {
+          // XXX using this.props here as this function does not get re-run
+          // unless projects are changed. Using a closure variable would result
+          // in stale values.
+          const time = getFormattedDate(data.value, 'MMM D, YYYY LT', {
+            local: !this.props.utc,
+          });
+          const version = escape(formatVersion(data.name, true));
+          return [
+            '<div class="tooltip-series">',
+            `<div><span class="tooltip-label"><strong>${t(
+              'Release'
+            )}</strong></span> ${version}</div>`,
+            '</div>',
+            '<div class="tooltip-date">',
+            time,
+            '</div>',
+            '</div>',
+            '<div class="tooltip-arrow"></div>',
+          ].join('');
         },
-        data: releases.map(release => ({
-          xAxis: +new Date(release.date),
-          name: formatVersion(release.version, true),
-          value: formatVersion(release.version, true),
-          onClick: () => {
-            router.push({
-              pathname: `/organizations/${organization.slug}/releases/${release.version}/`,
-              query,
-            });
-          },
-          label: {
-            formatter: () => formatVersion(release.version, true),
-          },
-        })),
-      }),
+      } as EChartOption.Tooltip);
+
+    return {
+      seriesName: 'Releases',
+      data: [],
+      markLine,
     };
   };
 

+ 9 - 12
src/sentry/static/sentry/app/views/performance/transactionSummary/durationChart.tsx

@@ -1,7 +1,7 @@
 import React from 'react';
 import {browserHistory} from 'react-router';
 import * as ReactRouter from 'react-router';
-import {Location} from 'history';
+import {Location, Query} from 'history';
 
 import {Client} from 'app/api';
 import AreaChart from 'app/components/charts/areaChart';
@@ -42,7 +42,7 @@ type Props = ReactRouter.WithRouterProps &
     api: Client;
     location: Location;
     organization: OrganizationSummary;
-    queryExtra: object;
+    queryExtra: Query;
   };
 
 const YAXIS_VALUES = ['p50()', 'p75()', 'p95()', 'p99()', 'p100()'];
@@ -80,12 +80,9 @@ class DurationChart extends React.Component<Props> {
       queryExtra,
     } = this.props;
 
-    const start = this.props.start
-      ? getUtcToLocalDateObject(this.props.start)
-      : undefined;
-
-    const end = this.props.end ? getUtcToLocalDateObject(this.props.end) : undefined;
-    const utc = decodeScalar(router.location.query.utc);
+    const start = this.props.start ? getUtcToLocalDateObject(this.props.start) : null;
+    const end = this.props.end ? getUtcToLocalDateObject(this.props.end) : null;
+    const utc = decodeScalar(router.location.query.utc) !== 'false';
 
     const legend = {
       right: 10,
@@ -104,8 +101,8 @@ class DurationChart extends React.Component<Props> {
     };
 
     const datetimeSelection = {
-      start: start || null,
-      end: end || null,
+      start,
+      end,
       period: statsPeriod,
     };
 
@@ -155,8 +152,8 @@ class DurationChart extends React.Component<Props> {
               api={api}
               organization={organization}
               period={statsPeriod}
-              project={[...project]}
-              environment={[...environment]}
+              project={project}
+              environment={environment}
               start={start}
               end={end}
               interval={getInterval(datetimeSelection, true)}

+ 3 - 3
src/sentry/static/sentry/app/views/performance/transactionSummary/sidebarCharts.tsx

@@ -41,7 +41,7 @@ function SidebarCharts({api, eventView, organization, router}: Props) {
   const statsPeriod = eventView.statsPeriod;
   const start = eventView.start ? getUtcToLocalDateObject(eventView.start) : undefined;
   const end = eventView.end ? getUtcToLocalDateObject(eventView.end) : undefined;
-  const utc = decodeScalar(router.location.query.utc);
+  const utc = decodeScalar(router.location.query.utc) !== 'false';
 
   const colors = theme.charts.getColorPalette(3);
   const axisLineConfig = {
@@ -184,8 +184,8 @@ function SidebarCharts({api, eventView, organization, router}: Props) {
             api={api}
             organization={organization}
             period={statsPeriod}
-            project={[...project]}
-            environment={[...environment]}
+            project={project}
+            environment={environment}
             start={start}
             end={end}
             interval={getInterval(datetimeSelection)}

+ 9 - 12
src/sentry/static/sentry/app/views/performance/transactionSummary/trendChart.tsx

@@ -1,7 +1,7 @@
 import React from 'react';
 import * as ReactRouter from 'react-router';
 import {browserHistory} from 'react-router';
-import {Location} from 'history';
+import {Location, Query} from 'history';
 
 import {Client} from 'app/api';
 import ChartZoom from 'app/components/charts/chartZoom';
@@ -43,7 +43,7 @@ type Props = ReactRouter.WithRouterProps &
     api: Client;
     location: Location;
     organization: OrganizationSummary;
-    queryExtra: object;
+    queryExtra: Query;
     trendDisplay: string;
   };
 
@@ -86,12 +86,9 @@ class TrendChart extends React.Component<Props> {
       queryExtra,
     } = this.props;
 
-    const start = this.props.start
-      ? getUtcToLocalDateObject(this.props.start)
-      : undefined;
-
-    const end = this.props.end ? getUtcToLocalDateObject(this.props.end) : undefined;
-    const utc = decodeScalar(router.location.query.utc);
+    const start = this.props.start ? getUtcToLocalDateObject(this.props.start) : null;
+    const end = this.props.end ? getUtcToLocalDateObject(this.props.end) : null;
+    const utc = decodeScalar(router.location.query.utc) !== 'false';
 
     const legend = {
       right: 10,
@@ -110,8 +107,8 @@ class TrendChart extends React.Component<Props> {
     };
 
     const datetimeSelection = {
-      start: start || null,
-      end: end || null,
+      start,
+      end,
       period: statsPeriod,
     };
 
@@ -160,8 +157,8 @@ class TrendChart extends React.Component<Props> {
               api={api}
               organization={organization}
               period={statsPeriod}
-              project={[...project]}
-              environment={[...environment]}
+              project={project}
+              environment={environment}
               start={start}
               end={end}
               interval={getInterval(datetimeSelection, true)}

+ 7 - 10
src/sentry/static/sentry/app/views/performance/transactionSummary/vitalsChart.tsx

@@ -83,12 +83,9 @@ class VitalsChart extends React.Component<Props> {
       queryExtra,
     } = this.props;
 
-    const start = this.props.start
-      ? getUtcToLocalDateObject(this.props.start)
-      : undefined;
-
-    const end = this.props.end ? getUtcToLocalDateObject(this.props.end) : undefined;
-    const utc = decodeScalar(router.location.query.utc);
+    const start = this.props.start ? getUtcToLocalDateObject(this.props.start) : null;
+    const end = this.props.end ? getUtcToLocalDateObject(this.props.end) : null;
+    const utc = decodeScalar(router.location.query.utc) !== 'false';
 
     const legend = {
       right: 10,
@@ -117,8 +114,8 @@ class VitalsChart extends React.Component<Props> {
     };
 
     const datetimeSelection = {
-      start: start || null,
-      end: end || null,
+      start,
+      end,
       period: statsPeriod,
     };
 
@@ -169,8 +166,8 @@ class VitalsChart extends React.Component<Props> {
               api={api}
               organization={organization}
               period={statsPeriod}
-              project={[...project]}
-              environment={[...environment]}
+              project={project}
+              environment={environment}
               start={start}
               end={end}
               interval={getInterval(datetimeSelection, true)}

+ 3 - 0
src/sentry/static/sentry/app/views/performance/transactionVitals/vitalCard.tsx

@@ -558,6 +558,9 @@ class VitalCard extends React.Component<Props, State> {
       },
     });
 
+    // TODO(tonyx): This conflicts with the types declaration of `MarkLine`
+    // if we add it in the constructor. So we opt to add it here so typescript
+    // doesn't complain.
     (markLine as any).tooltip = {
       formatter: () =>
         [

+ 3 - 4
src/sentry/static/sentry/app/views/performance/trends/chart.tsx

@@ -252,10 +252,9 @@ class Chart extends React.Component<Props> {
       trendFunction.chartLabel
     );
 
-    const start = props.start ? getUtcToLocalDateObject(props.start) : undefined;
-
-    const end = props.end ? getUtcToLocalDateObject(props.end) : undefined;
-    const utc = decodeScalar(router.location.query.utc);
+    const start = props.start ? getUtcToLocalDateObject(props.start) : null;
+    const end = props.end ? getUtcToLocalDateObject(props.end) : null;
+    const utc = decodeScalar(router.location.query.utc) !== 'false';
 
     const intervalRatio = getIntervalRatio(router.location);
     const seriesSelection = (

Некоторые файлы не были показаны из-за большого количества измененных файлов