@envelop/newrelic 4.0.0-alpha-4ba70ab.0 → 4.0.1-alpha-3ace273.0

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.
package/README.md CHANGED
@@ -44,7 +44,8 @@ const getEnveloped = envelop({
44
44
  trackResolvers: true, // default `false`. When set to `true`, track resolvers as segments to monitor their performance
45
45
  includeResolverArgs: false, // default `false`. When set to `true`, includes all the arguments passed to resolvers with their values
46
46
  rootFieldsNaming: true, // default `false`. When set to `true` append the names of operation root fields to the transaction name
47
- operationNameProperty: 'id', // default empty. When passed will check for the property name passed, within the document object. Will eventually use its value as operation name. Useful for custom document properties (e.g. queryId/hash)
47
+ skipError: error => { ... return true; }, // a function that allows you to skip reporting a given error to NewRelic. By default custom `EnvelopError`s will be skipped
48
+ extractOperationName: (context) => context.request.body.customOperationName, // Allows to set a custom operation name to be used as transaction name and attribute
48
49
  }),
49
50
  ],
50
51
  });
@@ -62,9 +63,9 @@ Below is a quick example of how you can use RegEx to set up white/black listing
62
63
 
63
64
  ```ts
64
65
  useNewRelic({
65
- includeExecuteVariables: /^(client.*|application.*)$.*$/i, // whitelist, track only variables whose name starts with client or application (e.g. clientName, applicationId)
66
+ includeExecuteVariables: /client|application/i, // whitelist, track only variables whose name contains "client" or "application" (e.g. clientName, applicationId, xApplicationId)
66
67
  trackResolvers: true, // track resolvers, since we also want to track resolvers' arguments
67
- includeResolverArgs: /^(?!(name|email|password)$).*$/, // blacklist, track all arguments whose name does not match 'name', 'email' nor 'password'
68
+ includeResolverArgs: /^(?!name|email|password).*/i, // blacklist, track all arguments whose name does not match 'name', 'email' nor 'password'
68
69
  }),
69
70
  ```
70
71
 
