|
@@ -1,206 +0,0 @@
|
|
|
-import {Component} from 'react';
|
|
|
-import styled from '@emotion/styled';
|
|
|
-import * as Sentry from '@sentry/react';
|
|
|
-import isEqual from 'lodash/isEqual';
|
|
|
-import uniqWith from 'lodash/uniqWith';
|
|
|
-
|
|
|
-import {Client} from 'sentry/api';
|
|
|
-import {Alert} from 'sentry/components/alert';
|
|
|
-import {ErrorItem, ErrorItemProps} from 'sentry/components/events/errorItem';
|
|
|
-import List from 'sentry/components/list';
|
|
|
-import {JavascriptProcessingErrors} from 'sentry/constants/eventErrors';
|
|
|
-import {t, tn} from 'sentry/locale';
|
|
|
-import space from 'sentry/styles/space';
|
|
|
-import {Artifact, Organization, Project} from 'sentry/types';
|
|
|
-import {Event} from 'sentry/types/event';
|
|
|
-import withApi from 'sentry/utils/withApi';
|
|
|
-
|
|
|
-import {DataSection} from './styles';
|
|
|
-
|
|
|
-const MAX_ERRORS = 100;
|
|
|
-
|
|
|
-export type Error = ErrorItemProps['error'];
|
|
|
-
|
|
|
-type Props = {
|
|
|
- api: Client;
|
|
|
- event: Event;
|
|
|
- orgSlug: Organization['slug'];
|
|
|
- proGuardErrors: Array<Error>;
|
|
|
- projectSlug: Project['slug'];
|
|
|
-};
|
|
|
-
|
|
|
-type State = {
|
|
|
- releaseArtifacts?: Array<Artifact>;
|
|
|
-};
|
|
|
-
|
|
|
-class Errors extends Component<Props, State> {
|
|
|
- state: State = {};
|
|
|
-
|
|
|
- componentDidMount() {
|
|
|
- this.checkSourceCodeErrors();
|
|
|
- }
|
|
|
-
|
|
|
- shouldComponentUpdate(nextProps: Props) {
|
|
|
- return this.props.event.id !== nextProps.event.id;
|
|
|
- }
|
|
|
-
|
|
|
- componentDidUpdate(prevProps: Props) {
|
|
|
- if (this.props.event.id !== prevProps.event.id) {
|
|
|
- this.checkSourceCodeErrors();
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- async fetchReleaseArtifacts(query: string) {
|
|
|
- const {api, orgSlug, event, projectSlug} = this.props;
|
|
|
- const {release} = event;
|
|
|
- const releaseVersion = release?.version;
|
|
|
-
|
|
|
- if (!releaseVersion || !query) {
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- try {
|
|
|
- const releaseArtifacts = await api.requestPromise(
|
|
|
- `/projects/${orgSlug}/${projectSlug}/releases/${encodeURIComponent(
|
|
|
- releaseVersion
|
|
|
- )}/files/?query=${query}`,
|
|
|
- {
|
|
|
- method: 'GET',
|
|
|
- }
|
|
|
- );
|
|
|
-
|
|
|
- this.setState({releaseArtifacts});
|
|
|
- } catch (error) {
|
|
|
- Sentry.captureException(error);
|
|
|
- // do nothing, the UI will not display extra error details
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- checkSourceCodeErrors() {
|
|
|
- const {event} = this.props;
|
|
|
- const {errors} = event;
|
|
|
-
|
|
|
- const sourceCodeErrors = (errors ?? []).filter(
|
|
|
- error => error.type === 'js_no_source' && error.data.url
|
|
|
- );
|
|
|
-
|
|
|
- if (!sourceCodeErrors.length) {
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- const pathNames: Array<string> = [];
|
|
|
-
|
|
|
- for (const sourceCodeError of sourceCodeErrors) {
|
|
|
- const url = sourceCodeError.data.url;
|
|
|
- if (url) {
|
|
|
- const pathName = this.getURLPathname(url);
|
|
|
-
|
|
|
- if (pathName) {
|
|
|
- pathNames.push(encodeURIComponent(pathName));
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- this.fetchReleaseArtifacts(pathNames.join('&query='));
|
|
|
- }
|
|
|
-
|
|
|
- getURLPathname(url: string) {
|
|
|
- try {
|
|
|
- return new URL(url).pathname;
|
|
|
- } catch {
|
|
|
- return undefined;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- render() {
|
|
|
- const {event, proGuardErrors} = this.props;
|
|
|
- const {releaseArtifacts} = this.state;
|
|
|
- const {dist: eventDistribution, errors: eventErrors = [], _meta} = event;
|
|
|
-
|
|
|
- // XXX: uniqWith returns unique errors and is not performant with large datasets
|
|
|
- const otherErrors: Array<Error> =
|
|
|
- eventErrors.length > MAX_ERRORS ? eventErrors : uniqWith(eventErrors, isEqual);
|
|
|
-
|
|
|
- const errors = [...otherErrors, ...proGuardErrors];
|
|
|
-
|
|
|
- return (
|
|
|
- <StyledDataSection>
|
|
|
- <StyledAlert
|
|
|
- type="error"
|
|
|
- showIcon
|
|
|
- data-test-id="event-error-alert"
|
|
|
- expand={[
|
|
|
- <ErrorList
|
|
|
- key="event-error-details"
|
|
|
- data-test-id="event-error-details"
|
|
|
- symbol="bullet"
|
|
|
- >
|
|
|
- {errors.map((error, errorIdx) => {
|
|
|
- const data = error.data ?? {};
|
|
|
- const meta = _meta?.errors?.[errorIdx];
|
|
|
-
|
|
|
- if (
|
|
|
- error.type === JavascriptProcessingErrors.JS_MISSING_SOURCE &&
|
|
|
- data.url &&
|
|
|
- !!releaseArtifacts?.length
|
|
|
- ) {
|
|
|
- const releaseArtifact = releaseArtifacts.find(releaseArt => {
|
|
|
- const pathname = data.url ? this.getURLPathname(data.url) : undefined;
|
|
|
-
|
|
|
- if (pathname) {
|
|
|
- return releaseArt.name.includes(pathname);
|
|
|
- }
|
|
|
- return false;
|
|
|
- });
|
|
|
-
|
|
|
- const releaseArtifactDistribution = releaseArtifact?.dist ?? null;
|
|
|
-
|
|
|
- // Neither event nor file have dist -> matching
|
|
|
- // Event has dist, file doesn’t -> not matching
|
|
|
- // File has dist, event doesn’t -> not matching
|
|
|
- // Both have dist, same value -> matching
|
|
|
- // Both have dist, different values -> not matching
|
|
|
- if (releaseArtifactDistribution !== eventDistribution) {
|
|
|
- error.message = t(
|
|
|
- 'Source code was not found because the distribution did not match'
|
|
|
- );
|
|
|
- data['expected-distribution'] = eventDistribution;
|
|
|
- data['current-distribution'] = releaseArtifactDistribution;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- return <ErrorItem key={errorIdx} error={{...error, data}} meta={meta} />;
|
|
|
- })}
|
|
|
- </ErrorList>,
|
|
|
- ]}
|
|
|
- >
|
|
|
- {tn(
|
|
|
- 'There was %s problem processing this event',
|
|
|
- 'There were %s problems processing this event',
|
|
|
- errors.length
|
|
|
- )}
|
|
|
- </StyledAlert>
|
|
|
- </StyledDataSection>
|
|
|
- );
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-const StyledDataSection = styled(DataSection)`
|
|
|
- border-top: none;
|
|
|
-
|
|
|
- @media (min-width: ${p => p.theme.breakpoints.small}) {
|
|
|
- padding-top: 0;
|
|
|
- }
|
|
|
-`;
|
|
|
-
|
|
|
-const StyledAlert = styled(Alert)`
|
|
|
- margin: ${space(0.5)} 0;
|
|
|
-`;
|
|
|
-
|
|
|
-const ErrorList = styled(List)`
|
|
|
- li:last-child {
|
|
|
- margin-bottom: 0;
|
|
|
- }
|
|
|
-`;
|
|
|
-
|
|
|
-export const EventErrors = withApi(Errors);
|