import {Fragment, useState} from 'react';
import styled from '@emotion/styled';
import ClippedBox from 'sentry/components/clippedBox';
import ErrorBoundary from 'sentry/components/errorBoundary';
import {EventDataSection} from 'sentry/components/events/eventDataSection';
import {GraphQlRequestBody} from 'sentry/components/events/interfaces/request/graphQlRequestBody';
import {getCurlCommand, getFullUrl} from 'sentry/components/events/interfaces/utils';
import ExternalLink from 'sentry/components/links/externalLink';
import {SegmentedControl} from 'sentry/components/segmentedControl';
import Truncate from 'sentry/components/truncate';
import {IconOpen} from 'sentry/icons';
import {t} from 'sentry/locale';
import {space} from 'sentry/styles/space';
import {EntryRequest, EntryType, Event} from 'sentry/types/event';
import {defined, isUrl} from 'sentry/utils';
import {RichHttpContentClippedBoxBodySection} from './richHttpContentClippedBoxBodySection';
import {RichHttpContentClippedBoxKeyValueList} from './richHttpContentClippedBoxKeyValueList';
interface RequestProps {
data: EntryRequest['data'];
event: Event;
}
interface RequestBodyProps extends RequestProps {
meta: any;
}
type View = 'formatted' | 'curl';
function RequestBodySection({data, event, meta}: RequestBodyProps) {
if (!defined(data.data)) {
return null;
}
if (data.apiTarget === 'graphql' && typeof data.data.query === 'string') {
return ;
}
return (
);
}
export function Request({data, event}: RequestProps) {
const entryIndex = event.entries.findIndex(entry => entry.type === EntryType.REQUEST);
const meta = event._meta?.entries?.[entryIndex]?.data;
const [view, setView] = useState('formatted');
const isPartial =
// We assume we only have a partial interface is we're missing
// an HTTP method. This means we don't have enough information
// to reliably construct a full HTTP request.
!data.method || !data.url;
let fullUrl = getFullUrl(data);
if (!isUrl(fullUrl)) {
// Check if the url passed in is a safe url to avoid XSS
fullUrl = undefined;
}
let parsedUrl: HTMLAnchorElement | null = null;
if (fullUrl) {
// use html tag to parse url, lol
parsedUrl = document.createElement('a');
parsedUrl.href = fullUrl;
}
let actions: React.ReactNode = null;
if (!isPartial && fullUrl) {
actions = (
{/* Translators: this means "formatted" rendering (fancy tables) */}
{t('Formatted')}
curl
);
}
const title = (
{data.method || 'GET'}
{fullUrl && }
{parsedUrl ? parsedUrl.hostname : ''}
);
return (
{view === 'curl' ? (
{getCurlCommand(data)}
) : (
{defined(data.query) && (
)}
{defined(data.fragment) && (
{data.fragment}
)}
{defined(data.cookies) && Object.keys(data.cookies).length > 0 && (
)}
{defined(data.headers) && (
)}
{defined(data.env) && (
)}
)}
);
}
const Monospace = styled('span')`
font-family: ${p => p.theme.text.familyMono};
`;
const Path = styled('span')`
color: ${p => p.theme.textColor};
text-transform: none;
font-weight: normal;
& strong {
margin-right: ${space(0.5)};
}
`;
// Nudge the icon down so it is centered. the `external-icon` class
// doesn't quite get it in place.
const StyledIconOpen = styled(IconOpen)`
transition: 0.1s linear color;
margin: 0 ${space(0.5)};
color: ${p => p.theme.subText};
position: relative;
top: 1px;
&:hover {
color: ${p => p.theme.textColor};
}
`;