Skip to content

Commit 4821c8a

Browse files
Merge branch 'integration/typescript' into PLAT-13700-iserror
2 parents 89bb6bf + fc103ad commit 4821c8a

File tree

26 files changed

+171
-198
lines changed

26 files changed

+171
-198
lines changed

packages/core/package.json

-2
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,7 @@
99
"import": "./dist/index.mjs",
1010
"default": "./dist/index.cjs"
1111
},
12-
"./lib/json-payload": "./src/lib/json-payload.js",
1312
"./lib/es-utils/assign": "./src/lib/es-utils/assign.js",
14-
"./lib/validators/int-range": "./src/lib/validators/int-range.js",
1513
"./lib/es-utils/includes": "./src/lib/es-utils/includes.js",
1614
"./lib/es-utils/filter": "./src/lib/es-utils/filter.js",
1715
"./lib/es-utils/reduce": "./src/lib/es-utils/reduce.js",

packages/core/src/common.ts

+4-12
Original file line numberDiff line numberDiff line change
@@ -60,16 +60,6 @@ export interface Logger {
6060
error: (...args: any[]) => void
6161
}
6262

63-
export interface EventPayload {
64-
apiKey: string
65-
notifier: {
66-
name: string
67-
version: string
68-
url: string
69-
}
70-
events: Event[]
71-
}
72-
7363
export interface SessionPayload {
7464
notifier: {
7565
name: string
@@ -169,7 +159,8 @@ export interface Notifier {
169159
}
170160

171161
export interface EventDeliveryPayload {
172-
apiKey: string
162+
apiKey?: string
163+
payloadVersion?: string
173164
notifier: Notifier
174165
events: Event[]
175166
}
@@ -184,6 +175,7 @@ export interface SessionDeliveryPayload {
184175
user?: User
185176
}>
186177
}
178+
187179
export interface Delivery {
188180
sendEvent(payload: EventDeliveryPayload, cb: (err?: Error | null) => void): void
189181
sendSession(session: SessionDeliveryPayload, cb: (err?: Error | null) => void): void
@@ -206,4 +198,4 @@ export interface BugsnagError {
206198
errorMessage: string
207199
type: string
208200
stacktrace: Stackframe[]
209-
}
201+
}

packages/core/src/index.ts

+2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ export { default as Event } from './event'
44
export { default as Session } from './session'
55
export { default as schema } from './config'
66
export { default as cloneClient } from './lib/clone-client'
7+
export { default as jsonPayload } from './lib/json-payload'
8+
export { default as intRange } from './lib/validators/int-range'
79
export { default as isError } from './lib/iserror'
810

911
export * from './common'

packages/core/src/lib/json-payload.d.ts

-10
This file was deleted.

packages/core/src/lib/json-payload.js

-25
This file was deleted.

packages/core/src/lib/json-payload.ts

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import jsonStringify from '@bugsnag/safe-json-stringify'
2+
import type { EventDeliveryPayload, SessionDeliveryPayload } from "../common";
3+
4+
type RedactedKey = string | RegExp
5+
6+
interface JsonPayload {
7+
event: (event: EventDeliveryPayload, redactedKeys?: RedactedKey[]) => string
8+
session: (session: SessionDeliveryPayload, redactedKeys?: RedactedKey[]) => string
9+
}
10+
11+
const EVENT_REDACTION_PATHS = [
12+
'events.[].metaData',
13+
'events.[].breadcrumbs.[].metaData',
14+
'events.[].request'
15+
]
16+
17+
const jsonPayload: JsonPayload = {
18+
event: (event, redactedKeys) => {
19+
let payload = jsonStringify(event, null, null, { redactedPaths: EVENT_REDACTION_PATHS, redactedKeys })
20+
if (payload.length > 10e5) {
21+
event.events[0]._metadata = {
22+
notifier:
23+
`WARNING!
24+
Serialized payload was ${payload.length / 10e5}MB (limit = 1MB)
25+
metadata was removed`
26+
}
27+
payload = jsonStringify(event, null, null, { redactedPaths: EVENT_REDACTION_PATHS, redactedKeys })
28+
}
29+
return payload
30+
},
31+
session: (session): string => {
32+
const payload = jsonStringify(session, null, null)
33+
return payload
34+
}
35+
}
36+
37+
export default jsonPayload

packages/core/src/lib/validators/int-range.d.ts

-4
This file was deleted.

packages/core/src/lib/validators/int-range.js

-4
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
const intRange: <T>(min?: number, max?: number) => (value: T) => boolean = (
2+
min = 1,
3+
max = Infinity
4+
) => (value) =>
5+
typeof value === "number" &&
6+
parseInt("" + value, 10) === value &&
7+
value >= min && value <= max;
8+
9+
export default intRange
+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
declare module '@bugsnag/safe-json-stringify' {
2+
export default function stringify(
3+
value: any,
4+
replacer?: null | ((this: any, key: string, value: any) => any),
5+
space?: null | string | number,
6+
options?: {
7+
redactedKeys?: Array<string | RegExp>;
8+
redactedPaths?: string[];
9+
}
10+
): string;
11+
}
+43-70
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
1+
import type { EventDeliveryPayload, SessionDeliveryPayload } from '../../src/common'
2+
import Event from '../../src/event'
13
import jsonPayload from '../../src/lib/json-payload'
4+
import Session from '../../src/session'
25

36
function makeBigObject () {
4-
var big: Record<string, string> = {}
5-
var i = 0
7+
const big: Record<string, string> = {}
8+
let i = 0
69
while (JSON.stringify(big).length < 2 * 10e5) {
710
big['entry' + i] = 'long repetitive string'.repeat(1000)
811
i++
@@ -12,91 +15,61 @@ function makeBigObject () {
1215

1316
describe('jsonPayload.event', () => {
1417
it('safe stringifies the payload and redacts values from certain paths of the supplied keys', () => {
18+
const event = new Event('CheckoutError', 'Failed load tickets')
19+
event.setUser('123', '[email protected]', 'Jim Bug')
20+
event.request = { apiKey: '245b39ebd3cd3992e85bffc81c045924' }
21+
1522
expect(jsonPayload.event({
16-
api_key: 'd145b8e5afb56516423bc4d605e45442',
17-
events: [
18-
{
19-
errorMessage: 'Failed load tickets',
20-
errorClass: 'CheckoutError',
21-
user: {
22-
name: 'Jim Bug',
23-
24-
},
25-
request: {
26-
api_key: '245b39ebd3cd3992e85bffc81c045924'
27-
}
28-
}
29-
]
30-
}, ['api_key'])).toBe('{"api_key":"d145b8e5afb56516423bc4d605e45442","events":[{"errorMessage":"Failed load tickets","errorClass":"CheckoutError","user":{"name":"Jim Bug","email":"[email protected]"},"request":{"api_key":"[REDACTED]"}}]}')
23+
apiKey: 'd145b8e5afb56516423bc4d605e45442',
24+
notifier: { name: 'Bugsnag', version: '1.0.0', url: 'https://bugsnag.com' },
25+
events: [event]
26+
}, ['apiKey'])).toBe('{"apiKey":"d145b8e5afb56516423bc4d605e45442","notifier":{"name":"Bugsnag","version":"1.0.0","url":"https://bugsnag.com"},"events":[{"payloadVersion":"4","exceptions":[{"errorClass":"CheckoutError","errorMessage":"Failed load tickets","type":"browserjs","stacktrace":[],"message":"Failed load tickets"}],"severity":"warning","unhandled":false,"severityReason":{"type":"handledException"},"app":{},"device":{},"request":{"apiKey":"[REDACTED]"},"breadcrumbs":[],"metaData":{},"user":{"id":"123","email":"[email protected]","name":"Jim Bug"},"featureFlags":[]}]}')
3127
})
3228

3329
it('strips the metaData of the first event if the payload is too large', () => {
34-
const payload = {
35-
api_key: 'd145b8e5afb56516423bc4d605e45442',
36-
events: [
37-
{
38-
errorMessage: 'Failed load tickets',
39-
errorClass: 'CheckoutError',
40-
user: {
41-
name: 'Jim Bug',
42-
43-
},
44-
request: {
45-
api_key: '245b39ebd3cd3992e85bffc81c045924'
46-
},
47-
_metadata: {}
48-
}
49-
]
50-
}
30+
const event = new Event('CheckoutError', 'Failed load tickets')
31+
event.setUser('123', '[email protected]', 'Jim Bug')
32+
event.request = { apiKey: '245b39ebd3cd3992e85bffc81c045924' }
33+
event._metadata = { 'big thing': makeBigObject() }
5134

52-
payload.events[0]._metadata = { 'big thing': makeBigObject() }
35+
const payload: EventDeliveryPayload = {
36+
apiKey: 'd145b8e5afb56516423bc4d605e45442',
37+
notifier: { name: 'Bugsnag', version: '1.0.0', url: 'https://bugsnag.com' },
38+
events: [event]
39+
}
5340

54-
expect(jsonPayload.event(payload)).toBe('{"api_key":"d145b8e5afb56516423bc4d605e45442","events":[{"errorMessage":"Failed load tickets","errorClass":"CheckoutError","user":{"name":"Jim Bug","email":"[email protected]"},"request":{"api_key":"245b39ebd3cd3992e85bffc81c045924"},"_metadata":{"notifier":"WARNING!\\nSerialized payload was 2.003435MB (limit = 1MB)\\nmetadata was removed"}}]}')
41+
expect(jsonPayload.event(payload)).toBe('{"apiKey":"d145b8e5afb56516423bc4d605e45442","notifier":{"name":"Bugsnag","version":"1.0.0","url":"https://bugsnag.com"},"events":[{"payloadVersion":"4","exceptions":[{"errorClass":"CheckoutError","errorMessage":"Failed load tickets","type":"browserjs","stacktrace":[],"message":"Failed load tickets"}],"severity":"warning","unhandled":false,"severityReason":{"type":"handledException"},"app":{},"device":{},"request":{"apiKey":"245b39ebd3cd3992e85bffc81c045924"},"breadcrumbs":[],"metaData":{"notifier":"WARNING!\\nSerialized payload was 2.003764MB (limit = 1MB)\\nmetadata was removed"},"user":{"id":"123","email":"[email protected]","name":"Jim Bug"},"featureFlags":[]}]}')
5542
})
5643

5744
it('does not attempt to strip any other data paths from the payload to reduce the size', () => {
45+
const event1 = new Event('CheckoutError', 'Failed load tickets')
46+
event1.setUser('123', '[email protected]', 'Jim Bug')
47+
event1.request = { apiKey: '245b39ebd3cd3992e85bffc81c045924' }
48+
49+
// Second event metadata should not be stripped, only the first
50+
const event2 = new Event('APIError', 'Request failed')
51+
event2._metadata = { 'big thing': makeBigObject() }
52+
5853
const payload = {
59-
api_key: 'd145b8e5afb56516423bc4d605e45442',
60-
events: [
61-
{
62-
errorMessage: 'Failed load tickets',
63-
errorClass: 'CheckoutError',
64-
user: {
65-
name: 'Jim Bug',
66-
67-
},
68-
_metadata: {}
69-
},
70-
{
71-
errorMessage: 'Request failed',
72-
errorClass: 'APIError',
73-
_metadata: {}
74-
}
75-
]
54+
apiKey: 'd145b8e5afb56516423bc4d605e45442',
55+
notifier: { name: 'Bugsnag', version: '1.0.0', url: 'https://bugsnag.com' },
56+
events: [event1, event2]
7657
}
77-
payload.events[1]._metadata = { 'big thing': makeBigObject() }
7858

7959
expect(jsonPayload.event(payload).length).toBeGreaterThan(10e5)
8060
})
8161
})
8262

8363
describe('jsonPayload.session', () => {
8464
it('safe stringifies the payload', () => {
85-
expect(jsonPayload.session({
86-
api_key: 'd145b8e5afb56516423bc4d605e45442',
87-
events: [
88-
{
89-
errorMessage: 'Failed load tickets',
90-
errorClass: 'CheckoutError',
91-
user: {
92-
name: 'Jim Bug',
93-
94-
},
95-
request: {
96-
api_key: '245b39ebd3cd3992e85bffc81c045924'
97-
}
98-
}
99-
]
100-
}, ['api_key'])).toBe('{"api_key":"d145b8e5afb56516423bc4d605e45442","events":[{"errorMessage":"Failed load tickets","errorClass":"CheckoutError","user":{"name":"Jim Bug","email":"[email protected]"},"request":{"api_key":"245b39ebd3cd3992e85bffc81c045924"}}]}')
65+
const session = new Session('123', new Date('2012-12-21T00:00:00.0000Z'))
66+
const sessionPayload: SessionDeliveryPayload = {
67+
app: { version: '1.0.0' },
68+
device: { id: '123' },
69+
notifier: { name: 'Bugsnag', version: '1.0.0', url: 'https://bugsnag.com' },
70+
sessions: [session]
71+
}
72+
73+
expect(jsonPayload.session(sessionPayload)).toBe('{"app":{"version":"1.0.0"},"device":{"id":"123"},"notifier":{"name":"Bugsnag","version":"1.0.0","url":"https://bugsnag.com"},"sessions":[{"id":"123","startedAt":"2012-12-21T00:00:00.000Z","events":{"handled":0,"unhandled":0}}]}')
10174
})
10275
})

packages/core/tsconfig.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,5 @@
99
"emitDeclarationOnly": true,
1010
"declarationDir": "dist/types",
1111
},
12-
"include": ["src/**/*.ts", "src/**/*.js"]
12+
"include": ["src/**/*.ts", "src/**/*.js", "safe-json-stringify.d.ts"]
1313
}

