import {type ReactNode, useMemo} from 'react'; import {ClassNames} from '@emotion/react'; import styled from '@emotion/styled'; import {Button, LinkButton} from 'sentry/components/button'; import {Hovercard} from 'sentry/components/hovercard'; import {platformsWithNestedInstrumentationGuides} from 'sentry/data/platformCategories'; import {IconOpen, IconQuestion} from 'sentry/icons'; import {t} from 'sentry/locale'; import {space} from 'sentry/styles/space'; import type {EventTransaction} from 'sentry/types/event'; import type {Project} from 'sentry/types/project'; import type {UseApiQueryResult} from 'sentry/utils/queryClient'; import type RequestError from 'sentry/utils/requestError/requestError'; import useOrganization from 'sentry/utils/useOrganization'; import useProjects from 'sentry/utils/useProjects'; import {traceAnalytics} from './traceAnalytics'; function Resource({ title, subtitle, link, }: { link: string; subtitle: ReactNode; title: string; }) { const organization = useOrganization(); return ( } borderless external href={link} onClick={() => { traceAnalytics.trackTraceConfigurationsDocsClicked(organization, title); }} > {title} {subtitle} ); } type ParsedPlatform = { platformName: string; framework?: string; }; function parsePlatform(platform: string): ParsedPlatform { const platformParts = platform.split('-'); // For example: dotnet-google-cloud-functions, we want to split it into // platformName: dotnet, framework: google-cloud-functions if (platformParts.length >= 3) { return {platformName: platformParts[0], framework: platformParts.slice(1).join('-')}; } // With some exceptions, all other project platforms have the following two structures: // 1. "{language}-{framework}", e.g. "javascript-nextjs" // 2. "{language}", e.g. "python" const [platformName, framework] = platformParts; if (platform === 'react-native') { return {platformName}; } if (platform.includes('awslambda')) { return {platformName, framework: 'aws-lambda'}; } if (platform.includes('gcpfunctions')) { return {platformName, framework: 'gcp-functions'}; } return {platformName, framework}; } export function getCustomInstrumentationLink(project: Project | undefined): string { // Default to JavaScript guide if project or platform is not available if (!project || !project.platform) { return `https://docs.sentry.io/platforms/javascript/tracing/instrumentation/custom-instrumentation/`; } const {platformName, framework} = parsePlatform(project.platform); return platformsWithNestedInstrumentationGuides.includes(project.platform) && framework ? `https://docs.sentry.io/platforms/${platformName}/guides/${framework}/tracing/instrumentation/custom-instrumentation/` : `https://docs.sentry.io/platforms/${platformName}/tracing/instrumentation/custom-instrumentation/`; } function getDistributedTracingLink(project: Project | undefined): string { // Default to JavaScript guide if project or platform is not available if (!project || !project.platform) { return `https://docs.sentry.io/platforms/javascript/tracing/trace-propagation/`; } const {platformName, framework} = parsePlatform(project.platform); return framework ? `https://docs.sentry.io/platforms/${platformName}/guides/${framework}/tracing/trace-propagation/` : `https://docs.sentry.io/platforms/${platformName}/tracing/trace-propagation/`; } type ResourceButtonsProps = { customInstrumentationLink: string; distributedTracingLink: string; }; function ResourceButtons({ customInstrumentationLink, distributedTracingLink, }: ResourceButtonsProps) { return ( ); } type TraceConfigurationsProps = { rootEventResults: UseApiQueryResult; }; export default function TraceConfigurations({ rootEventResults, }: TraceConfigurationsProps) { const {projects} = useProjects(); const traceProject = useMemo(() => { return rootEventResults.data ? projects.find(p => p.id === rootEventResults.data.projectID) : undefined; }, [projects, rootEventResults.data]); const customInstrumentationLink = useMemo( () => getCustomInstrumentationLink(traceProject), [traceProject] ); const distributedTracingLink = useMemo( () => getDistributedTracingLink(traceProject), [traceProject] ); return ( {({css}) => ( } bodyClassName={css` padding: ${space(1)}; `} position="top-end" > )} ); } const ButtonContainer = styled('div')` display: flex; flex-direction: column; gap: ${space(1)}; align-items: flex-start; `; const ButtonContent = styled('div')` display: flex; flex-direction: column; text-align: left; white-space: pre-line; gap: ${space(0.25)}; `; const ButtonTitle = styled('div')` font-weight: ${p => p.theme.fontWeightNormal}; `; const ButtonSubtitle = styled('div')` color: ${p => p.theme.gray300}; font-weight: ${p => p.theme.fontWeightNormal}; font-size: ${p => p.theme.fontSizeSmall}; `; const StyledLinkButton = styled(LinkButton)` padding: ${space(1)}; height: auto; `;