package/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
- import { Plugin } from '@envelop/core';
1
+ import { Plugin, DefaultContext } from '@envelop/core';
2
+ import { GraphQLError } from 'graphql';
2
3
  export declare type UseNewRelicOptions = {
3
4
  includeOperationDocument?: boolean;
4
5
  includeExecuteVariables?: boolean | RegExp;
@@ -6,12 +7,15 @@ export declare type UseNewRelicOptions = {
6
7
  trackResolvers?: boolean;
7
8
  includeResolverArgs?: boolean | RegExp;
8
9
  rootFieldsNaming?: boolean;
9
- operationNameProperty?: string;
10
10
  /**
11
- * Indicates whether or not to skip Sentry exception reporting for a given error.
12
- * By default, this plugin skips all `EnvelopError` errors and does not report it to Sentry.
11
+ * Function that returns a custom operation name to be used as transaction name and attribute
13
12
  */
14
- skipError?: (args: Error) => boolean;
13
+ extractOperationName?: (context: DefaultContext) => string | undefined;
14
+ /**
15
+ * Indicates whether or not to skip reporting a given error to NewRelic.
16
+ * By default, this plugin skips all `EnvelopError` errors and does not report them to NewRelic.
17
+ */
18
+ skipError?: (error: GraphQLError) => boolean;
15
19
  };
16
- export declare function defaultSkipError(error: Error): boolean;
20
+ export declare function defaultSkipError(error: GraphQLError): boolean;
17
21
  export declare const useNewRelic: (rawOptions?: UseNewRelicOptions | undefined) => Plugin;
package/index.js CHANGED
@@ -2,13 +2,28 @@
2
2
 
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
- function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
5
+ function _interopNamespace(e) {
6
+ if (e && e.__esModule) { return e; } else {
7
+ var n = {};
8
+ if (e) {
9
+ Object.keys(e).forEach(function (k) {
10
+ var d = Object.getOwnPropertyDescriptor(e, k);
11
+ Object.defineProperty(n, k, d.get ? d : {
12
+ enumerable: true,
13
+ get: function () {
14
+ return e[k];
15
+ }
16
+ });
17
+ });
18
+ }
19
+ n['default'] = e;
20
+ return n;
21
+ }
22
+ }
6
23
 
7
- const newRelic = _interopDefault(require('newrelic'));
8
24
  const core = require('@envelop/core');
9
25
  const graphql = require('graphql');
10
26
 
11
- const { shim: instrumentationApi } = newRelic;
12
27
  var AttributeName;
13
28
  (function (AttributeName) {
14
29
  AttributeName["COMPONENT_NAME"] = "Envelop_NewRelic_Plugin";
@@ -31,11 +46,10 @@ const DEFAULT_OPTIONS = {
31
46
  trackResolvers: false,
32
47
  includeResolverArgs: false,
33
48
  rootFieldsNaming: false,
34
- operationNameProperty: '',
35
49
  skipError: defaultSkipError,
36
50
  };
37
51
  function defaultSkipError(error) {
38
- return error instanceof core.EnvelopError;
52
+ return error.originalError instanceof core.EnvelopError;
39
53
  }
40
54
  const useNewRelic = (rawOptions) => {
41
55
  const options = {
@@ -44,23 +58,30 @@ const useNewRelic = (rawOptions) => {
44
58
  };
45
59
  options.isExecuteVariablesRegex = options.includeExecuteVariables instanceof RegExp;
46
60
  options.isResolverArgsRegex = options.includeResolverArgs instanceof RegExp;
47
- const logger = instrumentationApi.logger.child({ component: AttributeName.COMPONENT_NAME });
48
- logger.info(`${AttributeName.COMPONENT_NAME} registered`);
49
- instrumentationApi.agent.metrics
50
- .getOrCreateMetric(`Supportability/ExternalModules/${AttributeName.COMPONENT_NAME}`)
51
- .incrementCallCount();
61
+ const instrumentationApi$ = new Promise(function (resolve) { resolve(_interopNamespace(require('newrelic'))); })
62
+ .then(m => m.default || m)
63
+ .then(({ shim }) => {
64
+ shim.agent.metrics.getOrCreateMetric(`Supportability/ExternalModules/${AttributeName.COMPONENT_NAME}`).incrementCallCount();
65
+ return shim;
66
+ });
67
+ const logger$ = instrumentationApi$.then(({ logger }) => {
68
+ const childLogger = logger.child({ component: AttributeName.COMPONENT_NAME });
69
+ childLogger.info(`${AttributeName.COMPONENT_NAME} registered`);
70
+ return childLogger;
71
+ });
52
72
  return {
53
- onExecute({ args }) {
54
- var _a;
73
+ async onExecute({ args }) {
74
+ var _a, _b;
75
+ const instrumentationApi = await instrumentationApi$;
55
76
  const transactionNameState = instrumentationApi.agent.tracer.getTransaction().nameState;
56
77
  const spanContext = instrumentationApi.agent.tracer.getSpanContext();
57
78
  const delimiter = transactionNameState.delimiter;
58
79
  const rootOperation = args.document.definitions.find(definitionNode => definitionNode.kind === graphql.Kind.OPERATION_DEFINITION);
59
80
  const operationType = rootOperation.operation;
60
81
  const document = graphql.print(args.document);
61
- const operationName = args.document[options.operationNameProperty] ||
82
+ const operationName = ((_a = options.extractOperationName) === null || _a === void 0 ? void 0 : _a.call(options, args.contextValue)) ||
62
83
  args.operationName ||
63
- ((_a = rootOperation.name) === null || _a === void 0 ? void 0 : _a.value) ||
84
+ ((_b = rootOperation.name) === null || _b === void 0 ? void 0 : _b.value) ||
64
85
  AttributeName.ANONYMOUS_OPERATION;
65
86
  let rootFields = null;
66
87
  if (options.rootFieldsNaming) {
@@ -80,7 +101,8 @@ const useNewRelic = (rawOptions) => {
80
101
  }
81
102
  const operationSegment = instrumentationApi.getActiveSegment();
82
103
  const onResolverCalled = options.trackResolvers
83
- ? ({ args: resolversArgs, info }) => {
104
+ ? async ({ args: resolversArgs, info }) => {
105
+ const logger = await logger$;
84
106
  const { returnType, path, parentType } = info;
85
107
  const formattedPath = flattenPath(path, delimiter);
86
108
  const currentSegment = instrumentationApi.getActiveSegment();
@@ -105,14 +127,9 @@ const useNewRelic = (rawOptions) => {
105
127
  resolverSegment.addAttribute(AttributeName.RESOLVER_ARGS, JSON.stringify(resolverArgsToTrack));
106
128
  }
107
129
  return ({ result }) => {
108
- var _a;
109
130
  if (options.includeRawResult) {
110
131
  resolverSegment.addAttribute(AttributeName.RESOLVER_RESULT, JSON.stringify(result));
111
132
  }
112
- if (result instanceof Error && !((_a = options.skipError) === null || _a === void 0 ? void 0 : _a.call(options, result))) {
113
- const transaction = instrumentationApi.tracer.getTransaction();
114
- instrumentationApi.agent.errors.add(transaction, JSON.stringify(result));
115
- }
116
133
  resolverSegment.end();
117
134
  };
118
135
  }
@@ -120,23 +137,34 @@ const useNewRelic = (rawOptions) => {
120
137
  return {
121
138
  onResolverCalled,
122
139
  onExecuteDone({ result }) {
123
- if (core.isAsyncIterable(result)) {
124
- operationSegment.end();
125
- // eslint-disable-next-line no-console
126
- console.warn(`Plugin "newrelic" encountered a AsyncIterator which is not supported yet, so tracing data is not available for the operation.`);
127
- return;
128
- }
129
- if (result.data && options.includeRawResult) {
130
- spanContext.addCustomAttribute(AttributeName.EXECUTION_RESULT, JSON.stringify(result));
131
- }
132
- if (result.errors && result.errors.length > 0) {
133
- const agent = instrumentationApi.agent;
134
- const transaction = instrumentationApi.tracer.getTransaction();
135
- for (const error of result.errors) {
136
- agent.errors.add(transaction, JSON.stringify(error));
140
+ const sendResult = (singularResult) => {
141
+ var _a;
142
+ if (singularResult.data && options.includeRawResult) {
143
+ spanContext.addCustomAttribute(AttributeName.EXECUTION_RESULT, JSON.stringify(singularResult));
137
144
  }
145
+ if (singularResult.errors && singularResult.errors.length > 0) {
146
+ const agent = instrumentationApi.agent;
147
+ const transaction = instrumentationApi.tracer.getTransaction();
148
+ for (const error of singularResult.errors) {
149
+ if ((_a = options.skipError) === null || _a === void 0 ? void 0 : _a.call(options, error))
150
+ continue;
151
+ agent.errors.add(transaction, JSON.stringify(error));
152
+ }
153
+ }
154
+ };
155
+ if (core.isAsyncIterable(result)) {
156
+ return {
157
+ onNext: ({ result: singularResult }) => {
158
+ sendResult(singularResult);
159
+ },
160
+ onEnd: () => {
161
+ operationSegment.end();
162
+ },
163
+ };
138
164
  }
165
+ sendResult(result);
139
166
  operationSegment.end();
167
+ return {};
140
168
  },
141
169
  };
142
170
  },
package/index.mjs CHANGED
@@ -1,8 +1,6 @@
1
- import newRelic from 'newrelic';
2
1
  import { EnvelopError, isAsyncIterable } from '@envelop/core';
3
2
  import { Kind, print } from 'graphql';
4
3
 
5
- const { shim: instrumentationApi } = newRelic;
6
4
  var AttributeName;
7
5
  (function (AttributeName) {
8
6
  AttributeName["COMPONENT_NAME"] = "Envelop_NewRelic_Plugin";
@@ -25,11 +23,10 @@ const DEFAULT_OPTIONS = {
25
23
  trackResolvers: false,
26
24
  includeResolverArgs: false,
27
25
  rootFieldsNaming: false,
28
- operationNameProperty: '',
29
26
  skipError: defaultSkipError,
30
27
  };
31
28
  function defaultSkipError(error) {
32
- return error instanceof EnvelopError;
29
+ return error.originalError instanceof EnvelopError;
33
30
  }
34
31
  const useNewRelic = (rawOptions) => {
35
32
  const options = {
@@ -38,23 +35,30 @@ const useNewRelic = (rawOptions) => {
38
35
  };
39
36
  options.isExecuteVariablesRegex = options.includeExecuteVariables instanceof RegExp;
40
37
  options.isResolverArgsRegex = options.includeResolverArgs instanceof RegExp;
41
- const logger = instrumentationApi.logger.child({ component: AttributeName.COMPONENT_NAME });
42
- logger.info(`${AttributeName.COMPONENT_NAME} registered`);
43
- instrumentationApi.agent.metrics
44
- .getOrCreateMetric(`Supportability/ExternalModules/${AttributeName.COMPONENT_NAME}`)
45
- .incrementCallCount();
38
+ const instrumentationApi$ = import('newrelic')
39
+ .then(m => m.default || m)
40
+ .then(({ shim }) => {
41
+ shim.agent.metrics.getOrCreateMetric(`Supportability/ExternalModules/${AttributeName.COMPONENT_NAME}`).incrementCallCount();
42
+ return shim;
43
+ });
44
+ const logger$ = instrumentationApi$.then(({ logger }) => {
45
+ const childLogger = logger.child({ component: AttributeName.COMPONENT_NAME });
46
+ childLogger.info(`${AttributeName.COMPONENT_NAME} registered`);
47
+ return childLogger;
48
+ });
46
49
  return {
47
- onExecute({ args }) {
48
- var _a;
50
+ async onExecute({ args }) {
51
+ var _a, _b;
52
+ const instrumentationApi = await instrumentationApi$;
49
53
  const transactionNameState = instrumentationApi.agent.tracer.getTransaction().nameState;
50
54
  const spanContext = instrumentationApi.agent.tracer.getSpanContext();
51
55
  const delimiter = transactionNameState.delimiter;
52
56
  const rootOperation = args.document.definitions.find(definitionNode => definitionNode.kind === Kind.OPERATION_DEFINITION);
53
57
  const operationType = rootOperation.operation;
54
58
  const document = print(args.document);
55
- const operationName = args.document[options.operationNameProperty] ||
59
+ const operationName = ((_a = options.extractOperationName) === null || _a === void 0 ? void 0 : _a.call(options, args.contextValue)) ||
56
60
  args.operationName ||
57
- ((_a = rootOperation.name) === null || _a === void 0 ? void 0 : _a.value) ||
61
+ ((_b = rootOperation.name) === null || _b === void 0 ? void 0 : _b.value) ||
58
62
  AttributeName.ANONYMOUS_OPERATION;
59
63
  let rootFields = null;
60
64
  if (options.rootFieldsNaming) {
@@ -74,7 +78,8 @@ const useNewRelic = (rawOptions) => {
74
78
  }
75
79
  const operationSegment = instrumentationApi.getActiveSegment();
76
80
  const onResolverCalled = options.trackResolvers
77
- ? ({ args: resolversArgs, info }) => {
81
+ ? async ({ args: resolversArgs, info }) => {
82
+ const logger = await logger$;
78
83
  const { returnType, path, parentType } = info;
79
84
  const formattedPath = flattenPath(path, delimiter);
80
85
  const currentSegment = instrumentationApi.getActiveSegment();
@@ -99,14 +104,9 @@ const useNewRelic = (rawOptions) => {
99
104
  resolverSegment.addAttribute(AttributeName.RESOLVER_ARGS, JSON.stringify(resolverArgsToTrack));
100
105
  }
101
106
  return ({ result }) => {
102
- var _a;
103
107
  if (options.includeRawResult) {
104
108
  resolverSegment.addAttribute(AttributeName.RESOLVER_RESULT, JSON.stringify(result));
105
109
  }
106
- if (result instanceof Error && !((_a = options.skipError) === null || _a === void 0 ? void 0 : _a.call(options, result))) {
107
- const transaction = instrumentationApi.tracer.getTransaction();
108
- instrumentationApi.agent.errors.add(transaction, JSON.stringify(result));
109
- }
110
110
  resolverSegment.end();
111
111
  };
112
112
  }
@@ -114,23 +114,34 @@ const useNewRelic = (rawOptions) => {
114
114
  return {
115
115
  onResolverCalled,
116
116
  onExecuteDone({ result }) {
117
- if (isAsyncIterable(result)) {
118
- operationSegment.end();
119
- // eslint-disable-next-line no-console
120
- console.warn(`Plugin "newrelic" encountered a AsyncIterator which is not supported yet, so tracing data is not available for the operation.`);
121
- return;
122
- }
123
- if (result.data && options.includeRawResult) {
124
- spanContext.addCustomAttribute(AttributeName.EXECUTION_RESULT, JSON.stringify(result));
125
- }
126
- if (result.errors && result.errors.length > 0) {
127
- const agent = instrumentationApi.agent;
128
- const transaction = instrumentationApi.tracer.getTransaction();
129
- for (const error of result.errors) {
130
- agent.errors.add(transaction, JSON.stringify(error));
117
+ const sendResult = (singularResult) => {
118
+ var _a;
119
+ if (singularResult.data && options.includeRawResult) {
120
+ spanContext.addCustomAttribute(AttributeName.EXECUTION_RESULT, JSON.stringify(singularResult));
121
+ }
122
+ if (singularResult.errors && singularResult.errors.length > 0) {
123
+ const agent = instrumentationApi.agent;
124
+ const transaction = instrumentationApi.tracer.getTransaction();
125
+ for (const error of singularResult.errors) {
126
+ if ((_a = options.skipError) === null || _a === void 0 ? void 0 : _a.call(options, error))
127
+ continue;
128
+ agent.errors.add(transaction, JSON.stringify(error));
129
+ }
131
130
  }
131
+ };
132
+ if (isAsyncIterable(result)) {
133
+ return {
134
+ onNext: ({ result: singularResult }) => {
135
+ sendResult(singularResult);
136
+ },
137
+ onEnd: () => {
138
+ operationSegment.end();
139
+ },
140
+ };
132
141
  }
142
+ sendResult(result);
133
143
  operationSegment.end();
144
+ return {};
134
145
  },
135
146
  };
136
147
  },
package/package.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "@envelop/newrelic",
3
- "version": "4.0.0-alpha-4ba70ab.0",
3
+ "version": "4.0.1-alpha-3ace273.0",
4
4
  "sideEffects": false,
5
5
  "peerDependencies": {
6
- "@envelop/core": "2.1.0-alpha-4ba70ab.0",
6
+ "@envelop/core": "2.3.2-alpha-3ace273.0",
7
7
  "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0",
8
8
  "newrelic": "^7 || ^8.0.0"
9
9
  },