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}; } `;