@faststore/api 3.33.3 → 3.38.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,264 +0,0 @@
1
- import type { OnExecuteHookResult, Plugin } from '@envelop/core'
2
- import { isAsyncIterable } from '@envelop/core'
3
- import { useOnResolve } from '@envelop/on-resolve'
4
- import {
5
- type Context,
6
- context as openTelContext,
7
- type Span,
8
- SpanKind,
9
- trace as openTelTrace,
10
- } from '@opentelemetry/api'
11
- import type { LogRecord } from '@opentelemetry/api-logs'
12
- import { SeverityNumber } from '@opentelemetry/api-logs'
13
- import type { LoggerProvider } from '@opentelemetry/sdk-logs'
14
- import type { BasicTracerProvider } from '@opentelemetry/sdk-trace-base'
15
- import {
16
- Kind,
17
- type OperationDefinitionNode,
18
- print,
19
- type DefinitionNode,
20
- } from 'graphql'
21
- import type { Path } from 'graphql/jsutils/Path'
22
-
23
- export enum AttributeName {
24
- EXECUTION_ERROR = 'graphql.error',
25
- EXECUTION_RESULT = 'graphql.result',
26
- RESOLVER_EXECUTION_ERROR = 'graphql.resolver.error',
27
- RESOLVER_EXCEPTION = 'graphql.resolver.exception',
28
- RESOLVER_FIELD_NAME = 'graphql.resolver.fieldName',
29
- RESOLVER_TYPE_NAME = 'graphql.resolver.typeName',
30
- RESOLVER_RESULT_TYPE = 'graphql.resolver.resultType',
31
- RESOLVER_ARGS = 'graphql.resolver.args',
32
- EXECUTION_OPERATION_NAME = 'graphql.operation.name',
33
- EXECUTION_OPERATION_TYPE = 'graphql.operation.type',
34
- EXECUTION_OPERATION_DOCUMENT = 'graphql.document',
35
- EXECUTION_VARIABLES = 'graphql.variables',
36
- }
37
-
38
- const tracingSpanSymbol = Symbol('OPEN_TELEMETRY_GRAPHQL')
39
-
40
- export type PluginContext = {
41
- [tracingSpanSymbol]: Span
42
- }
43
-
44
- function getResolverSpanKey(path: Path) {
45
- const nodes = []
46
-
47
- // If the first node (after reversed, it will be the last one) is an integer, that is, identifies a list,
48
- // we don't want to include it in the key. Note that this will only happen when analysing .prev paths in
49
- // a list of elements. We just want to remove the initial node that is a integer, not all of them.
50
- //
51
- // Nodes with keys 6bc73341b2a183fc::product::image::0::url would not be able to find
52
- // parents with key 6bc73341b2a183fc::product::image because of the "0" list index -
53
- // it would search for 6bc73341b2a183fc::product::image::0
54
- let currentPath: Path | undefined =
55
- nodes.length === 0 && Number.isInteger(path.key) ? path.prev : path
56
-
57
- while (currentPath) {
58
- nodes.push(currentPath.key)
59
-
60
- currentPath = currentPath.prev
61
- }
62
-
63
- return [...nodes].reverse().join('.')
64
- }
65
-
66
- export const getFaststoreTelemetryPlugin = (
67
- tracingProvider: BasicTracerProvider,
68
- loggerProvider: LoggerProvider,
69
- serviceName: string,
70
- experimentalSendLogs: boolean
71
- ): (() => Plugin<PluginContext>) => {
72
- return function useFaststoreTelemetry() {
73
- const tracer = tracingProvider.getTracer(serviceName)
74
- const logger = loggerProvider.getLogger(serviceName)
75
-
76
- const resolverContextsByRootSpans: Record<
77
- string,
78
- Record<string, Context>
79
- > = {}
80
-
81
- return {
82
- onPluginInit({ addPlugin }) {
83
- addPlugin(
84
- useOnResolve(({ info, context }) => {
85
- if (
86
- context &&
87
- typeof context === 'object' &&
88
- context[tracingSpanSymbol]
89
- ) {
90
- tracer.getActiveSpanProcessor()
91
- const rootContextSpanId =
92
- context[tracingSpanSymbol].spanContext().spanId
93
-
94
- const { fieldName, returnType, parentType, path } = info
95
-
96
- const previousResolverSpanKey =
97
- path.prev && getResolverSpanKey(path.prev)
98
-
99
- let ctx: Context | null = null
100
-
101
- if (
102
- previousResolverSpanKey &&
103
- resolverContextsByRootSpans[rootContextSpanId][
104
- previousResolverSpanKey
105
- ]
106
- ) {
107
- ctx =
108
- resolverContextsByRootSpans[rootContextSpanId][
109
- previousResolverSpanKey
110
- ]
111
- } else {
112
- ctx = openTelTrace.setSpan(
113
- openTelContext.active(),
114
- context[tracingSpanSymbol]
115
- )
116
-
117
- resolverContextsByRootSpans[rootContextSpanId] =
118
- resolverContextsByRootSpans[rootContextSpanId] ?? {}
119
- }
120
-
121
- const resolverIndexInList = Number.isInteger(path.prev?.key)
122
- ? `[${path.prev?.key}]`
123
- : ''
124
-
125
- const resolverSpan = tracer.startSpan(
126
- `${parentType.toString()}.${fieldName}${resolverIndexInList}`,
127
- {
128
- attributes: {
129
- [AttributeName.RESOLVER_FIELD_NAME]: fieldName,
130
- [AttributeName.RESOLVER_TYPE_NAME]: parentType.toString(),
131
- [AttributeName.RESOLVER_RESULT_TYPE]: returnType.toString(),
132
- 'meta.span.path': getResolverSpanKey(path),
133
- },
134
- },
135
- ctx
136
- )
137
-
138
- const resolverCtx = openTelTrace.setSpan(ctx, resolverSpan)
139
-
140
- resolverContextsByRootSpans[rootContextSpanId][
141
- getResolverSpanKey(path)
142
- ] = resolverCtx
143
-
144
- return ({ result }) => {
145
- if (result instanceof Error) {
146
- resolverSpan.setAttributes({
147
- error: true,
148
- 'exception.category':
149
- AttributeName.RESOLVER_EXECUTION_ERROR,
150
- 'exception.message': result.message,
151
- 'exception.type': result.name,
152
- })
153
- resolverSpan.recordException(result)
154
- }
155
-
156
- resolverSpan.end()
157
- }
158
- }
159
-
160
- return () => {}
161
- })
162
- )
163
- },
164
- onExecute({ args, extendContext }) {
165
- const operationType = args.document.definitions
166
- .filter(
167
- (def: DefinitionNode) => def.kind === Kind.OPERATION_DEFINITION
168
- )
169
- .map(
170
- (def: DefinitionNode) => (def as OperationDefinitionNode).operation
171
- )?.[0]
172
-
173
- // Span name according to Semantic Conventions
174
- // https://github.com/open-telemetry/semantic-conventions
175
- let spanName = 'GraphQL Operation'
176
-
177
- if (operationType && args.operationName) {
178
- spanName = `${operationType} ${args.operationName}`
179
- } else if (operationType && !args.operationName) {
180
- spanName = operationType
181
- }
182
-
183
- const executionSpan = tracer.startSpan(spanName, {
184
- kind: SpanKind.SERVER,
185
- attributes: {
186
- [AttributeName.EXECUTION_OPERATION_NAME]:
187
- args.operationName ?? undefined,
188
- [AttributeName.EXECUTION_OPERATION_TYPE]:
189
- operationType ?? undefined,
190
- [AttributeName.EXECUTION_OPERATION_DOCUMENT]: print(args.document),
191
- },
192
- })
193
-
194
- const executeContext = openTelContext.active()
195
-
196
- const resultCbs: OnExecuteHookResult<PluginContext> = {
197
- onExecuteDone({ result }) {
198
- if (isAsyncIterable(result)) {
199
- executionSpan.end()
200
- console.warn(
201
- `Plugin "newrelic" encountered a AsyncIterator which is not supported yet, so tracing data is not available for the operation.`
202
- )
203
-
204
- return
205
- }
206
-
207
- const logRecord: LogRecord = {
208
- context: executeContext,
209
- attributes: {
210
- 'service.name': 'faststore-api',
211
- 'service.version': '1.12.38',
212
- 'service.name_and_version': 'faststore-api@1.12.38',
213
- 'vtex.search_index': 'faststore_beta_api',
214
- [AttributeName.EXECUTION_OPERATION_NAME]:
215
- args.operationName ?? undefined,
216
- [AttributeName.EXECUTION_OPERATION_DOCUMENT]: print(
217
- args.document
218
- ),
219
- [AttributeName.EXECUTION_VARIABLES]: JSON.stringify(
220
- args.variableValues ?? {}
221
- ),
222
- },
223
- }
224
-
225
- if (
226
- typeof result.data !== 'undefined' &&
227
- !(result.errors && result.errors.length > 0)
228
- ) {
229
- logRecord.severityNumber = SeverityNumber.INFO
230
- logRecord.severityText = 'Info'
231
- logRecord.attributes![AttributeName.EXECUTION_RESULT] =
232
- JSON.stringify(result)
233
- }
234
-
235
- if (result.errors && result.errors.length > 0) {
236
- logRecord.severityNumber = SeverityNumber.ERROR
237
- logRecord.severityText = 'Error'
238
- logRecord.attributes![AttributeName.EXECUTION_ERROR] =
239
- JSON.stringify(result.errors)
240
-
241
- executionSpan.setAttributes({
242
- error: true,
243
- 'exception.category': AttributeName.EXECUTION_ERROR,
244
- 'exception.message': JSON.stringify(result.errors),
245
- })
246
- }
247
-
248
- if (experimentalSendLogs) {
249
- logger.emit(logRecord)
250
- }
251
-
252
- executionSpan.end()
253
- },
254
- }
255
-
256
- extendContext({
257
- [tracingSpanSymbol]: executionSpan,
258
- })
259
-
260
- return resultCbs
261
- },
262
- }
263
- }
264
- }