Browse Source

ref: Convert evententries to typescript (#20609)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Markus Unterwaditzer 4 years ago
parent
commit
1415e603a4

+ 53 - 48
src/sentry/static/sentry/app/components/events/errors.tsx

@@ -15,7 +15,7 @@ import {
 } from 'app/actionCreators/indicator';
 import {Client} from 'app/api';
 import EventErrorItem from 'app/components/events/errorItem';
-import {Event, Project} from 'app/types';
+import {Event, AvatarProject, Project} from 'app/types';
 import {IconWarning} from 'app/icons';
 import {t, tn} from 'app/locale';
 import space from 'app/styles/space';
@@ -29,8 +29,8 @@ type Props = {
   api: Client;
   event: Event;
   orgId: string;
-  project: Project;
-  issueId: string;
+  project: AvatarProject | Project;
+  issueId?: string;
 };
 
 type State = {
@@ -43,7 +43,7 @@ class EventErrors extends React.Component<Props, State> {
     event: PropTypes.object.isRequired,
     orgId: PropTypes.string.isRequired,
     project: PropTypes.object.isRequired,
-    issueId: PropTypes.string.isRequired,
+    issueId: PropTypes.string,
   };
 
   state: State = {
@@ -61,7 +61,7 @@ class EventErrors extends React.Component<Props, State> {
     this.setState(state => ({isOpen: !state.isOpen}));
   };
 
-  uniqueErrors = errors => uniqWith(errors, isEqual);
+  uniqueErrors = (errors: any[]) => uniqWith(errors, isEqual);
 
   onReprocessEvent = async () => {
     const {api, orgId, project, event} = this.props;
@@ -87,8 +87,8 @@ class EventErrors extends React.Component<Props, State> {
     );
   };
 
-  onReprocessGroup = async () => {
-    const {api, orgId, issueId} = this.props;
+  onReprocessGroup = async (issueId: string) => {
+    const {api, orgId} = this.props;
     const endpoint = `/organizations/${orgId}/issues/${issueId}/reprocessing/`;
 
     addLoadingMessage(t('Reprocessing issue\u2026'));
@@ -115,49 +115,54 @@ class EventErrors extends React.Component<Props, State> {
     openModal(this.renderReprocessModal);
   };
 
-  renderReprocessModal = ({Body, closeModal, Footer}) => (
-    <React.Fragment>
-      <Body>
-        <p>
-          {t(
-            'You can choose to re-process events to see if your errors have been resolved. Keep the following limitations in mind:'
-          )}
-        </p>
-
-        <ul>
-          <li>
-            {t(
-              'Sentry will duplicate events in your project (for now) and not delete the old versions.'
-            )}
-          </li>
-          <li>
-            {t(
-              'Reprocessing one or multiple events counts against your quota, but bypasses rate limits.'
-            )}
-          </li>
-          <li>
+  renderReprocessModal = ({Body, closeModal, Footer}) => {
+    const {issueId} = this.props;
+    return (
+      <React.Fragment>
+        <Body>
+          <p>
             {t(
-              'If an event is reprocessed but did not change, we will not create the new version and not bill you for it (for now).'
+              'You can choose to re-process events to see if your errors have been resolved. Keep the following limitations in mind:'
             )}
-          </li>
-          <li>
-            {t(
-              'If you have provided missing symbols please wait at least 1 hour before attempting to re-process. This is a limitation we will try to get rid of.'
+          </p>
+
+          <ul>
+            <li>
+              {t(
+                'Sentry will duplicate events in your project (for now) and not delete the old versions.'
+              )}
+            </li>
+            <li>
+              {t(
+                'Reprocessing one or multiple events counts against your quota, but bypasses rate limits.'
+              )}
+            </li>
+            <li>
+              {t(
+                'If an event is reprocessed but did not change, we will not create the new version and not bill you for it (for now).'
+              )}
+            </li>
+            <li>
+              {t(
+                'If you have provided missing symbols please wait at least 1 hour before attempting to re-process. This is a limitation we will try to get rid of.'
+              )}
+            </li>
+          </ul>
+        </Body>
+        <Footer>
+          <ButtonBar gap={1}>
+            {issueId && (
+              <Button onClick={() => this.onReprocessGroup(issueId)}>
+                {t('Reprocess all events in issue')}
+              </Button>
             )}
-          </li>
-        </ul>
-      </Body>
-      <Footer>
-        <ButtonBar gap={1}>
-          <Button onClick={this.onReprocessGroup}>
-            {t('Reprocess all events in issue')}
-          </Button>
-          <Button onClick={this.onReprocessEvent}>{t('Reprocess single event')}</Button>
-          <Button onClick={closeModal}>{t('Cancel')}</Button>
-        </ButtonBar>
-      </Footer>
-    </React.Fragment>
-  );
+            <Button onClick={this.onReprocessEvent}>{t('Reprocess single event')}</Button>
+            <Button onClick={closeModal}>{t('Cancel')}</Button>
+          </ButtonBar>
+        </Footer>
+      </React.Fragment>
+    );
+  };
 
   render() {
     const {event, project} = this.props;
@@ -190,7 +195,7 @@ class EventErrors extends React.Component<Props, State> {
             <EventErrorItem key={errorIdx} error={error} />
           ))}
 
-          {project?.features?.includes('reprocessing-v2') && (
+          {(project as Project)?.features?.includes('reprocessing-v2') && (
             <Button size="xsmall" onClick={this.onReprocessStart}>
               {t('Try again')}
             </Button>

+ 53 - 28
src/sentry/static/sentry/app/components/events/eventEntries.jsx → src/sentry/static/sentry/app/components/events/eventEntries.tsx

@@ -1,6 +1,7 @@
 import React from 'react';
 import PropTypes from 'prop-types';
 import styled from '@emotion/styled';
+import {Location} from 'history';
 
 import {analytics} from 'app/utils/analytics';
 import {logException} from 'app/utils/logging';
@@ -37,6 +38,7 @@ import ThreadsInterface from 'app/components/events/interfaces/threads/threads';
 import {DataSection} from 'app/components/events/styles';
 import space from 'app/styles/space';
 import withOrganization from 'app/utils/withOrganization';
+import {Event, AvatarProject, Group} from 'app/types';
 
 export const INTERFACES = {
   exception: ExceptionInterface,
@@ -54,16 +56,40 @@ export const INTERFACES = {
   spans: SpansInterface,
 };
 
-class EventEntries extends React.Component {
+const defaultProps = {
+  isShare: false,
+  showExampleCommit: false,
+  showTagSummary: true,
+};
+
+// Custom shape because shared view doesn't get id.
+type SharedViewOrganization = {
+  slug: string;
+  id?: string;
+  features?: Array<string>;
+};
+
+type Props = {
+  // This is definitely required because this component would crash if
+  // organization were undefined.
+  organization: SharedViewOrganization;
+  event: Event;
+  project: AvatarProject;
+  location: Location;
+
+  group?: Group;
+  className?: string;
+} & typeof defaultProps;
+
+class EventEntries extends React.Component<Props> {
   static propTypes = {
     // Custom shape because shared view doesn't get id.
     organization: PropTypes.shape({
       id: PropTypes.string,
       slug: PropTypes.string.isRequired,
       features: PropTypes.arrayOf(PropTypes.string),
-    }),
-    // event is not guaranteed in shared issue view
-    event: SentryTypes.Event,
+    }).isRequired,
+    event: SentryTypes.Event.isRequired,
 
     group: SentryTypes.Group,
     project: PropTypes.object.isRequired,
@@ -74,11 +100,7 @@ class EventEntries extends React.Component {
     showTagSummary: PropTypes.bool,
   };
 
-  static defaultProps = {
-    isShare: false,
-    showExampleCommit: false,
-    showTagSummary: true,
-  };
+  static defaultProps = defaultProps;
 
   componentDidMount() {
     const {event} = this.props;
@@ -93,7 +115,7 @@ class EventEntries extends React.Component {
     this.recordIssueError(errorTypes, errorMessages);
   }
 
-  shouldComponentUpdate(nextProps) {
+  shouldComponentUpdate(nextProps: Props) {
     const {event, showExampleCommit} = this.props;
 
     return (
@@ -102,14 +124,15 @@ class EventEntries extends React.Component {
     );
   }
 
-  recordIssueError(errorTypes, errorMessages) {
+  recordIssueError(errorTypes: any[], errorMessages: string[]) {
     const {organization, project, event} = this.props;
+
     const orgId = organization.id;
     const platform = project.platform;
 
     analytics('issue_error_banner.viewed', {
-      org_id: parseInt(orgId, 10),
-      group: event.groupID,
+      org_id: orgId ? parseInt(orgId, 10) : null,
+      group: event?.groupID,
       error_type: errorTypes,
       error_message: errorMessages,
       ...(platform && {platform}),
@@ -150,12 +173,7 @@ class EventEntries extends React.Component {
       } catch (ex) {
         logException(ex);
         return (
-          <EventDataSection
-            projectId={project.slug}
-            event={event}
-            type={entry.type}
-            title={entry.type}
-          >
+          <EventDataSection type={entry.type} title={entry.type}>
             <p>{t('There was an error rendering this data.')}</p>
           </EventDataSection>
         );
@@ -212,7 +230,7 @@ class EventEntries extends React.Component {
               projectId={project.slug}
             />
           ))}
-        {event.userReport && group && (
+        {event?.userReport && group && (
           <StyledEventUserFeedback
             report={event.userReport}
             orgId={organization.slug}
@@ -232,9 +250,9 @@ class EventEntries extends React.Component {
         )}
         {this.renderEntries()}
         {hasContext && <EventContexts group={group} event={event} />}
-        {!objectIsEmpty(event.context) && <EventExtraData event={event} />}
-        {!objectIsEmpty(event.packages) && <EventPackageData event={event} />}
-        {!objectIsEmpty(event.device) && <EventDevice event={event} />}
+        {event && !objectIsEmpty(event.context) && <EventExtraData event={event} />}
+        {event && !objectIsEmpty(event.packages) && <EventPackageData event={event} />}
+        {event && !objectIsEmpty(event.device) && <EventDevice event={event} />}
         {!isShare && features.has('event-attachments') && (
           <EventAttachments
             event={event}
@@ -243,9 +261,11 @@ class EventEntries extends React.Component {
             location={location}
           />
         )}
-        {!objectIsEmpty(event.sdk) && <EventSdk sdk={event.sdk} />}
-        {!isShare && event?.sdkUpdates?.length > 0 && <EventSdkUpdates event={event} />}
-        {!isShare && event.groupID && (
+        {event?.sdk && !objectIsEmpty(event.sdk) && <EventSdk sdk={event.sdk} />}
+        {!isShare && event?.sdkUpdates && event.sdkUpdates.length > 0 && (
+          <EventSdkUpdates event={{sdkUpdates: event.sdkUpdates, ...event}} />
+        )}
+        {!isShare && event?.groupID && (
           <EventGroupingInfo
             projectId={project.slug}
             event={event}
@@ -287,7 +307,11 @@ const BorderlessEventEntries = styled(EventEntries)`
   }
 `;
 
-const StyledEventUserFeedback = styled(EventUserFeedback)`
+type StyledEventUserFeedbackProps = {
+  includeBorder: boolean;
+};
+
+const StyledEventUserFeedback = styled(EventUserFeedback)<StyledEventUserFeedbackProps>`
   border-radius: 0;
   box-shadow: none;
   padding: 20px 30px 0 40px;
@@ -296,5 +320,6 @@ const StyledEventUserFeedback = styled(EventUserFeedback)`
   margin: 0;
 `;
 
-export default withOrganization(EventEntries);
+// TODO(ts): any required due to our use of SharedViewOrganization
+export default withOrganization<any>(EventEntries);
 export {BorderlessEventEntries};

+ 0 - 2
src/sentry/static/sentry/app/components/events/groupingInfo/index.tsx

@@ -1,7 +1,6 @@
 import React from 'react';
 import styled from '@emotion/styled';
 
-import {Client} from 'app/api';
 import AsyncComponent from 'app/components/asyncComponent';
 import EventDataSection from 'app/components/events/eventDataSection';
 import {t} from 'app/locale';
@@ -15,7 +14,6 @@ import GroupVariant from './groupingVariant';
 import GroupingConfigSelect from './groupingConfigSelect';
 
 type Props = AsyncComponent['props'] & {
-  api: Client;
   organization: Organization;
   projectId: string;
   event: Event;

+ 3 - 1
src/sentry/static/sentry/app/types/index.tsx

@@ -319,7 +319,7 @@ type SentryEventBase = {
   dateReceived?: string;
   endTimestamp?: number;
   entries: EntryType[];
-  errors: object[];
+  errors: any[];
 
   previousEventID?: string;
   nextEventID?: string;
@@ -340,6 +340,8 @@ type SentryEventBase = {
     enhancements: string;
   };
 
+  userReport?: any;
+
   crashFile: EventAttachment | null;
 
   sdk?: {

+ 1 - 1
src/sentry/static/sentry/app/utils.tsx

@@ -91,7 +91,7 @@ export function sortArray<T>(arr: Array<T>, score_fn: (entry: T) => string): Arr
   return arr;
 }
 
-export function objectIsEmpty(obj: object): boolean {
+export function objectIsEmpty(obj = {}): boolean {
   for (const prop in obj) {
     if (obj.hasOwnProperty(prop)) {
       return false;

+ 1 - 1
src/sentry/static/sentry/app/utils/logging.tsx

@@ -1,6 +1,6 @@
 import * as Sentry from '@sentry/react';
 
-export function logException(ex: Error, context: any): void {
+export function logException(ex: Error, context?: any): void {
   Sentry.withScope(scope => {
     if (context) {
       scope.setExtra('context', context);

+ 2 - 6
src/sentry/static/sentry/app/views/eventsV2/eventDetails/content.tsx

@@ -10,8 +10,6 @@ import {EventQuery} from 'app/actionCreators/events';
 import space from 'app/styles/space';
 import {t} from 'app/locale';
 import {trackAnalyticsEvent} from 'app/utils/analytics';
-import {Client} from 'app/api';
-import withApi from 'app/utils/withApi';
 import {getMessage, getTitle} from 'app/utils/events';
 import {Organization, Event, EventTag} from 'app/types';
 import SentryTypes from 'app/sentryTypes';
@@ -56,7 +54,6 @@ type Props = {
   organization: Organization;
   location: Location;
   params: Params;
-  api: Client;
   eventSlug: string;
   eventView: EventView;
 };
@@ -145,7 +142,7 @@ class EventDetailsContent extends AsyncComponent<Props, State> {
   }
 
   renderContent(event: Event) {
-    const {api, organization, location, eventView} = this.props;
+    const {organization, location, eventView} = this.props;
     const {isSidebarVisible} = this.state;
 
     // metrics
@@ -221,7 +218,6 @@ class EventDetailsContent extends AsyncComponent<Props, State> {
                     }}
                   >
                     <BorderlessEventEntries
-                      api={api}
                       organization={organization}
                       event={event}
                       project={projects[0]}
@@ -321,4 +317,4 @@ const EventSubheading = styled('span')`
   margin-left: ${space(1)};
 `;
 
-export default withApi(EventDetailsContent);
+export default EventDetailsContent;

+ 3 - 7
src/sentry/static/sentry/app/views/performance/transactionDetails/content.tsx

@@ -5,8 +5,6 @@ import PropTypes from 'prop-types';
 
 import {t} from 'app/locale';
 import {trackAnalyticsEvent} from 'app/utils/analytics';
-import {Client} from 'app/api';
-import withApi from 'app/utils/withApi';
 import {Organization, Event, EventTag} from 'app/types';
 import SentryTypes from 'app/sentryTypes';
 import EventMetadata from 'app/components/events/eventMetadata';
@@ -32,7 +30,6 @@ type Props = {
   organization: Organization;
   location: Location;
   params: Params;
-  api: Client;
   eventSlug: string;
 };
 
@@ -107,7 +104,7 @@ class EventDetailsContent extends AsyncComponent<Props, State> {
   }
 
   renderContent(event: Event) {
-    const {api, organization, location, eventSlug} = this.props;
+    const {organization, location, eventSlug} = this.props;
 
     // metrics
     trackAnalyticsEvent({
@@ -156,13 +153,12 @@ class EventDetailsContent extends AsyncComponent<Props, State> {
                   }}
                 >
                   <BorderlessEventEntries
-                    api={api}
                     organization={organization}
                     event={event}
                     project={projects[0]}
-                    location={location}
                     showExampleCommit={false}
                     showTagSummary={false}
+                    location={location}
                   />
                 </SpanEntryContext.Provider>
               )}
@@ -219,4 +215,4 @@ class EventDetailsContent extends AsyncComponent<Props, State> {
   }
 }
 
-export default withApi(EventDetailsContent);
+export default EventDetailsContent;