{"version":3,"file":"index.js","sources":["../../../../../src/integrations/tracing/graphql/index.ts"],"sourcesContent":["import type { AttributeValue } from '@opentelemetry/api';\nimport { SpanStatusCode } from '@opentelemetry/api';\nimport { GraphQLInstrumentation } from './vendored/instrumentation';\nimport type { IntegrationFn } from '@sentry/core';\nimport { defineIntegration, getRootSpan, spanToJSON } from '@sentry/core';\nimport { addOriginToSpan, generateInstrumentOnce } from '@sentry/node-core';\nimport { SEMANTIC_ATTRIBUTE_SENTRY_GRAPHQL_OPERATION } from '@sentry/opentelemetry';\n\ninterface GraphqlOptions {\n /**\n * Do not create spans for resolvers.\n *\n * Defaults to true.\n */\n ignoreResolveSpans?: boolean;\n\n /**\n * Don't create spans for the execution of the default resolver on object properties.\n *\n * When a resolver function is not defined on the schema for a field, graphql will\n * use the default resolver which just looks for a property with that name on the object.\n * If the property is not a function, it's not very interesting to trace.\n * This option can reduce noise and number of spans created.\n *\n * Defaults to true.\n */\n ignoreTrivialResolveSpans?: boolean;\n\n /**\n * If this is enabled, a http.server root span containing this span will automatically be renamed to include the operation name.\n * Set this to `false` if you do not want this behavior, and want to keep the default http.server span name.\n *\n * Defaults to true.\n */\n useOperationNameForRootSpan?: boolean;\n}\n\nconst INTEGRATION_NAME = 'Graphql';\n\nexport const instrumentGraphql = generateInstrumentOnce(\n INTEGRATION_NAME,\n GraphQLInstrumentation,\n (_options: GraphqlOptions) => {\n const options = getOptionsWithDefaults(_options);\n\n return {\n ...options,\n responseHook(span, result) {\n addOriginToSpan(span, 'auto.graphql.otel.graphql');\n\n // We want to ensure spans are marked as errored if there are errors in the result\n // We only do that if the span is not already marked with a status\n const resultWithMaybeError = result as { errors?: { message: string }[] };\n if (resultWithMaybeError.errors?.length && !spanToJSON(span).status) {\n span.setStatus({ code: SpanStatusCode.ERROR });\n }\n\n const attributes = spanToJSON(span).data;\n\n // If operation.name is not set, we fall back to use operation.type only\n const operationType = attributes['graphql.operation.type'];\n const operationName = attributes['graphql.operation.name'];\n\n if (options.useOperationNameForRootSpan && operationType) {\n const rootSpan = getRootSpan(span);\n const rootSpanAttributes = spanToJSON(rootSpan).data;\n\n const existingOperations = rootSpanAttributes[SEMANTIC_ATTRIBUTE_SENTRY_GRAPHQL_OPERATION] || [];\n\n const newOperation = operationName ? `${operationType} ${operationName}` : `${operationType}`;\n\n // We keep track of each operation on the root span\n // This can either be a string, or an array of strings (if there are multiple operations)\n if (Array.isArray(existingOperations)) {\n (existingOperations as string[]).push(newOperation);\n rootSpan.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_GRAPHQL_OPERATION, existingOperations);\n } else if (typeof existingOperations === 'string') {\n rootSpan.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_GRAPHQL_OPERATION, [existingOperations, newOperation]);\n } else {\n rootSpan.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_GRAPHQL_OPERATION, newOperation);\n }\n\n if (!spanToJSON(rootSpan).data['original-description']) {\n rootSpan.setAttribute('original-description', spanToJSON(rootSpan).description);\n }\n // Important for e.g. @sentry/aws-serverless because this would otherwise overwrite the name again\n rootSpan.updateName(\n `${spanToJSON(rootSpan).data['original-description']} (${getGraphqlOperationNamesFromAttribute(\n existingOperations,\n )})`,\n );\n }\n },\n };\n },\n);\n\nconst _graphqlIntegration = ((options: GraphqlOptions = {}) => {\n return {\n name: INTEGRATION_NAME,\n setupOnce() {\n // We set defaults here, too, because otherwise we'd update the instrumentation config\n // to the config without defaults, as `generateInstrumentOnce` automatically calls `setConfig(options)`\n // when being called the second time\n instrumentGraphql(getOptionsWithDefaults(options));\n },\n };\n}) satisfies IntegrationFn;\n\n/**\n * Adds Sentry tracing instrumentation for the [graphql](https://www.npmjs.com/package/graphql) library.\n *\n * For more information, see the [`graphqlIntegration` documentation](https://docs.sentry.io/platforms/javascript/guides/node/configuration/integrations/graphql/).\n *\n * @param {GraphqlOptions} options Configuration options for the GraphQL integration.\n *\n * @example\n * ```javascript\n * const Sentry = require('@sentry/node');\n *\n * Sentry.init({\n * integrations: [Sentry.graphqlIntegration()],\n * });\n */\nexport const graphqlIntegration = defineIntegration(_graphqlIntegration);\n\nfunction getOptionsWithDefaults(options?: GraphqlOptions): GraphqlOptions {\n return {\n ignoreResolveSpans: true,\n ignoreTrivialResolveSpans: true,\n useOperationNameForRootSpan: true,\n ...options,\n };\n}\n\n// copy from packages/opentelemetry/utils\nfunction getGraphqlOperationNamesFromAttribute(attr: AttributeValue): string {\n if (Array.isArray(attr)) {\n // oxlint-disable-next-line typescript/require-array-sort-compare\n const sorted = attr.slice().sort();\n\n // Up to 5 items, we just add all of them\n if (sorted.length <= 5) {\n return sorted.join(', ');\n } else {\n // Else, we add the first 5 and the diff of other operations\n return `${sorted.slice(0, 5).join(', ')}, +${sorted.length - 5}`;\n }\n }\n\n return `${attr}`;\n}\n"],"names":[],"mappings":"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"}