Skip to content

Commit ad47e17

Browse files
authored
chore(ts): Initial typescript conversion in preparation of discover v2 (#14345)
1 parent 220769a commit ad47e17

23 files changed

+481
-103
lines changed

src/sentry/static/sentry/app/types/index.tsx

Lines changed: 104 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import {SpanEntry} from 'app/views/organizationEventsV2/transactionView/types';
2+
13
export type Organization = {
24
id: string;
35
slug: string;
@@ -64,19 +66,50 @@ export type EventAttachment = {
6466
type: string;
6567
};
6668

67-
// This type is incomplete
68-
export type Event = {
69+
type EntryType = {
70+
data: {[key: string]: any} | any[];
71+
type: string;
72+
};
73+
74+
export type EventTag = {key: string; value: string};
75+
76+
type SentryEventBase = {
6977
id: string;
7078
eventID: string;
7179
groupID?: string;
72-
type: string;
7380
title: string;
7481
culprit: string;
7582
metadata: EventMetadata;
7683
message: string;
7784
platform?: string;
85+
dateCreated?: string;
86+
endTimestamp?: number;
87+
entries: EntryType[];
88+
89+
previousEventID?: string;
90+
nextEventID?: string;
91+
projectSlug: string;
92+
93+
tags: EventTag[];
94+
95+
size: number;
96+
97+
location: string;
98+
99+
oldestEventID: string | null;
100+
latestEventID: string | null;
78101
};
79102

103+
// This type is incomplete
104+
export type Event =
105+
| ({type: string} & SentryEventBase)
106+
| {
107+
type: 'transaction';
108+
entries: SpanEntry[];
109+
startTimestamp: number;
110+
endTimestamp: number;
111+
} & SentryEventBase;
112+
80113
export type EventsStatsData = [number, {count: number}[]][];
81114

82115
export type EventsStats = {
@@ -190,3 +223,71 @@ export type Config = {
190223
};
191224
distPrefix: string;
192225
};
226+
227+
type Metadata = {
228+
value: string;
229+
message: string;
230+
directive: string;
231+
type: string;
232+
title: string;
233+
uri: string;
234+
};
235+
236+
type EventOrGroupType = [
237+
'error',
238+
'csp',
239+
'hpkp',
240+
'expectct',
241+
'expectstaple',
242+
'default',
243+
'transaction'
244+
];
245+
246+
// TODO(ts): incomplete
247+
export type Group = {
248+
id: string;
249+
annotations: string[];
250+
assignedTo: User;
251+
count: string;
252+
culprit: string;
253+
firstSeen: string;
254+
hasSeen: boolean;
255+
isBookmarked: boolean;
256+
isPublic: boolean;
257+
isSubscribed: boolean;
258+
lastSeen: string;
259+
level: string;
260+
logger: string;
261+
metadata: Metadata;
262+
numComments: number;
263+
permalink: string;
264+
project: {
265+
name: string;
266+
slug: string;
267+
};
268+
shareId: string;
269+
shortId: string;
270+
status: string;
271+
statusDetails: {};
272+
title: string;
273+
type: EventOrGroupType;
274+
userCount: number;
275+
seenBy: User[];
276+
};
277+
278+
export type EventView = {
279+
id: string;
280+
name: string;
281+
data: {
282+
fields: string[];
283+
columnNames: string[];
284+
sort: string[];
285+
query?: string;
286+
287+
// TODO: removed as of https://github.com/getsentry/sentry/pull/14321
288+
// groupby: string[];
289+
// orderby: string[];
290+
};
291+
tags: string[];
292+
columnWidths: string[];
293+
};

src/sentry/static/sentry/app/utils.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,7 @@ export function isWebpackChunkLoadingError(error: Error): boolean {
238238
);
239239
}
240240

241-
export function deepFreeze(object: {[x: string]: any}) {
241+
export function deepFreeze<T>(object: T) {
242242
// Retrieve the property names defined on object
243243
const propNames = Object.getOwnPropertyNames(object);
244244
// Freeze properties before freezing self

src/sentry/static/sentry/app/views/events/utils/eventsRequest.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -318,7 +318,7 @@ class EventsRequest extends React.PureComponent<EventsRequestProps, EventsReques
318318
? this.transformPreviousPeriodData(current, previous)
319319
: null;
320320
const timeAggregatedData = includeTimeAggregation
321-
? this.transformAggregatedTimeseries(current, timeAggregationSeriesName)
321+
? this.transformAggregatedTimeseries(current, timeAggregationSeriesName || '')
322322
: {};
323323
return {
324324
data: transformedData,

src/sentry/static/sentry/app/views/organizationEventsV2/data.jsx renamed to src/sentry/static/sentry/app/views/organizationEventsV2/data.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,19 @@ import getDynamicText from 'app/utils/getDynamicText';
1212
import overflowEllipsis from 'app/styles/overflowEllipsis';
1313
import pinIcon from 'app/../images/location-pin.png';
1414
import space from 'app/styles/space';
15+
import {EventView} from 'app/types';
1516

1617
import {QueryLink} from './styles';
1718

19+
// TODO(ts): add as const after babel upgrade
1820
export const MODAL_QUERY_KEYS = ['eventSlug'];
1921
export const PIN_ICON = `image://${pinIcon}`;
22+
// TODO(ts): add as const after babel upgrade
2023
export const AGGREGATE_ALIASES = ['last_seen', 'latest_event'];
2124

22-
export const ALL_VIEWS = deepFreeze([
25+
// TODO(ts): eventually defer to TS compile-time check to ensure this is readonly instead
26+
// of deepfreezing it in runtime
27+
export const ALL_VIEWS: Readonly<Array<EventView>> = deepFreeze([
2328
{
2429
id: 'all',
2530
name: t('All Events'),

src/sentry/static/sentry/app/views/organizationEventsV2/eventDetails.jsx renamed to src/sentry/static/sentry/app/views/organizationEventsV2/eventDetails.tsx

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import React from 'react';
22
import {browserHistory} from 'react-router';
3+
import {Location} from 'history';
4+
import {Params} from 'react-router/lib/Router';
35
import PropTypes from 'prop-types';
46
import {omit} from 'lodash';
57
import {css} from 'react-emotion';
@@ -11,15 +13,20 @@ import NotFound from 'app/components/errors/notFound';
1113
import withApi from 'app/utils/withApi';
1214
import theme from 'app/utils/theme';
1315
import space from 'app/styles/space';
16+
import {Organization, EventView, Event} from 'app/types';
1417

1518
import EventModalContent from './eventModalContent';
16-
import {getQuery} from './utils';
19+
import {EventQuery, getQuery} from './utils';
1720

18-
const slugValidator = function(props, propName, componentName) {
21+
const slugValidator = function(
22+
props: {[key: string]: any},
23+
propName: string,
24+
componentName: string
25+
) {
1926
const value = props[propName];
2027
// Accept slugs that look like:
2128
// * project-slug:deadbeef
22-
if (value && !/^(?:[^:]+):(?:[a-f0-9]+)$/.test(value)) {
29+
if (value && typeof value === 'string' && !/^(?:[^:]+):(?:[a-f0-9]+)$/.test(value)) {
2330
return new Error(`Invalid value for ${propName} provided to ${componentName}.`);
2431
}
2532
return null;
@@ -38,15 +45,27 @@ const modalStyles = css`
3845
}
3946
`;
4047

41-
class EventDetails extends AsyncComponent {
42-
static propTypes = {
48+
type Props = {
49+
organization: Organization;
50+
location: Location;
51+
eventSlug: string;
52+
view: EventView;
53+
params: Params;
54+
};
55+
56+
type State = {
57+
event: Event;
58+
};
59+
60+
class EventDetails extends AsyncComponent<Props, State & AsyncComponent['state']> {
61+
static propTypes: any = {
4362
organization: SentryTypes.Organization.isRequired,
4463
eventSlug: slugValidator,
4564
location: PropTypes.object.isRequired,
4665
view: PropTypes.object.isRequired,
4766
};
4867

49-
getEndpoints() {
68+
getEndpoints(): Array<[string, string, {query: EventQuery}]> {
5069
const {organization, eventSlug, view, location} = this.props;
5170
const query = getQuery(view, location);
5271
const url = `/organizations/${organization.slug}/events/${eventSlug}/`;
@@ -78,7 +97,6 @@ class EventDetails extends AsyncComponent {
7897
return (
7998
<ModalDialog onDismiss={this.onDismiss} className={modalStyles}>
8099
<EventModalContent
81-
onTabChange={this.handleTabChange}
82100
event={event}
83101
projectId={this.projectId}
84102
organization={organization}

src/sentry/static/sentry/app/views/organizationEventsV2/eventInterfaces.jsx renamed to src/sentry/static/sentry/app/views/organizationEventsV2/eventInterfaces.tsx

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,24 +11,31 @@ import EventExtraData from 'app/components/events/extraData';
1111
import EventPackageData from 'app/components/events/packageData';
1212
import NavTabs from 'app/components/navTabs';
1313
import {objectIsEmpty, toTitleCase} from 'app/utils';
14+
import {Event} from 'app/types';
1415

1516
const OTHER_SECTIONS = {
1617
context: EventExtraData,
1718
packages: EventPackageData,
1819
device: EventDevice,
1920
};
2021

22+
type ActiveTabProps = {
23+
projectId: string;
24+
event: Event;
25+
activeTab: string;
26+
};
27+
2128
/**
2229
* Render the currently active event interface tab.
2330
* Some but not all interface elements require a projectId.
2431
*/
25-
const ActiveTab = props => {
32+
const ActiveTab = (props: ActiveTabProps) => {
2633
const {projectId, event, activeTab} = props;
2734
if (!activeTab) {
2835
return null;
2936
}
3037
const entry = event.entries.find(item => item.type === activeTab);
31-
if (INTERFACES[activeTab]) {
38+
if (INTERFACES[activeTab] && entry) {
3239
const Component = INTERFACES[activeTab];
3340
return (
3441
<Component
@@ -50,7 +57,11 @@ const ActiveTab = props => {
5057
console.error('Unregistered interface: ' + activeTab);
5158

5259
return (
53-
<EventDataSection event={event} type={entry.type} title={entry.type}>
60+
<EventDataSection
61+
event={event}
62+
type={entry && entry.type}
63+
title={entry && entry.type}
64+
>
5465
<p>{t('There was an error rendering this data.')}</p>
5566
</EventDataSection>
5667
);
@@ -62,13 +73,24 @@ ActiveTab.propTypes = {
6273
projectId: PropTypes.string.isRequired,
6374
};
6475

65-
class EventInterfaces extends React.Component {
76+
type EventInterfacesProps = {
77+
event: Event;
78+
projectId: string;
79+
};
80+
type EventInterfacesState = {
81+
activeTab: string;
82+
};
83+
84+
class EventInterfaces extends React.Component<
85+
EventInterfacesProps,
86+
EventInterfacesState
87+
> {
6688
static propTypes = {
6789
event: SentryTypes.Event.isRequired,
6890
projectId: PropTypes.string.isRequired,
6991
};
7092

71-
constructor(props) {
93+
constructor(props: EventInterfacesProps) {
7294
super(props);
7395
this.state = {
7496
activeTab: props.event.entries[0].type,
@@ -89,7 +111,7 @@ class EventInterfaces extends React.Component {
89111
return null;
90112
}
91113
const type = entry.type;
92-
const classname = type === activeTab ? 'active' : null;
114+
const classname = type === activeTab ? 'active' : undefined;
93115
return (
94116
<li key={type} className={classname}>
95117
<a
@@ -108,7 +130,7 @@ class EventInterfaces extends React.Component {
108130
if (objectIsEmpty(event[section])) {
109131
return null;
110132
}
111-
const classname = section === activeTab ? 'active' : null;
133+
const classname = section === activeTab ? 'active' : undefined;
112134
return (
113135
<li key={section} className={classname}>
114136
<a

0 commit comments

Comments
 (0)