apollo-link-sentry
Advanced tools
Comparing version 3.0.2 to 3.0.3
@@ -5,3 +5,3 @@ "use strict"; | ||
var tslib_1 = require("tslib"); | ||
var dot_prop_1 = tslib_1.__importDefault(require("dot-prop")); | ||
var dot_prop_1 = (0, tslib_1.__importDefault)(require("dot-prop")); | ||
var graphql_1 = require("graphql"); | ||
@@ -12,3 +12,3 @@ var operation_1 = require("./operation"); | ||
var attachBreadcrumbs = options.attachBreadcrumbs; | ||
var definition = operation_1.extractDefinition(operation); | ||
var definition = (0, operation_1.extractDefinition)(operation); | ||
var data = {}; | ||
@@ -24,3 +24,4 @@ var uri = options.uri; | ||
if (attachBreadcrumbs.includeQuery) { | ||
data.query = (_d = (_c = (_b = definition.loc) === null || _b === void 0 ? void 0 : _b.source) === null || _c === void 0 ? void 0 : _c.body) !== null && _d !== void 0 ? _d : graphql_1.print(definition); | ||
data.query = | ||
(_d = (_c = (_b = definition.loc) === null || _b === void 0 ? void 0 : _b.source) === null || _c === void 0 ? void 0 : _c.body) !== null && _d !== void 0 ? _d : (0, graphql_1.print)(definition); | ||
} | ||
@@ -31,3 +32,4 @@ if (attachBreadcrumbs.includeVariables) { | ||
if (attachBreadcrumbs.includeCache) { | ||
data.cache = (_g = (_f = (_e = operation.getContext().cache) === null || _e === void 0 ? void 0 : _e.data) === null || _f === void 0 ? void 0 : _f.data) !== null && _g !== void 0 ? _g : undefined; | ||
data.cache = | ||
(_g = (_f = (_e = operation.getContext().cache) === null || _e === void 0 ? void 0 : _e.data) === null || _f === void 0 ? void 0 : _f.data) !== null && _g !== void 0 ? _g : undefined; | ||
} | ||
@@ -40,3 +42,3 @@ var contextKeys = attachBreadcrumbs.includeContext; | ||
type: 'http', | ||
category: "graphql." + definition.operation, | ||
category: "graphql.".concat(definition.operation), | ||
data: data, | ||
@@ -43,0 +45,0 @@ }; |
@@ -17,3 +17,3 @@ "use strict"; | ||
return function (breadcrumb, hint) { | ||
var withoutFetch = exports.excludeGraphQLFetch(breadcrumb, hint); | ||
var withoutFetch = (0, exports.excludeGraphQLFetch)(breadcrumb, hint); | ||
if (withoutFetch === null) { | ||
@@ -20,0 +20,0 @@ return null; |
@@ -5,5 +5,5 @@ "use strict"; | ||
function extractDefinition(operation) { | ||
return operation.query.definitions[0]; | ||
return operation.query.definitions.find(function (q) { return q.kind === 'OperationDefinition'; }); | ||
} | ||
exports.extractDefinition = extractDefinition; | ||
//# sourceMappingURL=operation.js.map |
@@ -5,3 +5,3 @@ "use strict"; | ||
var tslib_1 = require("tslib"); | ||
var deepmerge_1 = tslib_1.__importDefault(require("deepmerge")); | ||
var deepmerge_1 = (0, tslib_1.__importDefault)(require("deepmerge")); | ||
exports.defaultOptions = { | ||
@@ -23,5 +23,5 @@ shouldHandleOperation: undefined, | ||
function withDefaults(options) { | ||
return deepmerge_1.default(exports.defaultOptions, options); | ||
return (0, deepmerge_1.default)(exports.defaultOptions, options); | ||
} | ||
exports.withDefaults = withDefaults; | ||
//# sourceMappingURL=options.js.map |
@@ -8,6 +8,6 @@ "use strict"; | ||
function setTransaction(operation) { | ||
var definition = operation_1.extractDefinition(operation); | ||
var definition = (0, operation_1.extractDefinition)(operation); | ||
var name = definition.name; | ||
if (name) { | ||
minimal_1.configureScope(function (scope) { | ||
(0, minimal_1.configureScope)(function (scope) { | ||
scope.setTransactionName(name.value); | ||
@@ -20,6 +20,6 @@ }); | ||
function setFingerprint(operation) { | ||
var definition = operation_1.extractDefinition(operation); | ||
var definition = (0, operation_1.extractDefinition)(operation); | ||
var name = definition.name; | ||
if (name) { | ||
minimal_1.configureScope(function (scope) { | ||
(0, minimal_1.configureScope)(function (scope) { | ||
scope.setFingerprint([exports.DEFAULT_FINGERPRINT, name.value]); | ||
@@ -35,6 +35,6 @@ }); | ||
: breadcrumb; | ||
transformed.data = utils_1.stringifyObjectKeys(transformed.data); | ||
minimal_1.addBreadcrumb(transformed); | ||
transformed.data = (0, utils_1.stringifyObjectKeys)(transformed.data); | ||
(0, minimal_1.addBreadcrumb)(transformed); | ||
} | ||
exports.attachBreadcrumbToSentry = attachBreadcrumbToSentry; | ||
//# sourceMappingURL=sentry.js.map |
@@ -7,3 +7,3 @@ "use strict"; | ||
var types_1 = require("@sentry/types"); | ||
var zen_observable_1 = tslib_1.__importDefault(require("zen-observable")); | ||
var zen_observable_1 = (0, tslib_1.__importDefault)(require("zen-observable")); | ||
var breadcrumb_1 = require("./breadcrumb"); | ||
@@ -13,24 +13,24 @@ var options_1 = require("./options"); | ||
var SentryLink = (function (_super) { | ||
tslib_1.__extends(SentryLink, _super); | ||
(0, tslib_1.__extends)(SentryLink, _super); | ||
function SentryLink(options) { | ||
if (options === void 0) { options = {}; } | ||
var _this = _super.call(this) || this; | ||
_this.options = options_1.withDefaults(options); | ||
_this.options = (0, options_1.withDefaults)(options); | ||
return _this; | ||
} | ||
SentryLink.prototype.request = function (operation, forward) { | ||
var _this = this; | ||
if (typeof this.options.shouldHandleOperation === 'function') { | ||
if (!this.options.shouldHandleOperation(operation)) { | ||
return forward(operation); | ||
} | ||
var _a, _b; | ||
var options = this.options; | ||
if (!((_b = (_a = options.shouldHandleOperation) === null || _a === void 0 ? void 0 : _a.call(options, operation)) !== null && _b !== void 0 ? _b : true)) { | ||
return forward(operation); | ||
} | ||
if (this.options.setTransaction) { | ||
sentry_1.setTransaction(operation); | ||
if (options.setTransaction) { | ||
(0, sentry_1.setTransaction)(operation); | ||
} | ||
if (this.options.setFingerprint) { | ||
sentry_1.setFingerprint(operation); | ||
if (options.setFingerprint) { | ||
(0, sentry_1.setFingerprint)(operation); | ||
} | ||
var breadcrumb = this.options.attachBreadcrumbs | ||
? breadcrumb_1.makeBreadcrumb(operation, this.options) | ||
var attachBreadcrumbs = options.attachBreadcrumbs; | ||
var breadcrumb = attachBreadcrumbs | ||
? (0, breadcrumb_1.makeBreadcrumb)(operation, options) | ||
: undefined; | ||
@@ -40,7 +40,14 @@ return new zen_observable_1.default(function (originalObserver) { | ||
next: function (result) { | ||
if (_this.options.attachBreadcrumbs) { | ||
if (attachBreadcrumbs) { | ||
breadcrumb.level = severityForResult(result); | ||
if (_this.options.attachBreadcrumbs.includeFetchResult) { | ||
if (attachBreadcrumbs.includeFetchResult) { | ||
breadcrumb.data.fetchResult = result; | ||
} | ||
if (attachBreadcrumbs.includeError && | ||
result.errors && | ||
result.errors.length > 0) { | ||
breadcrumb.data.error = new core_1.ApolloError({ | ||
graphQLErrors: result.errors, | ||
}); | ||
} | ||
} | ||
@@ -50,4 +57,4 @@ originalObserver.next(result); | ||
complete: function () { | ||
if (_this.options.attachBreadcrumbs) { | ||
sentry_1.attachBreadcrumbToSentry(operation, breadcrumb, _this.options); | ||
if (attachBreadcrumbs) { | ||
(0, sentry_1.attachBreadcrumbToSentry)(operation, breadcrumb, options); | ||
} | ||
@@ -57,9 +64,9 @@ originalObserver.complete(); | ||
error: function (error) { | ||
if (_this.options.attachBreadcrumbs) { | ||
if (attachBreadcrumbs) { | ||
breadcrumb.level = types_1.Severity.Error; | ||
var scrubbedError = void 0; | ||
if (isServerError(error)) { | ||
var result = error.result, response = error.response, rest = tslib_1.__rest(error, ["result", "response"]); | ||
var result = error.result, response = error.response, rest = (0, tslib_1.__rest)(error, ["result", "response"]); | ||
scrubbedError = rest; | ||
if (_this.options.attachBreadcrumbs.includeFetchResult) { | ||
if (attachBreadcrumbs.includeFetchResult) { | ||
breadcrumb.data.fetchResult = result; | ||
@@ -71,6 +78,6 @@ } | ||
} | ||
if (_this.options.attachBreadcrumbs.includeError) { | ||
if (attachBreadcrumbs.includeError) { | ||
breadcrumb.data.error = scrubbedError; | ||
} | ||
sentry_1.attachBreadcrumbToSentry(operation, breadcrumb, _this.options); | ||
(0, sentry_1.attachBreadcrumbToSentry)(operation, breadcrumb, options); | ||
} | ||
@@ -77,0 +84,0 @@ originalObserver.error(error); |
{ | ||
"name": "apollo-link-sentry", | ||
"version": "3.0.2", | ||
"version": "3.0.3", | ||
"license": "MIT", | ||
@@ -27,5 +27,5 @@ "author": "Diederik van den Burger <diederikvandenburger@tab.capital>", | ||
"lint:fix": "eslint --fix .", | ||
"prerelease": "yarn test && yarn build", | ||
"release": "standard-version", | ||
"deploy": "yarn publish --non-interactive" | ||
"prettify": "prettier --write README.md UPGRADE.md LICENSE.md", | ||
"fix": "yarn run lint:fix && yarn run prettify", | ||
"validate": "yarn run lint && yarn run test && yarn run build" | ||
}, | ||
@@ -43,27 +43,30 @@ "dependencies": { | ||
"devDependencies": { | ||
"@apollo/client": "^3.2.3", | ||
"@sentry/browser": "^6.0.4", | ||
"@sentry/minimal": "^6.0.4", | ||
"@sentry/types": "^6.0.4", | ||
"@types/jest": "^26.0.14", | ||
"@typescript-eslint/eslint-plugin": "^4.14.2", | ||
"@typescript-eslint/parser": "^4.14.2", | ||
"eslint": "^7.19.0", | ||
"eslint-config-prettier": "^7.2.0", | ||
"eslint-plugin-import": "^2.22.1", | ||
"eslint-plugin-prettier": "^3.3.1", | ||
"graphql": "^15.4.0", | ||
"@apollo/client": "^3.5.6", | ||
"@semantic-release/changelog": "^6.0.1", | ||
"@semantic-release/git": "^10.0.1", | ||
"@sentry/browser": "^6.16.1", | ||
"@sentry/minimal": "^6.16.1", | ||
"@sentry/types": "^6.16.1", | ||
"@types/jest": "^27.0.3", | ||
"@types/zen-observable": "^0.8.3", | ||
"@typescript-eslint/eslint-plugin": "^5.7.0", | ||
"@typescript-eslint/parser": "^5.7.0", | ||
"eslint": "^8.4.1", | ||
"eslint-config-prettier": "^8.3.0", | ||
"eslint-plugin-import": "^2.25.3", | ||
"eslint-plugin-prettier": "^4.0.0", | ||
"graphql": "^15.8.0", | ||
"isomorphic-fetch": "^3.0.0", | ||
"jest": "^26.5.3", | ||
"jest-spec-reporter": "^1.0.14", | ||
"prettier": "^2.2.1", | ||
"jest": "^27.4.5", | ||
"jest-spec-reporter": "^1.0.17", | ||
"prettier": "^2.5.1", | ||
"rimraf": "^3.0.2", | ||
"sentry-testkit": "^3.2.1", | ||
"standard-version": "^9.0.0", | ||
"ts-jest": "^26.4.1", | ||
"tsc-watch": "~4.2.9", | ||
"tslib": "^2.0.3", | ||
"typescript": "^4.1.3", | ||
"semantic-release": "^18.0.1", | ||
"sentry-testkit": "^3.3.7", | ||
"ts-jest": "^27.1.1", | ||
"tsc-watch": "^4.5.0", | ||
"tslib": "^2.3.1", | ||
"typescript": "^4.5.4", | ||
"zen-observable": "^0.8.15" | ||
} | ||
} |
124
README.md
# Apollo Link Sentry | ||
Apollo Link middleware to enrich SentryJS events with GraphQL data. | ||
[![npm](https://img.shields.io/npm/v/apollo-link-sentry)](https://www.npmjs.com/package/apollo-link-sentry) | ||
Apollo Link to enrich Sentry events with GraphQL data | ||
[![GitHub Workflow Status](https://img.shields.io/github/workflow/status/DiederikvandenB/apollo-link-sentry/Test)](https://github.com/DiederikvandenB/apollo-link-sentry/actions) | ||
[![Coveralls github branch](https://img.shields.io/coveralls/github/DiederikvandenB/apollo-link-sentry/master)](https://coveralls.io/github/DiederikvandenB/apollo-link-sentry?branch=master) | ||
[![Code Coverage](https://img.shields.io/coveralls/github/DiederikvandenB/apollo-link-sentry/master)](https://coveralls.io/github/DiederikvandenB/apollo-link-sentry?branch=master) | ||
[![semantic-release](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg)](https://github.com/semantic-release/semantic-release) | ||
[![npm-version](https://img.shields.io/npm/v/apollo-link-sentry)](https://www.npmjs.com/package/apollo-link-sentry) | ||
[![npm-downloads](https://img.shields.io/npm/dt/apollo-link-sentry)](https://www.npmjs.com/package/apollo-link-sentry) | ||
[![David](https://img.shields.io/david/diederikvandenb/apollo-link-sentry)](https://github.com/diederikvandenb/apollo-link-sentry) | ||
[![Dependabot Status](https://api.dependabot.com/badges/status?host=github&repo=DiederikvandenB/apollo-link-sentry)](https://dependabot.com) | ||
## Installation | ||
``` | ||
@@ -20,12 +22,19 @@ yarn add apollo-link-sentry | ||
## Features | ||
Turn this: | ||
<p align="center"><img src="https://raw.githubusercontent.com/DiederikvandenB/apollo-link-sentry/master/screenshots/before.png" alt="Before" width="auto" align="center" /></p> | ||
<p align="center"> | ||
<img src="https://raw.githubusercontent.com/DiederikvandenB/apollo-link-sentry/master/screenshots/before.png" alt="Before" width="auto" /> | ||
</p> | ||
Into this: | ||
<p align="center"><img src="https://raw.githubusercontent.com/DiederikvandenB/apollo-link-sentry/master/screenshots/after.png" alt="After" width="auto" /></p> | ||
<p align="center"> | ||
<img src="https://raw.githubusercontent.com/DiederikvandenB/apollo-link-sentry/master/screenshots/after.png" alt="After" width="auto" /> | ||
</p> | ||
## Basic setup | ||
Initialize Sentry as you would normally. Then, add `apollo-link-sentry` to your Apollo Client's `link` array: | ||
```js | ||
@@ -44,2 +53,3 @@ import { SentryLink } from 'apollo-link-sentry'; | ||
## Options | ||
```typescript | ||
@@ -58,2 +68,4 @@ export interface FullOptions { | ||
* Used to add context information, e.g. to breadcrumbs. | ||
* | ||
* Defaults to undefined. | ||
*/ | ||
@@ -66,2 +78,4 @@ uri: undefined | string; | ||
* May be overwritten by other parts of your app. | ||
* | ||
* Defaults to true. | ||
*/ | ||
@@ -75,2 +89,4 @@ setTransaction: true | false; | ||
* May be overwritten by other parts of your app. | ||
* | ||
* Defaults to true. | ||
*/ | ||
@@ -96,2 +112,4 @@ setFingerprint: true | false; | ||
* Include the full query string? | ||
* | ||
* Defaults to false. | ||
*/ | ||
@@ -104,2 +122,4 @@ includeQuery: false | true; | ||
* Be careful not to leak sensitive information or send too much data. | ||
* | ||
* Defaults to false. | ||
*/ | ||
@@ -112,2 +132,4 @@ includeVariables: false | true; | ||
* Be careful not to leak sensitive information or send too much data. | ||
* | ||
* Defaults to false. | ||
*/ | ||
@@ -120,2 +142,4 @@ includeFetchResult: false | true; | ||
* Be careful not to leak sensitive information or send too much data. | ||
* | ||
* Defaults to false. | ||
*/ | ||
@@ -129,2 +153,4 @@ includeError: false | true; | ||
* see "Be careful what you include", unless carefully combined with `beforeBreadcrumb`. | ||
* | ||
* Defaults to false. | ||
*/ | ||
@@ -138,2 +164,4 @@ includeCache: false | true; | ||
* information such as headers. | ||
* | ||
* Defaults to false. | ||
*/ | ||
@@ -147,2 +175,4 @@ includeContext: false | NonEmptyArray<string>; | ||
* Very useful in combination with options like `includeVariables` and `includeContextKeys`. | ||
* | ||
* Defaults to undefined. | ||
*/ | ||
@@ -156,16 +186,31 @@ transform: | ||
### Compatibility with other Apollo Links | ||
`apollo-link-sentry` aims to be friendly with other `apollo-link` packages, in the sense that we would like for you to be able to attach as much data as you want. For example, if you would like to add the HTTP headers you set with `apollo-link-context`, you can do that by setting `includeContextKeys: ['headers']`. | ||
`apollo-link-sentry` aims to be friendly with other `apollo-link` packages, | ||
in the sense that we would like for you to be able to attach as much data as you want. | ||
For example, if you would like to add the HTTP headers you set with `apollo-link-context`, | ||
you can do that by setting `includeContextKeys: ['headers']`. | ||
In case you find that there's a piece of data you're missing, feel free to open an issue. | ||
### Be careful what you include | ||
Please note that Sentry sets some limits to how big events can be. For instance, **events greater than 200KiB are immediately dropped (pre decompression)**. More information on that [here](https://docs.sentry.io/accounts/quotas/#attributes-limits). Be especially careful with the `includeCache` option, as caches can become quite large. | ||
Furthermore, much of the data you are sending to Sentry can include (sensitive) personal information. This might lead you to violating the terms of the GDPR. Use Sentry's `beforeBreadcrumb` function to filter out all sensitive data. | ||
Please note that Sentry sets some limits to how big events can be. | ||
For instance, **events greater than 200KiB are immediately dropped (pre decompression)**. | ||
More information on that [here](https://docs.sentry.io/accounts/quotas/#attributes-limits). | ||
Be especially careful with the `includeCache` option, as caches can become quite large. | ||
Furthermore, much of the data you are sending to Sentry can include (sensitive) personal information. | ||
This might lead you to violating the terms of the GDPR. | ||
Use Sentry's `beforeBreadcrumb` function to filter out all sensitive data. | ||
## Exclude redundant `fetch` breadcrumbs | ||
By default, Sentry attaches all fetch events as breadcrumbs. Since this package tracks GraphQL requests as breadcrumbs, | ||
By default, Sentry attaches all fetch events as breadcrumbs. | ||
Since this package tracks GraphQL requests as breadcrumbs, | ||
they would show up duplicated in Sentry. | ||
1. Disable the default integration for fetch requests. Note that this is only recommended if you **only** use GraphQL requests in your application. The default integration can be disabled like this: | ||
1. Disable the default integration for fetch requests. | ||
Note that this is only recommended if you **only** use GraphQL requests in your application. | ||
The default integration can be disabled like this: | ||
```js | ||
@@ -181,3 +226,5 @@ Sentry.init({ | ||
2. Use the `beforeBreadcrumb` option of Sentry to filter out the duplicates. | ||
The helpers in this package recognize every breadcrumb of category `fetch` where the URL contains `/graphql` as a GraphQL request. | ||
The helpers in this package recognize every breadcrumb of category `fetch` | ||
where the URL contains `/graphql` as a GraphQL request. | ||
```js | ||
@@ -204,32 +251,29 @@ import { excludeGraphQLFetch } from 'apollo-link-sentry'; | ||
## FAQ | ||
- **I don't see any events appearing in my Sentry stream** | ||
- Note that this package (currently) only adds breadcrumbs. This means that you are still responsible for reporting errors to Sentry. You can do this by calling `Sentry.captureException()`. See this example: | ||
```jsx | ||
<Mutation mutation={ERROR_MUTATION}> | ||
{(mutate, { data, error, loading }) => { | ||
if (loading) return <div>loading</div>; | ||
if (error) return <div>{error.toString()}</div>; | ||
const onClick = () => mutate().catch((error) => { | ||
### I don't see any events appearing in my Sentry stream | ||
This package only adds breadcrumbs, you are still responsible for reporting errors to Sentry. | ||
You can do this by calling `Sentry.captureException()`: | ||
```jsx | ||
<Mutation mutation={MUTATION_THAT_MIGHT_FAIL}> | ||
{(mutate, { data, error, loading }) => { | ||
if (loading) return <div>loading</div>; | ||
if (error) return <div>{error.toString()}</div>; | ||
const onClick = () => | ||
mutate().catch((error) => { | ||
Sentry.captureException(error); | ||
}); | ||
return <div> | ||
<button type="button" onClick={() => onClick()}>Mutate</button> | ||
return ( | ||
<div> | ||
<button type="button" onClick={() => onClick()}> | ||
Mutate | ||
</button> | ||
{JSON.stringify(data)} | ||
</div> | ||
}} | ||
</Mutation> | ||
``` | ||
## Caveats | ||
- This package has not been tested for subscriptions | ||
- We also need to test for different links, i.e. `apollo-link-rest` | ||
## Roadmap / notes | ||
- Write best practice scenario: | ||
- setting `includeError` true | ||
- catch errors manually | ||
- throw custom error | ||
- how to use together with `apollo-link-error`? | ||
- does it report errors twice if you do sentry capture there and in your catch | ||
); | ||
}} | ||
</Mutation> | ||
``` |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
349
265
37990
27
35