packages/delivery-electron/delivery.js

+5-5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
const { createHash } = require('crypto')
2-
const payload = require('@bugsnag/core/lib/json-payload')
2+
const { jsonPayload } = require('@bugsnag/core')
33
const PayloadQueue = require('./queue')
44
const PayloadDeliveryLoop = require('./payload-loop')
55
const NetworkStatus = require('@bugsnag/electron-network-status')
@@ -74,9 +74,9 @@ const delivery = (client, filestore, net, app) => {
7474
const statusUpdater = new NetworkStatus(stateManagerPlugin, net, app)
7575
const { queues } = initRedelivery(filestore.getPaths(), statusUpdater, client._logger, send)
7676

77-
const hash = payload => {
77+
const hash = jsonPayload => {
7878
const h = createHash('sha1')
79-
h.update(payload)
79+
h.update(jsonPayload)
8080
return h.digest('hex')
8181
}
8282

@@ -91,7 +91,7 @@ const delivery = (client, filestore, net, app) => {
9191

9292
let body, opts
9393
try {
94-
body = payload.event(event, client._config.redactedKeys)
94+
body = jsonPayload.event(event, client._config.redactedKeys)
9595
opts = {
9696
url,
9797
method: 'POST',
@@ -131,7 +131,7 @@ const delivery = (client, filestore, net, app) => {
131131

132132
let body, opts
133133
try {
134-
body = payload.session(session, client._config.redactedKeys)
134+
body = jsonPayload.session(session, client._config.redactedKeys)
135135
opts = {
136136
url,
137137
method: 'POST',

packages/delivery-fetch/delivery.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import payload from '@bugsnag/core/lib/json-payload'
1+
import { jsonPayload } from '@bugsnag/core'
22

33
function getIntegrityHeaderValue (sendPayloadChecksums, windowOrWorkerGlobalScope, requestBody, headers) {
44
if (sendPayloadChecksums && windowOrWorkerGlobalScope.isSecureContext && windowOrWorkerGlobalScope.crypto && windowOrWorkerGlobalScope.crypto.subtle && windowOrWorkerGlobalScope.crypto.subtle.digest && typeof TextEncoder === 'function') {
@@ -20,7 +20,7 @@ const delivery = (client, fetch = global.fetch, windowOrWorkerGlobalScope = wind
2020
sendEvent: (event, cb = () => {}) => {
2121
const url = client._config.endpoints.notify
2222

23-
const body = payload.event(event, client._config.redactedKeys)
23+
const body = jsonPayload.event(event, client._config.redactedKeys)
2424

2525
getIntegrityHeaderValue(client._config.sendPayloadChecksums, windowOrWorkerGlobalScope, body).then(integrityHeaderValue => {
2626
const headers = {
@@ -49,7 +49,7 @@ const delivery = (client, fetch = global.fetch, windowOrWorkerGlobalScope = wind
4949
sendSession: (session, cb = () => { }) => {
5050
const url = client._config.endpoints.sessions
5151

52-
const body = payload.session(session, client._config.redactedKeys)
52+
const body = jsonPayload.session(session, client._config.redactedKeys)
5353

5454
getIntegrityHeaderValue(client._config.sendPayloadChecksums, windowOrWorkerGlobalScope, body).then((integrityHeaderValue) => {
5555
const headers = {

0 commit comments

Comments
 (0)