From 8cd1fd1fb3761da8493866c462e0ea822cd61c68 Mon Sep 17 00:00:00 2001 From: Dan Skinner Date: Fri, 12 Feb 2021 10:51:02 +0000 Subject: [PATCH 01/65] fix(plugin-koa): add body to requestInfo --- packages/plugin-koa/src/request-info.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/plugin-koa/src/request-info.js b/packages/plugin-koa/src/request-info.js index a6e0ca757..e5521d38f 100644 --- a/packages/plugin-koa/src/request-info.js +++ b/packages/plugin-koa/src/request-info.js @@ -12,6 +12,7 @@ module.exports = ctx => { headers: request.headers, httpVersion: request.httpVersion, query: ctx.request.query, + body: ctx.request.body, referer: request.headers.referer || request.headers.referrer, clientIp: ctx.ip || (request.connection ? request.connection.remoteAddress : undefined), connection: request.connection ? { From 7bb5ddd9246ef127db4e06641fbe79b055f75270 Mon Sep 17 00:00:00 2001 From: Dan Skinner Date: Fri, 12 Feb 2021 10:51:35 +0000 Subject: [PATCH 02/65] fix(plugin-koa): add body to request in getRequestAndMetadataFromCtx --- packages/plugin-koa/src/koa.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/plugin-koa/src/koa.js b/packages/plugin-koa/src/koa.js index 4c9daf4f8..3fdfd78e8 100644 --- a/packages/plugin-koa/src/koa.js +++ b/packages/plugin-koa/src/koa.js @@ -94,6 +94,7 @@ const getRequestAndMetadataFromCtx = ctx => { return { metadata: requestInfo, request: { + body: requestInfo.body, clientIp: requestInfo.clientIp, headers: requestInfo.headers, httpMethod: requestInfo.httpMethod, From 9d7c76a5956df978f16ac08a7f357e6b54f1d7c6 Mon Sep 17 00:00:00 2001 From: Dan Skinner Date: Fri, 12 Feb 2021 10:51:54 +0000 Subject: [PATCH 03/65] fix(plugin-koa): add httpVersion to request in getRequestAndMetadataFromCtx --- packages/plugin-koa/src/koa.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/plugin-koa/src/koa.js b/packages/plugin-koa/src/koa.js index 3fdfd78e8..661da11dd 100644 --- a/packages/plugin-koa/src/koa.js +++ b/packages/plugin-koa/src/koa.js @@ -98,6 +98,7 @@ const getRequestAndMetadataFromCtx = ctx => { clientIp: requestInfo.clientIp, headers: requestInfo.headers, httpMethod: requestInfo.httpMethod, + httpVersion: requestInfo.httpVersion, url: requestInfo.url, referer: requestInfo.referer } From 017ec57ba0ace21e20b3cb6cd661f8c0f7cbe6f1 Mon Sep 17 00:00:00 2001 From: Dan Skinner Date: Fri, 12 Feb 2021 10:52:30 +0000 Subject: [PATCH 04/65] fix(plugin-koa): determine request and metadata from ctx on error --- packages/plugin-koa/src/koa.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/plugin-koa/src/koa.js b/packages/plugin-koa/src/koa.js index 661da11dd..c8f17ebfa 100644 --- a/packages/plugin-koa/src/koa.js +++ b/packages/plugin-koa/src/koa.js @@ -21,10 +21,10 @@ module.exports = { ctx.bugsnag = requestClient // extract request info and pass it to the relevant bugsnag properties - const { request, metadata } = getRequestAndMetadataFromCtx(ctx) - requestClient.addMetadata('request', metadata) requestClient.addOnError((event) => { + const { request, metadata } = getRequestAndMetadataFromCtx(ctx) event.request = { ...event.request, ...request } + event.addMetadata('request', metadata) }, true) try { From f826b68ff86e69f6785819728be68c41ebcdbb78 Mon Sep 17 00:00:00 2001 From: Joe Haines Date: Mon, 15 Feb 2021 18:01:24 +0000 Subject: [PATCH 05/65] Add MazeRunner scaffolding --- test/aws-lambda/.gitignore | 3 + test/aws-lambda/Gemfile | 6 ++ test/aws-lambda/Gemfile.lock | 85 ++++++++++++++++++++++ test/aws-lambda/features/unhandled.feature | 0 4 files changed, 94 insertions(+) create mode 100644 test/aws-lambda/.gitignore create mode 100644 test/aws-lambda/Gemfile create mode 100644 test/aws-lambda/Gemfile.lock create mode 100644 test/aws-lambda/features/unhandled.feature diff --git a/test/aws-lambda/.gitignore b/test/aws-lambda/.gitignore new file mode 100644 index 000000000..e87177b93 --- /dev/null +++ b/test/aws-lambda/.gitignore @@ -0,0 +1,3 @@ +.aws-sam +node_modules/ +.tgz diff --git a/test/aws-lambda/Gemfile b/test/aws-lambda/Gemfile new file mode 100644 index 000000000..1af5b5bef --- /dev/null +++ b/test/aws-lambda/Gemfile @@ -0,0 +1,6 @@ +source 'https://rubygems.org' + +gem 'bugsnag-maze-runner', git: 'https://github.com/bugsnag/maze-runner', tag: 'v4.9.1' + +# Locally, you can run against Maze Runner branches and uncommitted changes: +#gem 'bugsnag-maze-runner', path: '../../../maze-runner' diff --git a/test/aws-lambda/Gemfile.lock b/test/aws-lambda/Gemfile.lock new file mode 100644 index 000000000..eca5cf10c --- /dev/null +++ b/test/aws-lambda/Gemfile.lock @@ -0,0 +1,85 @@ +GIT + remote: https://github.com/bugsnag/maze-runner + revision: 8677bcf4c1ab27422084b19e7da8149ede954ded + tag: v4.9.1 + specs: + bugsnag-maze-runner (4.9.1) + appium_lib (~> 11.2.0) + boring (~> 0.1.0) + cucumber (~> 3.1.2) + cucumber-expressions (~> 6.0.0) + curb (~> 0.9.6) + gherkin (~> 5.1.0) + minitest (~> 5.0) + optimist (~> 3.0.1) + os (~> 1.0.0) + rake (~> 12.3.3) + selenium-webdriver (~> 3.11) + test-unit (~> 3.3.0) + +GEM + remote: https://rubygems.org/ + specs: + appium_lib (11.2.0) + appium_lib_core (~> 4.1) + nokogiri (~> 1.8, >= 1.8.1) + tomlrb (~> 1.1) + appium_lib_core (4.4.1) + faye-websocket (~> 0.11.0) + selenium-webdriver (~> 3.14, >= 3.14.1) + backports (3.20.2) + boring (0.1.0) + builder (3.2.4) + childprocess (3.0.0) + cucumber (3.1.2) + builder (>= 2.1.2) + cucumber-core (~> 3.2.0) + cucumber-expressions (~> 6.0.1) + cucumber-wire (~> 0.0.1) + diff-lcs (~> 1.3) + gherkin (~> 5.1.0) + multi_json (>= 1.7.5, < 2.0) + multi_test (>= 0.1.2) + cucumber-core (3.2.1) + backports (>= 3.8.0) + cucumber-tag_expressions (~> 1.1.0) + gherkin (~> 5.0) + cucumber-expressions (6.0.1) + cucumber-tag_expressions (1.1.1) + cucumber-wire (0.0.1) + curb (0.9.11) + diff-lcs (1.4.4) + eventmachine (1.2.7) + faye-websocket (0.11.0) + eventmachine (>= 0.12.0) + websocket-driver (>= 0.5.1) + gherkin (5.1.0) + minitest (5.14.3) + multi_json (1.15.0) + multi_test (0.1.2) + nokogiri (1.11.1-x86_64-darwin) + racc (~> 1.4) + optimist (3.0.1) + os (1.0.1) + power_assert (2.0.0) + racc (1.5.2) + rake (12.3.3) + rubyzip (2.3.0) + selenium-webdriver (3.142.7) + childprocess (>= 0.5, < 4.0) + rubyzip (>= 1.2.2) + test-unit (3.3.9) + power_assert + tomlrb (1.3.0) + websocket-driver (0.7.3) + websocket-extensions (>= 0.1.0) + websocket-extensions (0.1.5) + +PLATFORMS + x86_64-darwin-19 + +DEPENDENCIES + bugsnag-maze-runner! + +BUNDLED WITH + 2.2.10 diff --git a/test/aws-lambda/features/unhandled.feature b/test/aws-lambda/features/unhandled.feature new file mode 100644 index 000000000..e69de29bb From ec089aa91845470733993a5034918bdd42227bee Mon Sep 17 00:00:00 2001 From: Joe Haines Date: Tue, 16 Feb 2021 14:12:53 +0000 Subject: [PATCH 06/65] Add simple AWS Lambda fixture --- .../features/fixtures/simple-app/README.md | 17 +++++++ .../simple-app/async/handled-exception.js | 17 +++++++ .../fixtures/simple-app/async/package.json | 21 +++++++++ .../simple-app/async/unhandled-exception.js | 12 +++++ .../events/async/handled-exception.json | 46 +++++++++++++++++++ .../events/async/unhandled-exception.json | 46 +++++++++++++++++++ .../fixtures/simple-app/template.yaml | 37 +++++++++++++++ 7 files changed, 196 insertions(+) create mode 100644 test/aws-lambda/features/fixtures/simple-app/README.md create mode 100644 test/aws-lambda/features/fixtures/simple-app/async/handled-exception.js create mode 100644 test/aws-lambda/features/fixtures/simple-app/async/package.json create mode 100644 test/aws-lambda/features/fixtures/simple-app/async/unhandled-exception.js create mode 100644 test/aws-lambda/features/fixtures/simple-app/events/async/handled-exception.json create mode 100644 test/aws-lambda/features/fixtures/simple-app/events/async/unhandled-exception.json create mode 100644 test/aws-lambda/features/fixtures/simple-app/template.yaml diff --git a/test/aws-lambda/features/fixtures/simple-app/README.md b/test/aws-lambda/features/fixtures/simple-app/README.md new file mode 100644 index 000000000..3c50358cc --- /dev/null +++ b/test/aws-lambda/features/fixtures/simple-app/README.md @@ -0,0 +1,17 @@ +1. Run the build script (requires Ruby): + ```sh + $ ../../scripts/build-fixtures simple-app + ``` + + Or run the build script all fixtures: + + ```sh + $ ../../scripts/build-fixtures + ``` + +1. Run a function: + ```sh + $ BUGSNAG_API_KEY=123 sam local invoke AsyncUnhandledExceptionFunction --event events/async/unhandled-exception.json + ``` + + Check `template.yaml` for all available functions diff --git a/test/aws-lambda/features/fixtures/simple-app/async/handled-exception.js b/test/aws-lambda/features/fixtures/simple-app/async/handled-exception.js new file mode 100644 index 000000000..7f85c8814 --- /dev/null +++ b/test/aws-lambda/features/fixtures/simple-app/async/handled-exception.js @@ -0,0 +1,17 @@ +const Bugsnag = require('@bugsnag/js') + +Bugsnag.start({ + apiKey: process.env.BUGSNAG_API_KEY, + plugins: [] +}) + +const handler = async (event, context) => { + Bugsnag.notify(new Error('Hello!')) + + return { + statusCode: 200, + body: JSON.stringify({ message: 'Did not crash!' }) + } +} + +module.exports.lambdaHandler = handler diff --git a/test/aws-lambda/features/fixtures/simple-app/async/package.json b/test/aws-lambda/features/fixtures/simple-app/async/package.json new file mode 100644 index 000000000..bc11cb60a --- /dev/null +++ b/test/aws-lambda/features/fixtures/simple-app/async/package.json @@ -0,0 +1,21 @@ +{ + "name": "simple-app", + "version": "1.0.0", + "description": "simple lambda app for testing", + "dependencies": { + "@bugsnag/core": "bugsnag-core.tgz", + "@bugsnag/delivery-node": "bugsnag-delivery-node.tgz", + "@bugsnag/js": "bugsnag-js.tgz", + "@bugsnag/node": "bugsnag-node.tgz", + "@bugsnag/plugin-app-duration": "bugsnag-plugin-app-duration.tgz", + "@bugsnag/plugin-contextualize": "bugsnag-plugin-contextualize.tgz", + "@bugsnag/plugin-intercept": "bugsnag-plugin-intercept.tgz", + "@bugsnag/plugin-node-device": "bugsnag-plugin-node-device.tgz", + "@bugsnag/plugin-node-in-project": "bugsnag-plugin-node-in-project.tgz", + "@bugsnag/plugin-node-surrounding-code": "bugsnag-plugin-node-surrounding-code.tgz", + "@bugsnag/plugin-node-uncaught-exception": "bugsnag-plugin-node-uncaught-exception.tgz", + "@bugsnag/plugin-node-unhandled-rejection": "bugsnag-plugin-node-unhandled-rejection.tgz", + "@bugsnag/plugin-server-session": "bugsnag-plugin-server-session.tgz", + "@bugsnag/plugin-strip-project-root": "bugsnag-plugin-strip-project-root.tgz" + } +} diff --git a/test/aws-lambda/features/fixtures/simple-app/async/unhandled-exception.js b/test/aws-lambda/features/fixtures/simple-app/async/unhandled-exception.js new file mode 100644 index 000000000..965a655ab --- /dev/null +++ b/test/aws-lambda/features/fixtures/simple-app/async/unhandled-exception.js @@ -0,0 +1,12 @@ +const Bugsnag = require('@bugsnag/js') + +Bugsnag.start({ + apiKey: process.env.BUGSNAG_API_KEY, + plugins: [] +}) + +const handler = async (event, context) => { + throw new Error('Oh no!') +} + +module.exports.lambdaHandler = handler diff --git a/test/aws-lambda/features/fixtures/simple-app/events/async/handled-exception.json b/test/aws-lambda/features/fixtures/simple-app/events/async/handled-exception.json new file mode 100644 index 000000000..52ba9c07a --- /dev/null +++ b/test/aws-lambda/features/fixtures/simple-app/events/async/handled-exception.json @@ -0,0 +1,46 @@ +{ + "body": "", + "resource": "/{proxy+}", + "path": "/async/handled/exception", + "httpMethod": "GET", + "isBase64Encoded": false, + "queryStringParameters": {}, + "multiValueQueryStringParameters": {}, + "pathParameters": {}, + "stageVariables": {}, + "headers": { + "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", + "Accept-Encoding": "gzip, deflate, sdch", + "Accept-Language": "en-US,en;q=0.8", + "Cache-Control": "max-age=0", + "Host": "1234567890.execute-api.us-east-1.amazonaws.com", + "Upgrade-Insecure-Requests": "1", + "User-Agent": "Custom User Agent String" + }, + "requestContext": { + "accountId": "123456789012", + "resourceId": "123456", + "stage": "prod", + "requestId": "c6af9ac6-7b61-11e6-9a41-93e8deadbeef", + "requestTime": "09/Apr/2015:12:34:56 +0000", + "requestTimeEpoch": 1428582896000, + "identity": { + "cognitoIdentityPoolId": null, + "accountId": null, + "cognitoIdentityId": null, + "caller": null, + "accessKey": null, + "sourceIp": "127.0.0.1", + "cognitoAuthenticationType": null, + "cognitoAuthenticationProvider": null, + "userArn": null, + "userAgent": "Custom User Agent String", + "user": null + }, + "path": "/prod/async/handled/exception", + "resourcePath": "/{proxy+}", + "httpMethod": "GET", + "apiId": "1234567890", + "protocol": "HTTP/1.1" + } +} diff --git a/test/aws-lambda/features/fixtures/simple-app/events/async/unhandled-exception.json b/test/aws-lambda/features/fixtures/simple-app/events/async/unhandled-exception.json new file mode 100644 index 000000000..362dafef6 --- /dev/null +++ b/test/aws-lambda/features/fixtures/simple-app/events/async/unhandled-exception.json @@ -0,0 +1,46 @@ +{ + "body": "", + "resource": "/{proxy+}", + "path": "/async/unhandled/exception", + "httpMethod": "GET", + "isBase64Encoded": false, + "queryStringParameters": {}, + "multiValueQueryStringParameters": {}, + "pathParameters": {}, + "stageVariables": {}, + "headers": { + "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", + "Accept-Encoding": "gzip, deflate, sdch", + "Accept-Language": "en-US,en;q=0.8", + "Cache-Control": "max-age=0", + "Host": "1234567890.execute-api.us-east-1.amazonaws.com", + "Upgrade-Insecure-Requests": "1", + "User-Agent": "Custom User Agent String" + }, + "requestContext": { + "accountId": "123456789012", + "resourceId": "123456", + "stage": "prod", + "requestId": "c6af9ac6-7b61-11e6-9a41-93e8deadbeef", + "requestTime": "09/Apr/2015:12:34:56 +0000", + "requestTimeEpoch": 1428582896000, + "identity": { + "cognitoIdentityPoolId": null, + "accountId": null, + "cognitoIdentityId": null, + "caller": null, + "accessKey": null, + "sourceIp": "127.0.0.1", + "cognitoAuthenticationType": null, + "cognitoAuthenticationProvider": null, + "userArn": null, + "userAgent": "Custom User Agent String", + "user": null + }, + "path": "/prod/async/unhandled/exception", + "resourcePath": "/{proxy+}", + "httpMethod": "GET", + "apiId": "1234567890", + "protocol": "HTTP/1.1" + } +} diff --git a/test/aws-lambda/features/fixtures/simple-app/template.yaml b/test/aws-lambda/features/fixtures/simple-app/template.yaml new file mode 100644 index 000000000..64820ab8a --- /dev/null +++ b/test/aws-lambda/features/fixtures/simple-app/template.yaml @@ -0,0 +1,37 @@ +AWSTemplateFormatVersion: '2010-09-09' +Transform: AWS::Serverless-2016-10-31 +Description: simple-app + +Globals: + Function: + Timeout: 30 + Environment: + Variables: + BUGSNAG_API_KEY: + +Resources: + AsyncUnhandledExceptionFunction: + Type: AWS::Serverless::Function + Properties: + CodeUri: async/ + Handler: unhandled-exception.lambdaHandler + Runtime: nodejs14.x + Events: + AsyncUnhandledException: + Type: Api + Properties: + Path: /async/unhandled/exception + Method: get + + AsyncHandledExceptionFunction: + Type: AWS::Serverless::Function + Properties: + CodeUri: async/ + Handler: handled-exception.lambdaHandler + Runtime: nodejs14.x + Events: + AsyncHandledException: + Type: Api + Properties: + Path: /async/handled/exception + Method: get From 27f0ec1794e66f427c156c3423f5ff067508b557 Mon Sep 17 00:00:00 2001 From: Joe Haines Date: Tue, 16 Feb 2021 14:13:58 +0000 Subject: [PATCH 07/65] Add build-fixtures script This will: 1. Pack all relevant Bugsnag packages 2. Install them into any fixtures in `features/fixtures` 3. Build each fixture 4. Tidy up after itself --- .../features/scripts/build-fixtures | 135 ++++++++++++++++++ 1 file changed, 135 insertions(+) create mode 100755 test/aws-lambda/features/scripts/build-fixtures diff --git a/test/aws-lambda/features/scripts/build-fixtures b/test/aws-lambda/features/scripts/build-fixtures new file mode 100755 index 000000000..9f431ae75 --- /dev/null +++ b/test/aws-lambda/features/scripts/build-fixtures @@ -0,0 +1,135 @@ +#!/usr/bin/env ruby + +require "yaml" +require "fileutils" + +# Allow specifying a specic fixture to build by passing it's name as an argument +# e.g. ./build-fixtures simple-app +SPECIFIC_FIXTURE = ARGV.first + +# Allow debugging by logging more verbosely and not tidying up packed packages +DEBUG = ["1", "true", true].include?(ENV.fetch("DEBUG", false)) + +# The root of bugsnag-js +ROOT = File.realpath("#{__dir__}/../../../../") + +# Bugsnag packages that we need to pack & install into the fixtures +BUGSNAG_PACKAGES = [ + "core", + "delivery-node", + "js", + "node", + "plugin-app-duration", + "plugin-contextualize", + "plugin-intercept", + "plugin-node-device", + "plugin-node-in-project", + "plugin-node-surrounding-code", + "plugin-node-uncaught-exception", + "plugin-node-unhandled-rejection", + "plugin-server-session", + "plugin-strip-project-root", +] + +def heading(message) + <<~HEADING + ====#{"=" * message.length}==== + = #{message} = + ====#{"=" * message.length}==== + HEADING +end + +# Run "npm pack" on the required packages and strip the version suffix so they +# can be depended on in a package.json file +def pack + puts heading("Packing Bugsnag packages") + + BUGSNAG_PACKAGES.each do |package| + path = "#{ROOT}/packages/#{package}" + flags = DEBUG ? "--verbose" : "--quiet" + + success = system("npm pack #{flags} #{path}/") + + raise "Failed to pack #{package}" unless success + end + + # Strip the version suffix from the packages + Dir["#{FileUtils.pwd}/bugsnag-*.tgz"].each do |package| + package_with_no_version = package.gsub(/-\d+\.\d+\.\d+/, '') + File.rename(package, package_with_no_version) + end +end + +# Install the packed packages in each fixture and build the fixture +def install_and_build + Dir["#{__dir__}/../fixtures/*"].each do |fixture| + fixture = File.realpath(fixture) + fixture_name = File.basename(fixture) + + if SPECIFIC_FIXTURE && SPECIFIC_FIXTURE != fixture_name + puts heading("Not building #{fixture_name} because it's not '#{SPECIFIC_FIXTURE}'") + next + end + + puts heading("Installing Bugsnag packages in #{fixture_name}") + + # We have to parse the template file to find out where to put the packages as + # any file outside of the function's directory is unavailable when building + template = "#{fixture}/template.yaml" + + raise "template.yaml does not exist!" unless File.exist?(template) + + parsed_template = YAML.safe_load(File.read(template)) + + # Find all of the directories we need to install the packages in + destinations = parsed_template.fetch("Resources").values.map do |resource| + "#{fixture}/#{resource.fetch("Properties").fetch("CodeUri")}" + end.uniq + + begin + # Install the packages into each of the destination directories + destinations.each do |destination| + puts "Installing packages to #{fixture_name}/#{File.basename(destination)}" + + FileUtils.copy(Dir["#{FileUtils.pwd}/bugsnag-*.tgz"], destination) + end + + puts heading("Building #{fixture_name}") + + # SAM builds projects in the PWD by default. This would mean we build all + # of the fixtures into the same directory so we change directories to + # build in the right place + Dir.chdir(fixture) do + success = system("sam build") + + raise "Failed to build #{fixture_name}" unless success + end + ensure + return if DEBUG + + # Remove the packages from the destination directories as they aren't + # needed anymore. A built lambda has them in its node_modules directory + destinations.each do |destination| + FileUtils.remove(Dir["#{destination}/bugsnag-*.tgz"]) + end + end + end +end + +# Tidy up PWD by removing all the packed packages created by "pack" +def tidy_up + return if DEBUG + + puts heading("Tidying up PWD") + + FileUtils.remove(Dir["#{FileUtils.pwd}/bugsnag-*.tgz"]) +end + +begin + pack + install_and_build +ensure + tidy_up + + puts "Done!" +end From 2b0602883a851141536e419ac7a6bc25c115ab8d Mon Sep 17 00:00:00 2001 From: Joe Haines Date: Tue, 16 Feb 2021 14:04:06 +0000 Subject: [PATCH 08/65] Run build-fixtures scripts when MazeRunner starts --- test/aws-lambda/features/support/env.rb | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 test/aws-lambda/features/support/env.rb diff --git a/test/aws-lambda/features/support/env.rb b/test/aws-lambda/features/support/env.rb new file mode 100644 index 000000000..7e6e907e5 --- /dev/null +++ b/test/aws-lambda/features/support/env.rb @@ -0,0 +1,3 @@ +success = system(File.realpath("#{__dir__}/../scripts/build-fixtures")) + +raise "Unable to build fixtures!" unless success From a335b20bb61281a01b0f28ecd5892de756f15179 Mon Sep 17 00:00:00 2001 From: Joe Haines Date: Tue, 16 Feb 2021 14:04:36 +0000 Subject: [PATCH 09/65] Add two basic tests that prove the lambdas run --- test/aws-lambda/features/handled.feature | 8 ++++++++ test/aws-lambda/features/unhandled.feature | 12 ++++++++++++ 2 files changed, 20 insertions(+) create mode 100644 test/aws-lambda/features/handled.feature diff --git a/test/aws-lambda/features/handled.feature b/test/aws-lambda/features/handled.feature new file mode 100644 index 000000000..0a2f76b14 --- /dev/null +++ b/test/aws-lambda/features/handled.feature @@ -0,0 +1,8 @@ +Feature: Handled exceptions are reported correctly in lambda functions + +Scenario: handled exception in an async lambda + Given I store the api key in the environment variable "BUGSNAG_API_KEY" + When I invoke the "AsyncHandledExceptionFunction" lambda in "features/fixtures/simple-app" with the "events/async/handled-exception.json" event + Then the lambda response "body.message" equals "Did not crash!" + And the lambda response "statusCode" equals 200 + And the SAM exit code equals 0 diff --git a/test/aws-lambda/features/unhandled.feature b/test/aws-lambda/features/unhandled.feature index e69de29bb..9da31490f 100644 --- a/test/aws-lambda/features/unhandled.feature +++ b/test/aws-lambda/features/unhandled.feature @@ -0,0 +1,12 @@ +Feature: Unhandled exceptions are reported correctly in lambda functions + +Scenario: unhandled exception in an async lambda + Given I store the api key in the environment variable "BUGSNAG_API_KEY" + When I invoke the "AsyncUnhandledExceptionFunction" lambda in "features/fixtures/simple-app" with the "events/async/unhandled-exception.json" event + Then the lambda response "errorMessage" equals "Oh no!" + And the lambda response "errorType" equals "Error" + And the lambda response "trace" is an array with 3 elements + And the lambda response "trace.0" equals "Error: Oh no!" + And the lambda response "body" is null + And the lambda response "statusCode" is null + And the SAM exit code equals 0 From 39fe0072eb537a400ca1e7b8691d760fbdadebae Mon Sep 17 00:00:00 2001 From: Joe Haines Date: Tue, 16 Feb 2021 14:15:26 +0000 Subject: [PATCH 10/65] Add basic README for running the tests --- test/aws-lambda/README.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 test/aws-lambda/README.md diff --git a/test/aws-lambda/README.md b/test/aws-lambda/README.md new file mode 100644 index 000000000..109684d74 --- /dev/null +++ b/test/aws-lambda/README.md @@ -0,0 +1,17 @@ +## Setup + +Install Maze Runner: + +```sh +$ bundle install +``` + +## Running the tests + +Run Maze Runner: + +```sh +$ bundle exec maze-runner +``` + +This will build all of the fixtures before running the tests From 7181fffd7b888bc2c96f10687525acfebc9c6b41 Mon Sep 17 00:00:00 2001 From: Joe Haines Date: Wed, 17 Feb 2021 11:32:36 +0000 Subject: [PATCH 11/65] Add AWS SAM install instructions to readme --- test/aws-lambda/README.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/test/aws-lambda/README.md b/test/aws-lambda/README.md index 109684d74..a3500c031 100644 --- a/test/aws-lambda/README.md +++ b/test/aws-lambda/README.md @@ -1,10 +1,12 @@ ## Setup -Install Maze Runner: +1. [Install the AWS SAM CLI](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html) -```sh -$ bundle install -``` +1. Install Maze Runner: + + ```sh + $ bundle install + ``` ## Running the tests From 9a2c8f372c369023b35b81a7c19a5d6e680fa128 Mon Sep 17 00:00:00 2001 From: Joe Haines Date: Wed, 17 Feb 2021 11:41:01 +0000 Subject: [PATCH 12/65] Check SAM is installed before running --- test/aws-lambda/features/support/env.rb | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/test/aws-lambda/features/support/env.rb b/test/aws-lambda/features/support/env.rb index 7e6e907e5..b86af7f3b 100644 --- a/test/aws-lambda/features/support/env.rb +++ b/test/aws-lambda/features/support/env.rb @@ -1,3 +1,19 @@ +`command -v sam` + +if $? != 0 + puts <<~ERROR + The AWS SAM CLI must be installed before running these tests! + + See the installation instructions: + https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html + + If you have already installed the SAM CLI, check that it's in your PATH: + $ command -v sam + ERROR + + exit 127 +end + success = system(File.realpath("#{__dir__}/../scripts/build-fixtures")) raise "Unable to build fixtures!" unless success From 594d475c44956cf8aed4c3d27961b5ef8000e445 Mon Sep 17 00:00:00 2001 From: Joe Haines Date: Wed, 17 Feb 2021 11:41:15 +0000 Subject: [PATCH 13/65] exit rather than raise This stops a ruby backtrace from showing, which can be confusing - the error happened in the 'build-fixtures' script rather than env.rb --- test/aws-lambda/features/support/env.rb | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/test/aws-lambda/features/support/env.rb b/test/aws-lambda/features/support/env.rb index b86af7f3b..6029b4e5f 100644 --- a/test/aws-lambda/features/support/env.rb +++ b/test/aws-lambda/features/support/env.rb @@ -16,4 +16,7 @@ success = system(File.realpath("#{__dir__}/../scripts/build-fixtures")) -raise "Unable to build fixtures!" unless success +unless success + puts "Unable to build fixtures!" + exit 1 +end From b869a23558c09dbf3825aa79755762327b9b5b76 Mon Sep 17 00:00:00 2001 From: Dan Skinner Date: Fri, 19 Feb 2021 14:51:05 +0000 Subject: [PATCH 14/65] fix(plugin-koa): include body in request only --- TESTING.md | 2 +- packages/plugin-koa/src/koa.js | 8 +- .../features/fixtures/koa/package-lock.json | 93 +++++++++++++++++++ test/node/features/fixtures/koa/package.json | 3 +- .../features/fixtures/koa/scenarios/app.js | 5 + test/node/features/koa.feature | 13 +++ 6 files changed, 119 insertions(+), 5 deletions(-) diff --git a/TESTING.md b/TESTING.md index 3333dc8be..00fdbddfd 100644 --- a/TESTING.md +++ b/TESTING.md @@ -69,7 +69,7 @@ You'll need to set the credentials for the aws profile in order to access the pr aws configure --profile=opensource ``` -Subsequently you'll need to run the following commmand to authenticate with the registry: +Subsequently you'll need to run the following command to authenticate with the registry: ``` npm run test:test-container-registry-login diff --git a/packages/plugin-koa/src/koa.js b/packages/plugin-koa/src/koa.js index c8f17ebfa..0419b7a7d 100644 --- a/packages/plugin-koa/src/koa.js +++ b/packages/plugin-koa/src/koa.js @@ -90,17 +90,19 @@ module.exports = { } const getRequestAndMetadataFromCtx = ctx => { - const requestInfo = extractRequestInfo(ctx) + // Exclude new mappings from metaData but keep existing ones to preserve backwards compatibility + const { body, ...requestInfo } = extractRequestInfo(ctx) + return { metadata: requestInfo, request: { - body: requestInfo.body, + body, clientIp: requestInfo.clientIp, headers: requestInfo.headers, httpMethod: requestInfo.httpMethod, httpVersion: requestInfo.httpVersion, url: requestInfo.url, - referer: requestInfo.referer + referer: requestInfo.referer // Not part of the notifier spec for request but leaving for backwards compatibility } } } diff --git a/test/node/features/fixtures/koa/package-lock.json b/test/node/features/fixtures/koa/package-lock.json index 8979c67a5..5c0c53326 100644 --- a/test/node/features/fixtures/koa/package-lock.json +++ b/test/node/features/fixtures/koa/package-lock.json @@ -17,6 +17,11 @@ "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", "integrity": "sha1-q8av7tzqUugJzcA3au0845Y10X8=" }, + "bytes": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", + "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" + }, "cache-content-type": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/cache-content-type/-/cache-content-type-1.0.1.tgz", @@ -31,6 +36,17 @@ "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=" }, + "co-body": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/co-body/-/co-body-6.1.0.tgz", + "integrity": "sha512-m7pOT6CdLN7FuXUcpuz/8lfQ/L77x8SchHCF4G0RBTJO20Wzmhn5Sp4/5WsKy8OSpifBSUrmg83qEqaDHdyFuQ==", + "requires": { + "inflation": "^2.0.0", + "qs": "^6.5.2", + "raw-body": "^2.3.3", + "type-is": "^1.6.16" + } + }, "content-disposition": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", @@ -50,6 +66,11 @@ "keygrip": "~1.0.2" } }, + "copy-to": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/copy-to/-/copy-to-2.0.1.tgz", + "integrity": "sha1-JoD7uAaKSNCGVrYJgJK9r8kG9KU=" + }, "debug": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", @@ -132,6 +153,19 @@ "toidentifier": "1.0.0" } }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "inflation": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/inflation/-/inflation-2.0.0.tgz", + "integrity": "sha1-i0F+R8KPklpFEz2RTKH9OJEH8w8=" + }, "inherits": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", @@ -178,6 +212,15 @@ "vary": "^1.1.2" } }, + "koa-bodyparser": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/koa-bodyparser/-/koa-bodyparser-4.3.0.tgz", + "integrity": "sha512-uyV8G29KAGwZc4q/0WUAjH+Tsmuv9ImfBUF2oZVyZtaeo0husInagyn/JH85xMSxM0hEk/mbCII5ubLDuqW/Rw==", + "requires": { + "co-body": "^6.0.0", + "copy-to": "^2.0.1" + } + }, "koa-compose": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/koa-compose/-/koa-compose-4.1.0.tgz", @@ -253,6 +296,51 @@ "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=" }, + "qs": { + "version": "6.9.6", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.6.tgz", + "integrity": "sha512-TIRk4aqYLNoJUbd+g2lEdz5kLWIuTMRagAXxl78Q0RiVjAOugHmeKNGdd3cwo/ktpf9aL9epCfFqWDEKysUlLQ==" + }, + "raw-body": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.1.tgz", + "integrity": "sha512-9WmIKF6mkvA0SLmA2Knm9+qj89e+j1zqgyn8aXGd7+nAduPoqgI9lO57SAZNn/Byzo5P7JhXTyg9PzaJbH73bA==", + "requires": { + "bytes": "3.1.0", + "http-errors": "1.7.3", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "dependencies": { + "http-errors": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.3.tgz", + "integrity": "sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw==", + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.4", + "setprototypeof": "1.1.1", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "setprototypeof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", + "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" + } + } + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, "setprototypeof": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", @@ -277,6 +365,11 @@ "mime-types": "~2.1.18" } }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" + }, "vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", diff --git a/test/node/features/fixtures/koa/package.json b/test/node/features/fixtures/koa/package.json index b1ec2eea6..cee6b46c3 100644 --- a/test/node/features/fixtures/koa/package.json +++ b/test/node/features/fixtures/koa/package.json @@ -1,6 +1,7 @@ { "name": "bugsnag-test", "dependencies": { - "koa": "^2.5.2" + "koa": "^2.5.2", + "koa-bodyparser": "^4.3.0" } } diff --git a/test/node/features/fixtures/koa/scenarios/app.js b/test/node/features/fixtures/koa/scenarios/app.js index 2aae3c6bf..163d9801b 100644 --- a/test/node/features/fixtures/koa/scenarios/app.js +++ b/test/node/features/fixtures/koa/scenarios/app.js @@ -1,6 +1,7 @@ const Bugsnag = require('@bugsnag/node') const bugsnagKoa = require('@bugsnag/plugin-koa') const Koa = require('koa') +const bodyParser = require('koa-bodyparser'); Bugsnag.start({ apiKey: process.env.BUGSNAG_API_KEY, @@ -36,6 +37,8 @@ app.use(async (ctx, next) => { app.use(middleware.requestHandler) +app.use(bodyParser()); + const erroneous = () => new Promise((resolve, reject) => reject(new Error('async noooop'))) app.use(async (ctx, next) => { @@ -54,6 +57,8 @@ app.use(async (ctx, next) => { } else if (ctx.path === '/handled') { ctx.bugsnag.notify(new Error('handled')) await next() + } else if (ctx.path === '/bodytest') { + throw new Error('request body') } else { await next() } diff --git a/test/node/features/koa.feature b/test/node/features/koa.feature index 3c6edd9ad..f85b80965 100644 --- a/test/node/features/koa.feature +++ b/test/node/features/koa.feature @@ -90,3 +90,16 @@ Scenario: A handled error with ctx.bugsnag.notify() And the event "request.url" equals "http://koa/handled" And the event "request.httpMethod" equals "GET" And the event "request.clientIp" is not null + +Scenario: adding body to request metadata + When I POST the data "data=in_request_body" to the URL "http://koa/bodytest" + And I wait to receive a request + Then the request is valid for the error reporting API version "4" for the "Bugsnag Node" notifier + And the event "unhandled" is true + And the event "severity" equals "error" + And the exception "errorClass" equals "Error" + And the exception "message" equals "request body" + And the exception "type" equals "nodejs" + And the "file" of stack frame 0 equals "scenarios/app.js" + And the event "metaData.request.body.data" equals "in_request_body" + And the event "request.httpMethod" equals "POST" From 443cbefcbdd6e0eae26555f0b5873123b1925217 Mon Sep 17 00:00:00 2001 From: Dan Skinner Date: Fri, 19 Feb 2021 15:20:28 +0000 Subject: [PATCH 15/65] fix(plugin-koa): correct test fixture --- test/node/features/koa.feature | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/node/features/koa.feature b/test/node/features/koa.feature index f85b80965..ddbb689ac 100644 --- a/test/node/features/koa.feature +++ b/test/node/features/koa.feature @@ -101,5 +101,5 @@ Scenario: adding body to request metadata And the exception "message" equals "request body" And the exception "type" equals "nodejs" And the "file" of stack frame 0 equals "scenarios/app.js" - And the event "metaData.request.body.data" equals "in_request_body" + And the event "request.body.data" equals "in_request_body" And the event "request.httpMethod" equals "POST" From 93b1fc4b0a0fe32f0c99e0807c2dba8bcf6ecce2 Mon Sep 17 00:00:00 2001 From: Dan Skinner Date: Fri, 19 Feb 2021 17:39:23 +0000 Subject: [PATCH 16/65] chore: update changelog --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 068adfdcd..515109454 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## TBD + +### Changed + +- (plugin-koa): Ensure `ctx.request.body` is present on the event's request property by collecting it at the last possible moment [#1292](https://github.com/bugsnag/bugsnag-js/pull/1292) + ## v7.7.0 (2021-02-15) ### Changed From 1387119141b1525c0c3e69a7cb9b221eb8ba1e71 Mon Sep 17 00:00:00 2001 From: Joe Haines Date: Wed, 24 Feb 2021 12:18:44 +0000 Subject: [PATCH 17/65] Add '_depth' to internal Client type --- packages/core/client.d.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/core/client.d.ts b/packages/core/client.d.ts index 8b251eb1c..72f30ae42 100644 --- a/packages/core/client.d.ts +++ b/packages/core/client.d.ts @@ -41,6 +41,7 @@ interface Delivery { export default class ClientWithInternals extends Client { public constructor(opts: T, schema?: {[key: string]: any}, internalPlugins?: Plugin[], notifier?: Notifier) _config: T + _depth: number _logger: LoggerConfig _breadcrumbs: Breadcrumb[]; _delivery: Delivery From 5162bb8ef88f7862a8e6abcba59fdbeb3026c980 Mon Sep 17 00:00:00 2001 From: Joe Haines Date: Wed, 24 Feb 2021 11:32:33 +0000 Subject: [PATCH 18/65] Initial package scaffolding --- jest.config.js | 1 + packages/in-flight/LICENSE.txt | 19 ++++++++++++ packages/in-flight/README.md | 7 +++++ packages/in-flight/package-lock.json | 13 ++++++++ packages/in-flight/package.json | 30 +++++++++++++++++++ packages/in-flight/src/in-flight.js | 0 packages/in-flight/test/in-flight.test.ts | 7 +++++ .../in-flight/types/bugsnag-in-flight.d.ts | 0 tsconfig.json | 1 + 9 files changed, 78 insertions(+) create mode 100644 packages/in-flight/LICENSE.txt create mode 100644 packages/in-flight/README.md create mode 100644 packages/in-flight/package-lock.json create mode 100644 packages/in-flight/package.json create mode 100644 packages/in-flight/src/in-flight.js create mode 100644 packages/in-flight/test/in-flight.test.ts create mode 100644 packages/in-flight/types/bugsnag-in-flight.d.ts diff --git a/jest.config.js b/jest.config.js index 7611ae701..527502ae9 100644 --- a/jest.config.js +++ b/jest.config.js @@ -78,6 +78,7 @@ module.exports = { ]), project('node plugins', [ 'delivery-node', + 'in-flight', 'plugin-express', 'plugin-koa', 'plugin-restify', diff --git a/packages/in-flight/LICENSE.txt b/packages/in-flight/LICENSE.txt new file mode 100644 index 000000000..ddc0631e2 --- /dev/null +++ b/packages/in-flight/LICENSE.txt @@ -0,0 +1,19 @@ +Copyright (c) Bugsnag, https://www.bugsnag.com/ + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/packages/in-flight/README.md b/packages/in-flight/README.md new file mode 100644 index 000000000..388679e7a --- /dev/null +++ b/packages/in-flight/README.md @@ -0,0 +1,7 @@ +# @bugsnag/in-flight + +Internal [@bugsnag/js](https://github.com/bugsnag/bugsnag-js) package to keep track of in-flight requests + +## License + +This package is free software released under the MIT License. See [LICENSE.txt](./LICENSE.txt) for details. diff --git a/packages/in-flight/package-lock.json b/packages/in-flight/package-lock.json new file mode 100644 index 000000000..d0881cfb5 --- /dev/null +++ b/packages/in-flight/package-lock.json @@ -0,0 +1,13 @@ +{ + "name": "@bugsnag/in-flight", + "version": "7.7.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@bugsnag/cuid": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@bugsnag/cuid/-/cuid-3.0.0.tgz", + "integrity": "sha512-LOt8aaBI+KvOQGneBtpuCz3YqzyEAehd1f3nC5yr9TIYW1+IzYKa2xWS4EiMz5pPOnRPHkyyS5t/wmSmN51Gjg==" + } + } +} diff --git a/packages/in-flight/package.json b/packages/in-flight/package.json new file mode 100644 index 000000000..4e08fd75f --- /dev/null +++ b/packages/in-flight/package.json @@ -0,0 +1,30 @@ +{ + "name": "@bugsnag/in-flight", + "version": "7.7.0", + "main": "src/in-flight.js", + "types": "types/bugsnag-in-flight.d.ts", + "description": "Internal package to keep track of in-flight requests to Bugsnag", + "homepage": "https://www.bugsnag.com/", + "repository": { + "type": "git", + "url": "git@github.com:bugsnag/bugsnag-js.git" + }, + "publishConfig": { + "access": "public" + }, + "files": [ + "src", + "types" + ], + "author": "Bugsnag", + "license": "MIT", + "dependencies": { + "@bugsnag/cuid": "^3.0.0" + }, + "devDependencies": { + "@bugsnag/core": "^7.7.0" + }, + "peerDependencies": { + "@bugsnag/core": "^7.0.0" + } +} diff --git a/packages/in-flight/src/in-flight.js b/packages/in-flight/src/in-flight.js new file mode 100644 index 000000000..e69de29bb diff --git a/packages/in-flight/test/in-flight.test.ts b/packages/in-flight/test/in-flight.test.ts new file mode 100644 index 000000000..b483b2b84 --- /dev/null +++ b/packages/in-flight/test/in-flight.test.ts @@ -0,0 +1,7 @@ +describe('@bugsnag/in-flight', () => { + it.todo('tracks in-flight events') + it.todo('tracks in-flight sessions') + it.todo('tracks all in-flight requests') + it.todo('can flush successfully') + it.todo('will timeout if flush takes too long') +}) diff --git a/packages/in-flight/types/bugsnag-in-flight.d.ts b/packages/in-flight/types/bugsnag-in-flight.d.ts new file mode 100644 index 000000000..e69de29bb diff --git a/tsconfig.json b/tsconfig.json index c5eb79256..eb5dc389d 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -70,6 +70,7 @@ "packages/delivery-react-native", "packages/delivery-x-domain-request", "packages/delivery-xml-http-request", + "packages/in-flight", "packages/plugin-app-duration", "packages/plugin-browser-context", "packages/plugin-browser-device", From ca27c26999657c51829a6d24a4c555107af63baf Mon Sep 17 00:00:00 2001 From: Joe Haines Date: Wed, 24 Feb 2021 12:19:04 +0000 Subject: [PATCH 19/65] Allow waiting for in-flight requests to complete This monkey-patches the given client to keep track of the number of in-flight requests and allows waiting until all requests have finished (with a timeout) --- packages/in-flight/src/in-flight.js | 67 ++++++ packages/in-flight/test/in-flight.test.ts | 222 +++++++++++++++++- .../in-flight/types/bugsnag-in-flight.d.ts | 8 + 3 files changed, 292 insertions(+), 5 deletions(-) diff --git a/packages/in-flight/src/in-flight.js b/packages/in-flight/src/in-flight.js index e69de29bb..89fc7bb07 100644 --- a/packages/in-flight/src/in-flight.js +++ b/packages/in-flight/src/in-flight.js @@ -0,0 +1,67 @@ +const cuid = require('@bugsnag/cuid') + +const FLUSH_POLL_INTERVAL_MS = 50 +const inFlightRequests = new Map() + +const noop = () => {} + +module.exports = { + trackInFlight (client) { + const originalNotify = client._notify + + client._notify = function (event, onError, callback = noop) { + const id = cuid() + inFlightRequests.set(id, true) + + const _callback = function () { + inFlightRequests.delete(id) + callback.apply(null, arguments) + } + + client._depth += 1 + + try { + originalNotify.call(client, event, onError, _callback) + } finally { + client._depth -= 1 + } + } + + const delivery = client._delivery + const originalSendSession = delivery.sendSession + + delivery.sendSession = function (session, callback = noop) { + const id = cuid() + inFlightRequests.set(id, true) + + const _callback = function () { + inFlightRequests.delete(id) + callback.apply(null, arguments) + } + + originalSendSession.call(delivery, session, _callback) + } + }, + + flush (timeoutMs) { + return new Promise(function (resolve, reject) { + const timeout = setTimeout( + () => { reject(new Error(`flush timed out after ${timeoutMs}ms`)) }, + timeoutMs + ) + + const resolveIfNoRequests = function () { + if (inFlightRequests.size === 0) { + clearTimeout(timeout) + resolve() + + return + } + + setTimeout(resolveIfNoRequests, FLUSH_POLL_INTERVAL_MS) + } + + resolveIfNoRequests() + }) + } +} diff --git a/packages/in-flight/test/in-flight.test.ts b/packages/in-flight/test/in-flight.test.ts index b483b2b84..76abb4cb7 100644 --- a/packages/in-flight/test/in-flight.test.ts +++ b/packages/in-flight/test/in-flight.test.ts @@ -1,7 +1,219 @@ +import Client, { EventDeliveryPayload, SessionDeliveryPayload } from '@bugsnag/core/client' + +// The in-flight package has module level state which can leak between tests +// We can avoid this using jest's 'isolateModules' but need to type the +// 'bugsnagInFlight' variable for this test to compile +import BugsnagInFlightJustForTypescript from '../types/bugsnag-in-flight' + +let bugsnagInFlight: BugsnagInFlightJustForTypescript +jest.isolateModules(() => { bugsnagInFlight = require('../src/in-flight') }) + describe('@bugsnag/in-flight', () => { - it.todo('tracks in-flight events') - it.todo('tracks in-flight sessions') - it.todo('tracks all in-flight requests') - it.todo('can flush successfully') - it.todo('will timeout if flush takes too long') + it('tracks in-flight events', () => { + const client = new Client({ apiKey: 'AN_API_KEY' }) + const payloads: EventDeliveryPayload[] = [] + const sendSession = jest.fn() + + client._setDelivery(() => ({ + sendEvent: (payload, cb) => { + expect(client._depth).toBe(2) + payloads.push(payload) + cb() + }, + sendSession + })) + + bugsnagInFlight.trackInFlight(client) + + expect(payloads.length).toBe(0) + + const onError = jest.fn() + const callback = jest.fn() + + expect(client._depth).toBe(1) + + client.notify(new Error('xyz'), onError, callback) + + expect(client._depth).toBe(1) + expect(onError).toHaveBeenCalledTimes(1) + expect(callback).toHaveBeenCalledTimes(1) + expect(payloads.length).toBe(1) + expect(sendSession).not.toHaveBeenCalled() + }) + + it('tracks in-flight sessions', () => { + const client = new Client({ apiKey: 'AN_API_KEY' }) + const payloads: SessionDeliveryPayload[] = [] + const sendEvent = jest.fn() + const callback = jest.fn() + + client._sessionDelegate = { + startSession: jest.fn(function (client, session) { + client._delivery.sendSession(session, callback) + }), + pauseSession: jest.fn(), + resumeSession: jest.fn() + } + + client._setDelivery(() => ({ + sendEvent, + sendSession: (payload, cb) => { + payloads.push(payload) + cb() + } + })) + + bugsnagInFlight.trackInFlight(client) + + expect(payloads.length).toBe(0) + expect(callback).not.toHaveBeenCalled() + expect(client._sessionDelegate.startSession).not.toHaveBeenCalled() + expect(client._sessionDelegate.pauseSession).not.toHaveBeenCalled() + expect(client._sessionDelegate.resumeSession).not.toHaveBeenCalled() + + client.startSession() + + expect(payloads.length).toBe(1) + expect(callback).toHaveBeenCalledTimes(1) + expect(client._sessionDelegate.startSession).toHaveBeenCalledTimes(1) + expect(client._sessionDelegate.pauseSession).not.toHaveBeenCalled() + expect(client._sessionDelegate.resumeSession).not.toHaveBeenCalled() + }) + + it('tracks all in-flight requests', () => { + const client = new Client({ apiKey: 'AN_API_KEY' }) + const eventPayloads: EventDeliveryPayload[] = [] + const sessionPayloads: SessionDeliveryPayload[] = [] + const sessionCallback = jest.fn() + + client._sessionDelegate = { + startSession: jest.fn(function (client, session) { + client._delivery.sendSession(session, sessionCallback) + }), + pauseSession: jest.fn(), + resumeSession: jest.fn() + } + + client._setDelivery(() => ({ + sendEvent: (payload, cb) => { + expect(client._depth).toBe(2) + eventPayloads.push(payload) + cb() + }, + sendSession: (payload, cb) => { + sessionPayloads.push(payload) + cb() + } + })) + + bugsnagInFlight.trackInFlight(client) + + expect(eventPayloads.length).toBe(0) + expect(sessionPayloads.length).toBe(0) + + const onError = jest.fn() + const notifyCallback = jest.fn() + + expect(client._depth).toBe(1) + + client.notify(new Error('xyz'), onError, notifyCallback) + client.startSession() + + expect(client._depth).toBe(1) + expect(onError).toHaveBeenCalledTimes(1) + expect(notifyCallback).toHaveBeenCalledTimes(1) + expect(sessionCallback).toHaveBeenCalledTimes(1) + expect(eventPayloads.length).toBe(1) + expect(sessionPayloads.length).toBe(1) + }) + + it('can flush successfully', async () => { + const client = new Client({ apiKey: 'AN_API_KEY' }) + const eventPayloads: EventDeliveryPayload[] = [] + const sessionPayloads: SessionDeliveryPayload[] = [] + + client._sessionDelegate = { + startSession (client, session) { + client._delivery.sendSession(session, () => {}) + }, + pauseSession: () => {}, + resumeSession: () => {} + } + + client._setDelivery(() => ({ + sendEvent (payload, cb) { + setTimeout(function () { + eventPayloads.push(payload) + cb() + }, 100) + }, + sendSession (payload, cb) { + setTimeout(function () { + sessionPayloads.push(payload) + cb() + }, 100) + } + })) + + bugsnagInFlight.trackInFlight(client) + + client.notify(new Error('xyz')) + client.startSession() + + expect(eventPayloads.length).toBe(0) + expect(sessionPayloads.length).toBe(0) + + await bugsnagInFlight.flush(1000) + + expect(eventPayloads.length).toBe(1) + expect(sessionPayloads.length).toBe(1) + }) + + it('will timeout if flush takes too long', async () => { + const client = new Client({ apiKey: 'AN_API_KEY' }) + const eventPayloads: EventDeliveryPayload[] = [] + const sessionPayloads: SessionDeliveryPayload[] = [] + + client._sessionDelegate = { + startSession: (client, session) => { + client._delivery.sendSession(session, () => {}) + }, + pauseSession: () => {}, + resumeSession: () => {} + } + + client._setDelivery(() => ({ + sendEvent (payload, cb) { + setTimeout(() => { + eventPayloads.push(payload) + cb() + }, 250) + }, + sendSession (payload, cb) { + setTimeout(() => { + sessionPayloads.push(payload) + cb() + }, 250) + } + })) + + bugsnagInFlight.trackInFlight(client) + + client.notify(new Error('xyz')) + client.startSession() + + expect(eventPayloads.length).toBe(0) + expect(sessionPayloads.length).toBe(0) + + const expected = new Error('flush timed out after 10ms') + await expect(() => bugsnagInFlight.flush(10)).rejects.toThrow(expected) + + expect(eventPayloads.length).toBe(0) + expect(sessionPayloads.length).toBe(0) + + await bugsnagInFlight.flush(1000) + + expect(eventPayloads.length).toBe(1) + expect(sessionPayloads.length).toBe(1) + }) }) diff --git a/packages/in-flight/types/bugsnag-in-flight.d.ts b/packages/in-flight/types/bugsnag-in-flight.d.ts index e69de29bb..c2dd1a86e 100644 --- a/packages/in-flight/types/bugsnag-in-flight.d.ts +++ b/packages/in-flight/types/bugsnag-in-flight.d.ts @@ -0,0 +1,8 @@ +import { Client } from '@bugsnag/core' + +interface BugsnagInFlight { + trackInFlight (client: Client): void + flush (timeoutMs: number): Promise +} + +export default BugsnagInFlight From 99d3e00c267e83f22af87f3d56e0faa99f7359d6 Mon Sep 17 00:00:00 2001 From: Joe Haines Date: Wed, 24 Feb 2021 17:39:26 +0000 Subject: [PATCH 20/65] Avoid resolving after ject has been called This should never cause issues as the ES spec guarantees promises that are resolved/rejected can't be resolved or rejected again, however this is such a small change and is a bit more complete/safe --- packages/in-flight/src/in-flight.js | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/packages/in-flight/src/in-flight.js b/packages/in-flight/src/in-flight.js index 89fc7bb07..c85b287fa 100644 --- a/packages/in-flight/src/in-flight.js +++ b/packages/in-flight/src/in-flight.js @@ -45,20 +45,25 @@ module.exports = { flush (timeoutMs) { return new Promise(function (resolve, reject) { - const timeout = setTimeout( - () => { reject(new Error(`flush timed out after ${timeoutMs}ms`)) }, + let resolveTimeout + const rejectTimeout = setTimeout( + () => { + if (resolveTimeout) clearTimeout(resolveTimeout) + + reject(new Error(`flush timed out after ${timeoutMs}ms`)) + }, timeoutMs ) const resolveIfNoRequests = function () { if (inFlightRequests.size === 0) { - clearTimeout(timeout) + clearTimeout(rejectTimeout) resolve() return } - setTimeout(resolveIfNoRequests, FLUSH_POLL_INTERVAL_MS) + resolveTimeout = setTimeout(resolveIfNoRequests, FLUSH_POLL_INTERVAL_MS) } resolveIfNoRequests() From f16b1164d5d28e4accd9704ba79d0904b651d969 Mon Sep 17 00:00:00 2001 From: Joe Haines Date: Thu, 25 Feb 2021 15:55:29 +0000 Subject: [PATCH 21/65] Track requests when delivery is changed --- packages/in-flight/src/in-flight.js | 35 ++++++++--- packages/in-flight/test/in-flight.test.ts | 75 +++++++++++++++++++++++ 2 files changed, 100 insertions(+), 10 deletions(-) diff --git a/packages/in-flight/src/in-flight.js b/packages/in-flight/src/in-flight.js index c85b287fa..364d27022 100644 --- a/packages/in-flight/src/in-flight.js +++ b/packages/in-flight/src/in-flight.js @@ -27,20 +27,35 @@ module.exports = { } } - const delivery = client._delivery - const originalSendSession = delivery.sendSession + const patchDelivery = (delivery) => { + const originalSendSession = delivery.sendSession - delivery.sendSession = function (session, callback = noop) { - const id = cuid() - inFlightRequests.set(id, true) + delivery.sendSession = function (session, callback = noop) { + const id = cuid() + inFlightRequests.set(id, true) - const _callback = function () { - inFlightRequests.delete(id) - callback.apply(null, arguments) - } + const _callback = function () { + inFlightRequests.delete(id) + callback.apply(null, arguments) + } - originalSendSession.call(delivery, session, _callback) + originalSendSession.call(delivery, session, _callback) + } } + + let delivery = client._delivery + patchDelivery(delivery) + + // ensure we also monkey-patch any new delivery that might be set + Object.defineProperty(client, '_delivery', { + get () { + return delivery + }, + set (newDeliviery) { + patchDelivery(newDeliviery) + delivery = newDeliviery + } + }) }, flush (timeoutMs) { diff --git a/packages/in-flight/test/in-flight.test.ts b/packages/in-flight/test/in-flight.test.ts index 76abb4cb7..b9c009877 100644 --- a/packages/in-flight/test/in-flight.test.ts +++ b/packages/in-flight/test/in-flight.test.ts @@ -216,4 +216,79 @@ describe('@bugsnag/in-flight', () => { expect(eventPayloads.length).toBe(1) expect(sessionPayloads.length).toBe(1) }) + + it('can track requests when delivery is changed', async () => { + const client = new Client({ apiKey: 'AN_API_KEY' }) + const originalEventPayloads: EventDeliveryPayload[] = [] + const originalSessionPayloads: SessionDeliveryPayload[] = [] + + client._sessionDelegate = { + startSession (client, session) { + client._delivery.sendSession(session, () => {}) + }, + pauseSession: () => {}, + resumeSession: () => {} + } + + client._setDelivery(() => ({ + sendEvent (payload, cb) { + setTimeout(function () { + originalEventPayloads.push(payload) + cb() + }, 100) + }, + sendSession (payload, cb) { + setTimeout(function () { + originalSessionPayloads.push(payload) + cb() + }, 100) + } + })) + + bugsnagInFlight.trackInFlight(client) + + client.notify(new Error('xyz')) + client.startSession() + + expect(originalEventPayloads.length).toBe(0) + expect(originalSessionPayloads.length).toBe(0) + + await bugsnagInFlight.flush(1000) + + expect(originalEventPayloads.length).toBe(1) + expect(originalSessionPayloads.length).toBe(1) + + const newEventPayloads: EventDeliveryPayload[] = [] + const newSessionPayloads: SessionDeliveryPayload[] = [] + + client._setDelivery(() => ({ + sendEvent (payload, cb) { + setTimeout(function () { + newEventPayloads.push(payload) + cb() + }, 100) + }, + sendSession (payload, cb) { + setTimeout(function () { + newSessionPayloads.push(payload) + cb() + }, 100) + } + })) + + client.notify(new Error('xyz')) + client.startSession() + + expect(originalEventPayloads.length).toBe(1) + expect(originalSessionPayloads.length).toBe(1) + expect(newEventPayloads.length).toBe(0) + expect(newSessionPayloads.length).toBe(0) + + await bugsnagInFlight.flush(1000) + + expect(originalEventPayloads.length).toBe(1) + expect(originalSessionPayloads.length).toBe(1) + expect(newEventPayloads.length).toBe(1) + expect(newSessionPayloads.length).toBe(1) + }) }) From 6ba4042fc0bea5693bc8c953f12f689544b51ff7 Mon Sep 17 00:00:00 2001 From: Joe Haines Date: Mon, 22 Feb 2021 14:58:35 +0000 Subject: [PATCH 22/65] Initial plugin scaffolding --- jest.config.js | 1 + packages/plugin-aws-lambda/LICENSE.txt | 19 ++++++++++ packages/plugin-aws-lambda/README.md | 7 ++++ packages/plugin-aws-lambda/package.json | 35 +++++++++++++++++++ packages/plugin-aws-lambda/src/index.js | 0 packages/plugin-aws-lambda/test/index.test.ts | 0 .../types/bugsnag-plugin-aws-lambda.d.ts | 0 tsconfig.json | 1 + 8 files changed, 63 insertions(+) create mode 100644 packages/plugin-aws-lambda/LICENSE.txt create mode 100644 packages/plugin-aws-lambda/README.md create mode 100644 packages/plugin-aws-lambda/package.json create mode 100644 packages/plugin-aws-lambda/src/index.js create mode 100644 packages/plugin-aws-lambda/test/index.test.ts create mode 100644 packages/plugin-aws-lambda/types/bugsnag-plugin-aws-lambda.d.ts diff --git a/jest.config.js b/jest.config.js index 527502ae9..57ff343ce 100644 --- a/jest.config.js +++ b/jest.config.js @@ -79,6 +79,7 @@ module.exports = { project('node plugins', [ 'delivery-node', 'in-flight', + 'plugin-aws-lambda', 'plugin-express', 'plugin-koa', 'plugin-restify', diff --git a/packages/plugin-aws-lambda/LICENSE.txt b/packages/plugin-aws-lambda/LICENSE.txt new file mode 100644 index 000000000..ddc0631e2 --- /dev/null +++ b/packages/plugin-aws-lambda/LICENSE.txt @@ -0,0 +1,19 @@ +Copyright (c) Bugsnag, https://www.bugsnag.com/ + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/packages/plugin-aws-lambda/README.md b/packages/plugin-aws-lambda/README.md new file mode 100644 index 000000000..cb4ca2585 --- /dev/null +++ b/packages/plugin-aws-lambda/README.md @@ -0,0 +1,7 @@ +# @bugsnag/plugin-aws-lambda + +A [@bugsnag/js](https://github.com/bugsnag/bugsnag-js) plugin for capturing errors in AWS Lambda functions. + +## License + +This package is free software released under the MIT License. See [LICENSE.txt](./LICENSE.txt) for details. diff --git a/packages/plugin-aws-lambda/package.json b/packages/plugin-aws-lambda/package.json new file mode 100644 index 000000000..e7d6cdbc2 --- /dev/null +++ b/packages/plugin-aws-lambda/package.json @@ -0,0 +1,35 @@ +{ + "name": "@bugsnag/plugin-aws-lambda", + "version": "7.7.0", + "main": "dist/bugsnag-aws-lambda.js", + "types": "types/bugsnag-plugin-aws-lambda.d.ts", + "description": "AWS Lambda support for @bugsnag/node", + "homepage": "https://www.bugsnag.com/", + "repository": { + "type": "git", + "url": "git@github.com:bugsnag/bugsnag-js.git" + }, + "publishConfig": { + "access": "public" + }, + "files": [ + "dist", + "types" + ], + "scripts": { + "clean": "rm -fr dist && mkdir dist", + "build": "npm run clean && ../../bin/bundle src/index.js --node --standalone=BugsnagPluginAwsLambda | ../../bin/extract-source-map dist/bugsnag-aws-lambda.js", + "postversion": "npm run build" + }, + "author": "Bugsnag", + "license": "MIT", + "dependencies": { + "@bugsnag/in-flight": "^7.7.0" + }, + "devDependencies": { + "@bugsnag/core": "^7.7.0" + }, + "peerDependencies": { + "@bugsnag/core": "^7.0.0" + } +} diff --git a/packages/plugin-aws-lambda/src/index.js b/packages/plugin-aws-lambda/src/index.js new file mode 100644 index 000000000..e69de29bb diff --git a/packages/plugin-aws-lambda/test/index.test.ts b/packages/plugin-aws-lambda/test/index.test.ts new file mode 100644 index 000000000..e69de29bb diff --git a/packages/plugin-aws-lambda/types/bugsnag-plugin-aws-lambda.d.ts b/packages/plugin-aws-lambda/types/bugsnag-plugin-aws-lambda.d.ts new file mode 100644 index 000000000..e69de29bb diff --git a/tsconfig.json b/tsconfig.json index eb5dc389d..8c5176a8d 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -72,6 +72,7 @@ "packages/delivery-xml-http-request", "packages/in-flight", "packages/plugin-app-duration", + "packages/plugin-aws-lambda", "packages/plugin-browser-context", "packages/plugin-browser-device", "packages/plugin-contextualize", From 856f042d3ba436078f4a917dfea09dc544b7e7f3 Mon Sep 17 00:00:00 2001 From: Joe Haines Date: Mon, 22 Feb 2021 15:59:30 +0000 Subject: [PATCH 23/65] Implement AWS Lambda function wrapper --- packages/plugin-aws-lambda/src/index.js | 82 +++++ packages/plugin-aws-lambda/test/index.test.ts | 298 ++++++++++++++++++ .../types/bugsnag-plugin-aws-lambda.d.ts | 24 ++ 3 files changed, 404 insertions(+) diff --git a/packages/plugin-aws-lambda/src/index.js b/packages/plugin-aws-lambda/src/index.js index e69de29bb..998212fcc 100644 --- a/packages/plugin-aws-lambda/src/index.js +++ b/packages/plugin-aws-lambda/src/index.js @@ -0,0 +1,82 @@ +const bugsnagInFlight = require('@bugsnag/in-flight') + +const BugsnagPluginAwsLambda = { + name: 'awsLambda', + + load (client) { + bugsnagInFlight.trackInFlight(client) + + return { + createHandler ({ flushTimeoutMs = 2000 } = {}) { + return wrapHandler.bind(null, client, flushTimeoutMs) + } + } + } +} + +function wrapHandler (client, flushTimeoutMs, handler) { + let _handler = handler + + if (handler.length > 2) { + // This is a handler expecting a 'callback' argument, so we convert + // it to return a Promise so '_handler' always has the same API + _handler = promisifyHandler(handler) + } + + return async function (event, context) { + client.addMetadata('AWS Lambda context', context) + + try { + return await _handler(event, context) + } catch (err) { + const handledState = { + severity: 'error', + unhandled: true, + severityReason: { type: 'unhandledException' } + } + + const event = client.Event.create(err, true, handledState, 1) + + client._notify(event) + + throw err + } finally { + try { + await bugsnagInFlight.flush(flushTimeoutMs) + } catch (err) { + client._logger.error(`Delivery may be unsuccessful: ${err.message}`) + } + } + } +} + +// Convert a handler that uses callbacks to an async handler +function promisifyHandler (handler) { + return function (event, context) { + return new Promise(function (resolve, reject) { + const result = handler(event, context, function (err, response) { + if (err) { + reject(err) + return + } + + resolve(response) + }) + + // Handle an edge case where the passed handler has the callback parameter + // but actually returns a promise. In this case we need to resolve/reject + // based on the returned promise instead of in the callback + if (isPromise(result)) { + result.then(resolve).catch(reject) + } + }) + } +} + +function isPromise (value) { + return (typeof value === 'object' || typeof value === 'function') && + typeof value.then === 'function' && + typeof value.catch === 'function' +} + +module.exports = BugsnagPluginAwsLambda diff --git a/packages/plugin-aws-lambda/test/index.test.ts b/packages/plugin-aws-lambda/test/index.test.ts index e69de29bb..0ab9c860d 100644 --- a/packages/plugin-aws-lambda/test/index.test.ts +++ b/packages/plugin-aws-lambda/test/index.test.ts @@ -0,0 +1,298 @@ +import BugsnagPluginAwsLambda from '../src/' +import Client, { EventDeliveryPayload } from '@bugsnag/core/client' + +describe('plugin: aws lambda', () => { + it('has a name', () => { + expect(BugsnagPluginAwsLambda.name).toBe('awsLambda') + + const client = new Client({ apiKey: 'AN_API_KEY', plugins: [BugsnagPluginAwsLambda] }) + const plugin = client.getPlugin('awsLambda') + + expect(plugin).toBeTruthy() + }) + + it('exports a "createHandler" function', () => { + const client = new Client({ apiKey: 'AN_API_KEY', plugins: [BugsnagPluginAwsLambda] }) + const plugin = client.getPlugin('awsLambda') + + expect(plugin).toMatchObject({ createHandler: expect.any(Function) }) + }) + + it('adds the context as metadata', async () => { + const client = new Client({ apiKey: 'AN_API_KEY', plugins: [BugsnagPluginAwsLambda] }) + + const handler = (event: any, context: any) => 'abc' + + const event = { very: 'eventy' } + const context = { extremely: 'contextual' } + + const plugin = client.getPlugin('awsLambda') + + if (!plugin) { + throw new Error('Plugin was not loaded!') + } + + const bugsnagHandler = plugin.createHandler() + const wrappedHandler = bugsnagHandler(handler) + + expect(await wrappedHandler(event, context)).toBe('abc') + + expect(client.getMetadata('AWS Lambda context')).toEqual(context) + }) + + it('logs an error if flush times out', async () => { + const client = new Client({ apiKey: 'AN_API_KEY', plugins: [BugsnagPluginAwsLambda] }) + client._logger.error = jest.fn() + + client._setDelivery(() => ({ + sendEvent (payload, cb) { + setTimeout(cb, 250) + }, + sendSession: () => {} + })) + + const handler = () => { + client.notify('hello') + + return 'abc' + } + + const event = { very: 'eventy' } + const context = { extremely: 'contextual' } + + const timeoutError = new Error('flush timed out after 20ms') + + const plugin = client.getPlugin('awsLambda') + + if (!plugin) { + throw new Error('Plugin was not loaded!') + } + + const bugsnagHandler = plugin.createHandler({ flushTimeoutMs: 20 }) + const wrappedHandler = bugsnagHandler(handler) + + expect(await wrappedHandler(event, context)).toBe('abc') + expect(client._logger.error).toHaveBeenCalledWith(`Delivery may be unsuccessful: ${timeoutError.message}`) + }) + + it('returns a wrapped handler that resolves to the original return value (async)', async () => { + const client = new Client({ apiKey: 'AN_API_KEY', plugins: [BugsnagPluginAwsLambda] }) + + const handler = () => 'abc' + + const event = { very: 'eventy' } + const context = { extremely: 'contextual' } + + const plugin = client.getPlugin('awsLambda') + + if (!plugin) { + throw new Error('Plugin was not loaded!') + } + + const bugsnagHandler = plugin.createHandler() + const wrappedHandler = bugsnagHandler(handler) + + expect(await handler()).toBe('abc') + expect(await wrappedHandler(event, context)).toBe('abc') + }) + + it('notifies when an error is thrown (async)', async () => { + const client = new Client({ apiKey: 'AN_API_KEY', plugins: [BugsnagPluginAwsLambda] }) + const payloads: EventDeliveryPayload[] = [] + + client._setDelivery(() => ({ + sendEvent (payload, cb) { + payloads.push(payload) + cb() + }, + sendSession: () => {} + })) + + const error = new Error('oh no') + const handler = (event: any, context: any) => { throw error } + + const event = { very: 'eventy' } + const context = { extremely: 'contextual' } + + const plugin = client.getPlugin('awsLambda') + + if (!plugin) { + throw new Error('Plugin was not loaded!') + } + + const bugsnagHandler = plugin.createHandler() + const wrappedHandler = bugsnagHandler(handler) + + expect(payloads).toHaveLength(0) + + await expect(() => wrappedHandler(event, context)).rejects.toThrow(error) + + expect(payloads).toHaveLength(1) + expect(payloads[0].events[0].errors[0].errorMessage).toBe(error.message) + }) + + it('returns a wrapped handler that resolves to the value passed to the callback (callback)', async () => { + const client = new Client({ apiKey: 'AN_API_KEY', plugins: [BugsnagPluginAwsLambda] }) + + const handler = (event: any, context: any, callback: any) => { callback(null, 'xyz') } + + const event = { very: 'eventy' } + const context = { extremely: 'contextual' } + + const plugin = client.getPlugin('awsLambda') + + if (!plugin) { + throw new Error('Plugin was not loaded!') + } + + const bugsnagHandler = plugin.createHandler() + const wrappedHandler = bugsnagHandler(handler) + + expect(await wrappedHandler(event, context)).toBe('xyz') + }) + + it('notifies when an error is passed (callback)', async () => { + const client = new Client({ apiKey: 'AN_API_KEY', plugins: [BugsnagPluginAwsLambda] }) + const payloads: EventDeliveryPayload[] = [] + + client._setDelivery(() => ({ + sendEvent (payload, cb) { + payloads.push(payload) + cb() + }, + sendSession: () => {} + })) + + const error = new Error('uh oh') + const handler = (event: any, context: any, callback: any) => { callback(error, 'xyz') } + + const event = { very: 'eventy' } + const context = { extremely: 'contextual' } + + const plugin = client.getPlugin('awsLambda') + + if (!plugin) { + throw new Error('Plugin was not loaded!') + } + + const bugsnagHandler = plugin.createHandler() + const wrappedHandler = bugsnagHandler(handler) + + expect(payloads).toHaveLength(0) + + await expect(() => wrappedHandler(event, context)).rejects.toThrow(error) + + expect(payloads).toHaveLength(1) + expect(payloads[0].events[0].errors[0].errorMessage).toBe(error.message) + }) + + it('works when an async handler has the callback parameter', async () => { + const client = new Client({ apiKey: 'AN_API_KEY', plugins: [BugsnagPluginAwsLambda] }) + + const handler = async (event: any, context: any, callback: any) => 'abcxyz' + + const event = { very: 'eventy' } + const context = { extremely: 'contextual' } + + const plugin = client.getPlugin('awsLambda') + + if (!plugin) { + throw new Error('Plugin was not loaded!') + } + + const bugsnagHandler = plugin.createHandler() + const wrappedHandler = bugsnagHandler(handler) + + expect(await wrappedHandler(event, context)).toBe('abcxyz') + }) + + it('works when an async handler has the callback parameter and calls it', async () => { + const client = new Client({ apiKey: 'AN_API_KEY', plugins: [BugsnagPluginAwsLambda] }) + + const handler = async (event: any, context: any, callback: any) => { callback(null, 'abcxyz') } + + const event = { very: 'eventy' } + const context = { extremely: 'contextual' } + + const plugin = client.getPlugin('awsLambda') + + if (!plugin) { + throw new Error('Plugin was not loaded!') + } + + const bugsnagHandler = plugin.createHandler() + const wrappedHandler = bugsnagHandler(handler) + + expect(await wrappedHandler(event, context)).toBe('abcxyz') + }) + + it('works when an async handler has the callback parameter and throws', async () => { + const client = new Client({ apiKey: 'AN_API_KEY', plugins: [BugsnagPluginAwsLambda] }) + const payloads: EventDeliveryPayload[] = [] + + client._setDelivery(() => ({ + sendEvent (payload, cb) { + payloads.push(payload) + cb() + }, + sendSession: () => {} + })) + + const error = new Error('abcxyz') + const handler = async (event: any, context: any, callback: any) => { throw error } + + const event = { very: 'eventy' } + const context = { extremely: 'contextual' } + + const plugin = client.getPlugin('awsLambda') + + if (!plugin) { + throw new Error('Plugin was not loaded!') + } + + const bugsnagHandler = plugin.createHandler() + const wrappedHandler = bugsnagHandler(handler) + + expect(payloads).toHaveLength(0) + + await expect(() => wrappedHandler(event, context)).rejects.toThrow(error) + + expect(payloads).toHaveLength(1) + expect(payloads[0].events[0].errors[0].errorMessage).toBe(error.message) + }) + + it('works when an async handler has the callback parameter and calls it with an error', async () => { + const client = new Client({ apiKey: 'AN_API_KEY', plugins: [BugsnagPluginAwsLambda] }) + const payloads: EventDeliveryPayload[] = [] + + client._setDelivery(() => ({ + sendEvent (payload, cb) { + payloads.push(payload) + cb() + }, + sendSession: () => {} + })) + + const error = new Error('abcxyz') + const handler = async (event: any, context: any, callback: any) => { callback(error) } + + const event = { very: 'eventy' } + const context = { extremely: 'contextual' } + + const plugin = client.getPlugin('awsLambda') + + if (!plugin) { + throw new Error('Plugin was not loaded!') + } + + const bugsnagHandler = plugin.createHandler() + const wrappedHandler = bugsnagHandler(handler) + + expect(payloads).toHaveLength(0) + + await expect(() => wrappedHandler(event, context)).rejects.toThrow(error) + + expect(payloads).toHaveLength(1) + expect(payloads[0].events[0].errors[0].errorMessage).toBe(error.message) + }) +}) diff --git a/packages/plugin-aws-lambda/types/bugsnag-plugin-aws-lambda.d.ts b/packages/plugin-aws-lambda/types/bugsnag-plugin-aws-lambda.d.ts index e69de29bb..231a06cc6 100644 --- a/packages/plugin-aws-lambda/types/bugsnag-plugin-aws-lambda.d.ts +++ b/packages/plugin-aws-lambda/types/bugsnag-plugin-aws-lambda.d.ts @@ -0,0 +1,24 @@ +import { Plugin, Client } from '@bugsnag/core' + +declare const BugsnagPluginAwsLambda: Plugin +export default BugsnagPluginAwsLambda + +type AsyncHandler = (event: any, context: any) => Promise +type CallbackHandler = (event: any, context: any, callback: (err: Error|null, response: any) => void) => void + +export type BugsnagPluginAwsLambdaHandler = (handler: AsyncHandler|CallbackHandler) => AsyncHandler + +export interface BugsnagPluginAwsLambdaConfiguration { + flushTimeoutMs?: number +} + +export interface BugsnagPluginAwsLambdaResult { + createHandler(configuration?: BugsnagPluginAwsLambdaConfiguration): BugsnagPluginAwsLambdaHandler +} + +// add a new call signature for the getPlugin() method that types the plugin result +declare module '@bugsnag/core' { + interface Client { + getPlugin(id: 'awsLambda'): BugsnagPluginAwsLambdaResult | undefined + } +} From b9532a8cbf166d48cb3c5d98df2d3acbb3b77ecd Mon Sep 17 00:00:00 2001 From: Joe Haines Date: Tue, 23 Feb 2021 15:17:19 +0000 Subject: [PATCH 24/65] Add basic Maze Runner tests --- .../simple-app/async/handled-exception.js | 11 +++- .../fixtures/simple-app/async/package.json | 2 + .../simple-app/async/unhandled-exception.js | 11 +++- .../fixtures/simple-app/template.yaml | 2 + test/aws-lambda/features/handled.feature | 13 ++++ .../features/scripts/build-fixtures | 11 ++++ .../features/steps/aws-lambda-steps.rb | 7 +++ test/aws-lambda/features/support/env.rb | 60 +++++++++++++++++++ test/aws-lambda/features/unhandled.feature | 15 ++++- 9 files changed, 127 insertions(+), 5 deletions(-) create mode 100644 test/aws-lambda/features/steps/aws-lambda-steps.rb diff --git a/test/aws-lambda/features/fixtures/simple-app/async/handled-exception.js b/test/aws-lambda/features/fixtures/simple-app/async/handled-exception.js index 7f85c8814..ce7a30115 100644 --- a/test/aws-lambda/features/fixtures/simple-app/async/handled-exception.js +++ b/test/aws-lambda/features/fixtures/simple-app/async/handled-exception.js @@ -1,10 +1,17 @@ const Bugsnag = require('@bugsnag/js') +const BugsnagPluginAwsLambda = require('@bugsnag/plugin-aws-lambda') Bugsnag.start({ apiKey: process.env.BUGSNAG_API_KEY, - plugins: [] + endpoints: { + notify: process.env.BUGSNAG_NOTIFY_ENDPOINT, + sessions: process.env.BUGSNAG_SESSIONS_ENDPOINT + }, + plugins: [BugsnagPluginAwsLambda] }) +const bugsnagHandler = Bugsnag.getPlugin('awsLambda').createHandler() + const handler = async (event, context) => { Bugsnag.notify(new Error('Hello!')) @@ -14,4 +21,4 @@ const handler = async (event, context) => { } } -module.exports.lambdaHandler = handler +module.exports.lambdaHandler = bugsnagHandler(handler) diff --git a/test/aws-lambda/features/fixtures/simple-app/async/package.json b/test/aws-lambda/features/fixtures/simple-app/async/package.json index bc11cb60a..a5c32db16 100644 --- a/test/aws-lambda/features/fixtures/simple-app/async/package.json +++ b/test/aws-lambda/features/fixtures/simple-app/async/package.json @@ -5,9 +5,11 @@ "dependencies": { "@bugsnag/core": "bugsnag-core.tgz", "@bugsnag/delivery-node": "bugsnag-delivery-node.tgz", + "@bugsnag/in-flight": "bugsnag-in-flight.tgz", "@bugsnag/js": "bugsnag-js.tgz", "@bugsnag/node": "bugsnag-node.tgz", "@bugsnag/plugin-app-duration": "bugsnag-plugin-app-duration.tgz", + "@bugsnag/plugin-aws-lambda": "bugsnag-plugin-aws-lambda.tgz", "@bugsnag/plugin-contextualize": "bugsnag-plugin-contextualize.tgz", "@bugsnag/plugin-intercept": "bugsnag-plugin-intercept.tgz", "@bugsnag/plugin-node-device": "bugsnag-plugin-node-device.tgz", diff --git a/test/aws-lambda/features/fixtures/simple-app/async/unhandled-exception.js b/test/aws-lambda/features/fixtures/simple-app/async/unhandled-exception.js index 965a655ab..37d4f6791 100644 --- a/test/aws-lambda/features/fixtures/simple-app/async/unhandled-exception.js +++ b/test/aws-lambda/features/fixtures/simple-app/async/unhandled-exception.js @@ -1,12 +1,19 @@ const Bugsnag = require('@bugsnag/js') +const BugsnagPluginAwsLambda = require('@bugsnag/plugin-aws-lambda') Bugsnag.start({ apiKey: process.env.BUGSNAG_API_KEY, - plugins: [] + endpoints: { + notify: process.env.BUGSNAG_NOTIFY_ENDPOINT, + sessions: process.env.BUGSNAG_SESSIONS_ENDPOINT + }, + plugins: [BugsnagPluginAwsLambda] }) +const bugsnagHandler = Bugsnag.getPlugin('awsLambda').createHandler() + const handler = async (event, context) => { throw new Error('Oh no!') } -module.exports.lambdaHandler = handler +module.exports.lambdaHandler = bugsnagHandler(handler) diff --git a/test/aws-lambda/features/fixtures/simple-app/template.yaml b/test/aws-lambda/features/fixtures/simple-app/template.yaml index 64820ab8a..34e33dc6a 100644 --- a/test/aws-lambda/features/fixtures/simple-app/template.yaml +++ b/test/aws-lambda/features/fixtures/simple-app/template.yaml @@ -8,6 +8,8 @@ Globals: Environment: Variables: BUGSNAG_API_KEY: + BUGSNAG_NOTIFY_ENDPOINT: + BUGSNAG_SESSIONS_ENDPOINT: Resources: AsyncUnhandledExceptionFunction: diff --git a/test/aws-lambda/features/handled.feature b/test/aws-lambda/features/handled.feature index 0a2f76b14..48136e21f 100644 --- a/test/aws-lambda/features/handled.feature +++ b/test/aws-lambda/features/handled.feature @@ -2,7 +2,20 @@ Feature: Handled exceptions are reported correctly in lambda functions Scenario: handled exception in an async lambda Given I store the api key in the environment variable "BUGSNAG_API_KEY" + And I store the notify endpoint in the environment variable "BUGSNAG_NOTIFY_ENDPOINT" + And I store the sessions endpoint in the environment variable "BUGSNAG_SESSIONS_ENDPOINT" When I invoke the "AsyncHandledExceptionFunction" lambda in "features/fixtures/simple-app" with the "events/async/handled-exception.json" event Then the lambda response "body.message" equals "Did not crash!" And the lambda response "statusCode" equals 200 And the SAM exit code equals 0 + When I wait to receive an error + Then the error is valid for the error reporting API version "4" for the "Bugsnag Node" notifier + And the event "unhandled" is false + And the event "severity" equals "warning" + And the event "severityReason.type" equals "handledException" + And the exception "errorClass" equals "Error" + And the exception "message" equals "Hello!" + And the exception "type" equals "nodejs" + And the "file" of stack frame 0 equals "handled-exception.js" + And the event "metaData.AWS Lambda context.functionName" equals "AsyncHandledExceptionFunction" + And the event "metaData.AWS Lambda context.awsRequestId" is not null diff --git a/test/aws-lambda/features/scripts/build-fixtures b/test/aws-lambda/features/scripts/build-fixtures index 9f431ae75..349a5edb8 100755 --- a/test/aws-lambda/features/scripts/build-fixtures +++ b/test/aws-lambda/features/scripts/build-fixtures @@ -17,9 +17,11 @@ ROOT = File.realpath("#{__dir__}/../../../../") BUGSNAG_PACKAGES = [ "core", "delivery-node", + "in-flight", "js", "node", "plugin-app-duration", + "plugin-aws-lambda", "plugin-contextualize", "plugin-intercept", "plugin-node-device", @@ -39,6 +41,14 @@ def heading(message) HEADING end +# Bootstrap and build Bugsnag packages so they're ready to install +def bootstrap + Dir.chdir(ROOT) do + system("npm run bootstrap") + system("npm run build") + end +end + # Run "npm pack" on the required packages and strip the version suffix so they # can be depended on in a package.json file def pack @@ -126,6 +136,7 @@ def tidy_up end begin + bootstrap pack install_and_build ensure diff --git a/test/aws-lambda/features/steps/aws-lambda-steps.rb b/test/aws-lambda/features/steps/aws-lambda-steps.rb new file mode 100644 index 000000000..0fe2f4bad --- /dev/null +++ b/test/aws-lambda/features/steps/aws-lambda-steps.rb @@ -0,0 +1,7 @@ +Given('I store the notify endpoint in the environment variable {string}') do |name| + step("I set environment variable '#{name}' to 'http://host.docker.internal:9339/notify'") +end + +Given('I store the sessions endpoint in the environment variable {string}') do |name| + step("I set environment variable '#{name}' to 'http://host.docker.internal:9339/sessions'") +end diff --git a/test/aws-lambda/features/support/env.rb b/test/aws-lambda/features/support/env.rb index 6029b4e5f..52546aa86 100644 --- a/test/aws-lambda/features/support/env.rb +++ b/test/aws-lambda/features/support/env.rb @@ -1,3 +1,61 @@ +require 'maze/server' + +# FIXME: this is a temporary workaround that binds the Maze::Server to 0.0.0.0 +# so that it is reachable from the docker containers started by AWS SAM +# This can be removed when PLAT-6116 is done +module Maze + class Server + class << self + def start + attempts = 0 + $logger.info "Maze Runner v#{Maze::VERSION}" + $logger.info 'Starting mock server' + loop do + @thread = Thread.new do + server = WEBrick::HTTPServer.new( + BindAddress: '0.0.0.0', + Port: PORT, + Logger: $logger, + AccessLog: [] + ) + + # Mount a block to respond to all requests with status:200 + server.mount_proc '/' do |_request, response| + $logger.info 'Received request on server root, responding with 200' + response.header['Access-Control-Allow-Origin'] = '*' + response.body = 'Maze runner received request' + response.status = 200 + end + + # When adding more endpoints, be sure to update the 'I should receive no requests' step + server.mount '/notify', Servlet, errors + server.mount '/sessions', Servlet, sessions + server.mount '/builds', Servlet, builds + # server.mount '/logs', LogServlet + server.start + rescue StandardError => e + $logger.warn "Failed to start mock server: #{e.message}" + ensure + server&.shutdown + end + + # Need a short sleep here as a dying thread is still alive momentarily + sleep 1 + break if running? + + # Bail out after 3 attempts + attempts += 1 + raise 'Too many failed attempts to start mock server' if attempts == 3 + + # Failed to start - sleep before retrying + $logger.info 'Retrying in 5 seconds' + sleep 5 + end + end + end + end +end + `command -v sam` if $? != 0 @@ -20,3 +78,5 @@ puts "Unable to build fixtures!" exit 1 end + +Maze.config.enforce_bugsnag_integrity = false diff --git a/test/aws-lambda/features/unhandled.feature b/test/aws-lambda/features/unhandled.feature index 9da31490f..a9f3ed63d 100644 --- a/test/aws-lambda/features/unhandled.feature +++ b/test/aws-lambda/features/unhandled.feature @@ -2,11 +2,24 @@ Feature: Unhandled exceptions are reported correctly in lambda functions Scenario: unhandled exception in an async lambda Given I store the api key in the environment variable "BUGSNAG_API_KEY" + And I store the notify endpoint in the environment variable "BUGSNAG_NOTIFY_ENDPOINT" + And I store the sessions endpoint in the environment variable "BUGSNAG_SESSIONS_ENDPOINT" When I invoke the "AsyncUnhandledExceptionFunction" lambda in "features/fixtures/simple-app" with the "events/async/unhandled-exception.json" event Then the lambda response "errorMessage" equals "Oh no!" And the lambda response "errorType" equals "Error" - And the lambda response "trace" is an array with 3 elements + And the lambda response "trace" is an array with 4 elements And the lambda response "trace.0" equals "Error: Oh no!" And the lambda response "body" is null And the lambda response "statusCode" is null And the SAM exit code equals 0 + When I wait to receive an error + Then the error is valid for the error reporting API version "4" for the "Bugsnag Node" notifier + And the event "unhandled" is true + And the event "severity" equals "error" + And the event "severityReason.type" equals "unhandledException" + And the exception "errorClass" equals "Error" + And the exception "message" equals "Oh no!" + And the exception "type" equals "nodejs" + And the "file" of stack frame 0 equals "unhandled-exception.js" + And the event "metaData.AWS Lambda context.functionName" equals "AsyncUnhandledExceptionFunction" + And the event "metaData.AWS Lambda context.awsRequestId" is not null From 96494c9cb8c8750dc2b53ebff27c36674982df3c Mon Sep 17 00:00:00 2001 From: Joe Haines Date: Fri, 26 Feb 2021 15:37:51 +0000 Subject: [PATCH 25/65] Honour automatic notify config options --- packages/plugin-aws-lambda/src/index.js | 16 ++- packages/plugin-aws-lambda/test/index.test.ts | 136 ++++++++++++++++++ 2 files changed, 145 insertions(+), 7 deletions(-) diff --git a/packages/plugin-aws-lambda/src/index.js b/packages/plugin-aws-lambda/src/index.js index 998212fcc..5e3a70681 100644 --- a/packages/plugin-aws-lambda/src/index.js +++ b/packages/plugin-aws-lambda/src/index.js @@ -29,15 +29,17 @@ function wrapHandler (client, flushTimeoutMs, handler) { try { return await _handler(event, context) } catch (err) { - const handledState = { - severity: 'error', - unhandled: true, - severityReason: { type: 'unhandledException' } - } + if (client._config.autoDetectErrors && client._config.enabledErrorTypes.unhandledExceptions) { + const handledState = { + severity: 'error', + unhandled: true, + severityReason: { type: 'unhandledException' } + } - const event = client.Event.create(err, true, handledState, 1) + const event = client.Event.create(err, true, handledState, 1) - client._notify(event) + client._notify(event) + } throw err } finally { diff --git a/packages/plugin-aws-lambda/test/index.test.ts b/packages/plugin-aws-lambda/test/index.test.ts index 0ab9c860d..63a718232 100644 --- a/packages/plugin-aws-lambda/test/index.test.ts +++ b/packages/plugin-aws-lambda/test/index.test.ts @@ -131,6 +131,74 @@ describe('plugin: aws lambda', () => { expect(payloads[0].events[0].errors[0].errorMessage).toBe(error.message) }) + it('does not notify when "autoDetectErrors" is false (async)', async () => { + const client = new Client({ apiKey: 'AN_API_KEY', plugins: [BugsnagPluginAwsLambda], autoDetectErrors: false }) + const payloads: EventDeliveryPayload[] = [] + + client._setDelivery(() => ({ + sendEvent (payload, cb) { + payloads.push(payload) + cb() + }, + sendSession: () => {} + })) + + const error = new Error('oh no') + const handler = (event: any, context: any) => { throw error } + + const event = { very: 'eventy' } + const context = { extremely: 'contextual' } + + const plugin = client.getPlugin('awsLambda') + + if (!plugin) { + throw new Error('Plugin was not loaded!') + } + + const bugsnagHandler = plugin.createHandler() + const wrappedHandler = bugsnagHandler(handler) + + expect(payloads).toHaveLength(0) + + await expect(() => wrappedHandler(event, context)).rejects.toThrow(error) + + expect(payloads).toHaveLength(0) + }) + + it('does not notify when "unhandledExceptions" are disabled (async)', async () => { + const client = new Client({ apiKey: 'AN_API_KEY', plugins: [BugsnagPluginAwsLambda], enabledErrorTypes: { unhandledExceptions: false } }) + const payloads: EventDeliveryPayload[] = [] + + client._setDelivery(() => ({ + sendEvent (payload, cb) { + payloads.push(payload) + cb() + }, + sendSession: () => {} + })) + + const error = new Error('oh no') + const handler = (event: any, context: any) => { throw error } + + const event = { very: 'eventy' } + const context = { extremely: 'contextual' } + + const plugin = client.getPlugin('awsLambda') + + if (!plugin) { + throw new Error('Plugin was not loaded!') + } + + const bugsnagHandler = plugin.createHandler() + const wrappedHandler = bugsnagHandler(handler) + + expect(payloads).toHaveLength(0) + + await expect(() => wrappedHandler(event, context)).rejects.toThrow(error) + + expect(payloads).toHaveLength(0) + }) + it('returns a wrapped handler that resolves to the value passed to the callback (callback)', async () => { const client = new Client({ apiKey: 'AN_API_KEY', plugins: [BugsnagPluginAwsLambda] }) @@ -186,6 +254,74 @@ describe('plugin: aws lambda', () => { expect(payloads[0].events[0].errors[0].errorMessage).toBe(error.message) }) + it('does not notify when "autoDetectErrors" is false (callback)', async () => { + const client = new Client({ apiKey: 'AN_API_KEY', plugins: [BugsnagPluginAwsLambda], autoDetectErrors: false }) + const payloads: EventDeliveryPayload[] = [] + + client._setDelivery(() => ({ + sendEvent (payload, cb) { + payloads.push(payload) + cb() + }, + sendSession: () => {} + })) + + const error = new Error('uh oh') + const handler = (event: any, context: any, callback: any) => { callback(error, 'xyz') } + + const event = { very: 'eventy' } + const context = { extremely: 'contextual' } + + const plugin = client.getPlugin('awsLambda') + + if (!plugin) { + throw new Error('Plugin was not loaded!') + } + + const bugsnagHandler = plugin.createHandler() + const wrappedHandler = bugsnagHandler(handler) + + expect(payloads).toHaveLength(0) + + await expect(() => wrappedHandler(event, context)).rejects.toThrow(error) + + expect(payloads).toHaveLength(0) + }) + + it('does not notify when "unhandledExceptions" are disabled (callback)', async () => { + const client = new Client({ apiKey: 'AN_API_KEY', plugins: [BugsnagPluginAwsLambda], enabledErrorTypes: { unhandledExceptions: false } }) + const payloads: EventDeliveryPayload[] = [] + + client._setDelivery(() => ({ + sendEvent (payload, cb) { + payloads.push(payload) + cb() + }, + sendSession: () => {} + })) + + const error = new Error('uh oh') + const handler = (event: any, context: any, callback: any) => { callback(error, 'xyz') } + + const event = { very: 'eventy' } + const context = { extremely: 'contextual' } + + const plugin = client.getPlugin('awsLambda') + + if (!plugin) { + throw new Error('Plugin was not loaded!') + } + + const bugsnagHandler = plugin.createHandler() + const wrappedHandler = bugsnagHandler(handler) + + expect(payloads).toHaveLength(0) + + await expect(() => wrappedHandler(event, context)).rejects.toThrow(error) + + expect(payloads).toHaveLength(0) + }) + it('works when an async handler has the callback parameter', async () => { const client = new Client({ apiKey: 'AN_API_KEY', plugins: [BugsnagPluginAwsLambda] }) From 40ffebbebf71d3908f3f7fe045788d84fded74a3 Mon Sep 17 00:00:00 2001 From: Joe Haines Date: Thu, 25 Feb 2021 14:29:07 +0000 Subject: [PATCH 26/65] Start a session when a lambda is invoked --- packages/plugin-aws-lambda/package.json | 3 +- packages/plugin-aws-lambda/src/index.js | 6 + packages/plugin-aws-lambda/test/index.test.ts | 287 +++++++++++------- 3 files changed, 193 insertions(+), 103 deletions(-) diff --git a/packages/plugin-aws-lambda/package.json b/packages/plugin-aws-lambda/package.json index e7d6cdbc2..e289aafe5 100644 --- a/packages/plugin-aws-lambda/package.json +++ b/packages/plugin-aws-lambda/package.json @@ -24,7 +24,8 @@ "author": "Bugsnag", "license": "MIT", "dependencies": { - "@bugsnag/in-flight": "^7.7.0" + "@bugsnag/in-flight": "^7.7.0", + "@bugsnag/plugin-browser-session": "^7.7.0" }, "devDependencies": { "@bugsnag/core": "^7.7.0" diff --git a/packages/plugin-aws-lambda/src/index.js b/packages/plugin-aws-lambda/src/index.js index 5e3a70681..43f9f1529 100644 --- a/packages/plugin-aws-lambda/src/index.js +++ b/packages/plugin-aws-lambda/src/index.js @@ -1,10 +1,12 @@ const bugsnagInFlight = require('@bugsnag/in-flight') +const BugsnagPluginBrowserSession = require('@bugsnag/plugin-browser-session') const BugsnagPluginAwsLambda = { name: 'awsLambda', load (client) { bugsnagInFlight.trackInFlight(client) + client._loadPlugin(BugsnagPluginBrowserSession) return { createHandler ({ flushTimeoutMs = 2000 } = {}) { @@ -26,6 +28,10 @@ function wrapHandler (client, flushTimeoutMs, handler) { return async function (event, context) { client.addMetadata('AWS Lambda context', context) + if (client._config.autoTrackSessions) { + client.startSession() + } + try { return await _handler(event, context) } catch (err) { diff --git a/packages/plugin-aws-lambda/test/index.test.ts b/packages/plugin-aws-lambda/test/index.test.ts index 63a718232..301c66061 100644 --- a/packages/plugin-aws-lambda/test/index.test.ts +++ b/packages/plugin-aws-lambda/test/index.test.ts @@ -1,5 +1,28 @@ +import util from 'util' import BugsnagPluginAwsLambda from '../src/' -import Client, { EventDeliveryPayload } from '@bugsnag/core/client' +import Client, { EventDeliveryPayload, SessionDeliveryPayload } from '@bugsnag/core/client' + +const createClient = (events: EventDeliveryPayload[], sessions: SessionDeliveryPayload[], config = {}) => { + const client = new Client({ apiKey: 'AN_API_KEY', plugins: [BugsnagPluginAwsLambda], ...config }) + + // a flush failure won't throw as we don't want to crash apps if delivery takes + // too long. To avoid the unit tests passing when this happens, we make the logger + // throw on any 'error' log call + client._logger.error = (...args) => { throw new Error(util.format(args)) } + + client._delivery = { + sendEvent (payload, cb = () => {}) { + events.push(payload) + cb() + }, + sendSession (payload, cb = () => {}) { + sessions.push(payload) + cb() + } + } + + return client +} describe('plugin: aws lambda', () => { it('has a name', () => { @@ -19,7 +42,10 @@ describe('plugin: aws lambda', () => { }) it('adds the context as metadata', async () => { - const client = new Client({ apiKey: 'AN_API_KEY', plugins: [BugsnagPluginAwsLambda] }) + const events: EventDeliveryPayload[] = [] + const sessions: SessionDeliveryPayload[] = [] + + const client = createClient(events, sessions) const handler = (event: any, context: any) => 'abc' @@ -44,12 +70,14 @@ describe('plugin: aws lambda', () => { const client = new Client({ apiKey: 'AN_API_KEY', plugins: [BugsnagPluginAwsLambda] }) client._logger.error = jest.fn() - client._setDelivery(() => ({ - sendEvent (payload, cb) { + client._delivery = { + sendEvent (payload, cb = () => {}) { setTimeout(cb, 250) }, - sendSession: () => {} - })) + sendSession (payload, cb = () => {}) { + setTimeout(cb, 250) + } + } const handler = () => { client.notify('hello') @@ -76,7 +104,10 @@ describe('plugin: aws lambda', () => { }) it('returns a wrapped handler that resolves to the original return value (async)', async () => { - const client = new Client({ apiKey: 'AN_API_KEY', plugins: [BugsnagPluginAwsLambda] }) + const events: EventDeliveryPayload[] = [] + const sessions: SessionDeliveryPayload[] = [] + + const client = createClient(events, sessions) const handler = () => 'abc' @@ -94,19 +125,16 @@ describe('plugin: aws lambda', () => { expect(await handler()).toBe('abc') expect(await wrappedHandler(event, context)).toBe('abc') + + expect(events).toHaveLength(0) + expect(sessions).toHaveLength(1) }) it('notifies when an error is thrown (async)', async () => { - const client = new Client({ apiKey: 'AN_API_KEY', plugins: [BugsnagPluginAwsLambda] }) - const payloads: EventDeliveryPayload[] = [] + const events: EventDeliveryPayload[] = [] + const sessions: SessionDeliveryPayload[] = [] - client._setDelivery(() => ({ - sendEvent (payload, cb) { - payloads.push(payload) - cb() - }, - sendSession: () => {} - })) + const client = createClient(events, sessions) const error = new Error('oh no') const handler = (event: any, context: any) => { throw error } @@ -123,25 +151,22 @@ describe('plugin: aws lambda', () => { const bugsnagHandler = plugin.createHandler() const wrappedHandler = bugsnagHandler(handler) - expect(payloads).toHaveLength(0) + expect(events).toHaveLength(0) + expect(sessions).toHaveLength(0) await expect(() => wrappedHandler(event, context)).rejects.toThrow(error) - expect(payloads).toHaveLength(1) - expect(payloads[0].events[0].errors[0].errorMessage).toBe(error.message) + expect(events).toHaveLength(1) + expect(events[0].events[0].errors[0].errorMessage).toBe(error.message) + + expect(sessions).toHaveLength(1) }) it('does not notify when "autoDetectErrors" is false (async)', async () => { - const client = new Client({ apiKey: 'AN_API_KEY', plugins: [BugsnagPluginAwsLambda], autoDetectErrors: false }) - const payloads: EventDeliveryPayload[] = [] + const events: EventDeliveryPayload[] = [] + const sessions: SessionDeliveryPayload[] = [] - client._setDelivery(() => ({ - sendEvent (payload, cb) { - payloads.push(payload) - cb() - }, - sendSession: () => {} - })) + const client = createClient(events, sessions, { autoDetectErrors: false }) const error = new Error('oh no') const handler = (event: any, context: any) => { throw error } @@ -158,24 +183,20 @@ describe('plugin: aws lambda', () => { const bugsnagHandler = plugin.createHandler() const wrappedHandler = bugsnagHandler(handler) - expect(payloads).toHaveLength(0) + expect(events).toHaveLength(0) + expect(sessions).toHaveLength(0) await expect(() => wrappedHandler(event, context)).rejects.toThrow(error) - expect(payloads).toHaveLength(0) + expect(events).toHaveLength(0) + expect(sessions).toHaveLength(1) }) it('does not notify when "unhandledExceptions" are disabled (async)', async () => { - const client = new Client({ apiKey: 'AN_API_KEY', plugins: [BugsnagPluginAwsLambda], enabledErrorTypes: { unhandledExceptions: false } }) - const payloads: EventDeliveryPayload[] = [] + const events: EventDeliveryPayload[] = [] + const sessions: SessionDeliveryPayload[] = [] - client._setDelivery(() => ({ - sendEvent (payload, cb) { - payloads.push(payload) - cb() - }, - sendSession: () => {} - })) + const client = createClient(events, sessions, { enabledErrorTypes: { unhandledExceptions: false } }) const error = new Error('oh no') const handler = (event: any, context: any) => { throw error } @@ -192,15 +213,20 @@ describe('plugin: aws lambda', () => { const bugsnagHandler = plugin.createHandler() const wrappedHandler = bugsnagHandler(handler) - expect(payloads).toHaveLength(0) + expect(events).toHaveLength(0) + expect(sessions).toHaveLength(0) await expect(() => wrappedHandler(event, context)).rejects.toThrow(error) - expect(payloads).toHaveLength(0) + expect(events).toHaveLength(0) + expect(sessions).toHaveLength(1) }) it('returns a wrapped handler that resolves to the value passed to the callback (callback)', async () => { - const client = new Client({ apiKey: 'AN_API_KEY', plugins: [BugsnagPluginAwsLambda] }) + const events: EventDeliveryPayload[] = [] + const sessions: SessionDeliveryPayload[] = [] + + const client = createClient(events, sessions) const handler = (event: any, context: any, callback: any) => { callback(null, 'xyz') } @@ -216,20 +242,20 @@ describe('plugin: aws lambda', () => { const bugsnagHandler = plugin.createHandler() const wrappedHandler = bugsnagHandler(handler) + expect(events).toHaveLength(0) + expect(sessions).toHaveLength(0) + expect(await wrappedHandler(event, context)).toBe('xyz') + + expect(events).toHaveLength(0) + expect(sessions).toHaveLength(1) }) it('notifies when an error is passed (callback)', async () => { - const client = new Client({ apiKey: 'AN_API_KEY', plugins: [BugsnagPluginAwsLambda] }) - const payloads: EventDeliveryPayload[] = [] + const events: EventDeliveryPayload[] = [] + const sessions: SessionDeliveryPayload[] = [] - client._setDelivery(() => ({ - sendEvent (payload, cb) { - payloads.push(payload) - cb() - }, - sendSession: () => {} - })) + const client = createClient(events, sessions) const error = new Error('uh oh') const handler = (event: any, context: any, callback: any) => { callback(error, 'xyz') } @@ -246,25 +272,22 @@ describe('plugin: aws lambda', () => { const bugsnagHandler = plugin.createHandler() const wrappedHandler = bugsnagHandler(handler) - expect(payloads).toHaveLength(0) + expect(events).toHaveLength(0) + expect(sessions).toHaveLength(0) await expect(() => wrappedHandler(event, context)).rejects.toThrow(error) - expect(payloads).toHaveLength(1) - expect(payloads[0].events[0].errors[0].errorMessage).toBe(error.message) + expect(events).toHaveLength(1) + expect(events[0].events[0].errors[0].errorMessage).toBe(error.message) + + expect(sessions).toHaveLength(1) }) it('does not notify when "autoDetectErrors" is false (callback)', async () => { - const client = new Client({ apiKey: 'AN_API_KEY', plugins: [BugsnagPluginAwsLambda], autoDetectErrors: false }) - const payloads: EventDeliveryPayload[] = [] + const events: EventDeliveryPayload[] = [] + const sessions: SessionDeliveryPayload[] = [] - client._setDelivery(() => ({ - sendEvent (payload, cb) { - payloads.push(payload) - cb() - }, - sendSession: () => {} - })) + const client = createClient(events, sessions, { autoDetectErrors: false }) const error = new Error('uh oh') const handler = (event: any, context: any, callback: any) => { callback(error, 'xyz') } @@ -281,24 +304,20 @@ describe('plugin: aws lambda', () => { const bugsnagHandler = plugin.createHandler() const wrappedHandler = bugsnagHandler(handler) - expect(payloads).toHaveLength(0) + expect(events).toHaveLength(0) + expect(sessions).toHaveLength(0) await expect(() => wrappedHandler(event, context)).rejects.toThrow(error) - expect(payloads).toHaveLength(0) + expect(events).toHaveLength(0) + expect(sessions).toHaveLength(1) }) it('does not notify when "unhandledExceptions" are disabled (callback)', async () => { - const client = new Client({ apiKey: 'AN_API_KEY', plugins: [BugsnagPluginAwsLambda], enabledErrorTypes: { unhandledExceptions: false } }) - const payloads: EventDeliveryPayload[] = [] + const events: EventDeliveryPayload[] = [] + const sessions: SessionDeliveryPayload[] = [] - client._setDelivery(() => ({ - sendEvent (payload, cb) { - payloads.push(payload) - cb() - }, - sendSession: () => {} - })) + const client = createClient(events, sessions, { enabledErrorTypes: { unhandledExceptions: false } }) const error = new Error('uh oh') const handler = (event: any, context: any, callback: any) => { callback(error, 'xyz') } @@ -315,15 +334,20 @@ describe('plugin: aws lambda', () => { const bugsnagHandler = plugin.createHandler() const wrappedHandler = bugsnagHandler(handler) - expect(payloads).toHaveLength(0) + expect(events).toHaveLength(0) + expect(sessions).toHaveLength(0) await expect(() => wrappedHandler(event, context)).rejects.toThrow(error) - expect(payloads).toHaveLength(0) + expect(events).toHaveLength(0) + expect(sessions).toHaveLength(1) }) it('works when an async handler has the callback parameter', async () => { - const client = new Client({ apiKey: 'AN_API_KEY', plugins: [BugsnagPluginAwsLambda] }) + const events: EventDeliveryPayload[] = [] + const sessions: SessionDeliveryPayload[] = [] + + const client = createClient(events, sessions) const handler = async (event: any, context: any, callback: any) => 'abcxyz' @@ -339,11 +363,20 @@ describe('plugin: aws lambda', () => { const bugsnagHandler = plugin.createHandler() const wrappedHandler = bugsnagHandler(handler) + expect(events).toHaveLength(0) + expect(sessions).toHaveLength(0) + expect(await wrappedHandler(event, context)).toBe('abcxyz') + + expect(events).toHaveLength(0) + expect(sessions).toHaveLength(1) }) it('works when an async handler has the callback parameter and calls it', async () => { - const client = new Client({ apiKey: 'AN_API_KEY', plugins: [BugsnagPluginAwsLambda] }) + const events: EventDeliveryPayload[] = [] + const sessions: SessionDeliveryPayload[] = [] + + const client = createClient(events, sessions) const handler = async (event: any, context: any, callback: any) => { callback(null, 'abcxyz') } @@ -359,20 +392,20 @@ describe('plugin: aws lambda', () => { const bugsnagHandler = plugin.createHandler() const wrappedHandler = bugsnagHandler(handler) + expect(events).toHaveLength(0) + expect(sessions).toHaveLength(0) + expect(await wrappedHandler(event, context)).toBe('abcxyz') + + expect(events).toHaveLength(0) + expect(sessions).toHaveLength(1) }) it('works when an async handler has the callback parameter and throws', async () => { - const client = new Client({ apiKey: 'AN_API_KEY', plugins: [BugsnagPluginAwsLambda] }) - const payloads: EventDeliveryPayload[] = [] + const events: EventDeliveryPayload[] = [] + const sessions: SessionDeliveryPayload[] = [] - client._setDelivery(() => ({ - sendEvent (payload, cb) { - payloads.push(payload) - cb() - }, - sendSession: () => {} - })) + const client = createClient(events, sessions) const error = new Error('abcxyz') const handler = async (event: any, context: any, callback: any) => { throw error } @@ -389,25 +422,22 @@ describe('plugin: aws lambda', () => { const bugsnagHandler = plugin.createHandler() const wrappedHandler = bugsnagHandler(handler) - expect(payloads).toHaveLength(0) + expect(events).toHaveLength(0) + expect(sessions).toHaveLength(0) await expect(() => wrappedHandler(event, context)).rejects.toThrow(error) - expect(payloads).toHaveLength(1) - expect(payloads[0].events[0].errors[0].errorMessage).toBe(error.message) + expect(events).toHaveLength(1) + expect(events[0].events[0].errors[0].errorMessage).toBe(error.message) + + expect(sessions).toHaveLength(1) }) it('works when an async handler has the callback parameter and calls it with an error', async () => { - const client = new Client({ apiKey: 'AN_API_KEY', plugins: [BugsnagPluginAwsLambda] }) - const payloads: EventDeliveryPayload[] = [] + const events: EventDeliveryPayload[] = [] + const sessions: SessionDeliveryPayload[] = [] - client._setDelivery(() => ({ - sendEvent (payload, cb) { - payloads.push(payload) - cb() - }, - sendSession: () => {} - })) + const client = createClient(events, sessions) const error = new Error('abcxyz') const handler = async (event: any, context: any, callback: any) => { callback(error) } @@ -424,11 +454,64 @@ describe('plugin: aws lambda', () => { const bugsnagHandler = plugin.createHandler() const wrappedHandler = bugsnagHandler(handler) - expect(payloads).toHaveLength(0) + expect(events).toHaveLength(0) + expect(sessions).toHaveLength(0) await expect(() => wrappedHandler(event, context)).rejects.toThrow(error) - expect(payloads).toHaveLength(1) - expect(payloads[0].events[0].errors[0].errorMessage).toBe(error.message) + expect(events).toHaveLength(1) + expect(events[0].events[0].errors[0].errorMessage).toBe(error.message) + + expect(sessions).toHaveLength(1) + }) + + it('will track sessions when "autoTrackSessions" is enabled', async () => { + const events: EventDeliveryPayload[] = [] + const sessions: SessionDeliveryPayload[] = [] + const client = createClient(events, sessions, { autoTrackSessions: true }) + + const handler = () => 'abc' + + const event = { very: 'eventy' } + const context = { extremely: 'contextual' } + + const plugin = client.getPlugin('awsLambda') + + if (!plugin) { + throw new Error('Plugin was not loaded!') + } + + const bugsnagHandler = plugin.createHandler() + const wrappedHandler = bugsnagHandler(handler) + + expect(await wrappedHandler(event, context)).toBe('abc') + + expect(events).toHaveLength(0) + expect(sessions).toHaveLength(1) + }) + + it('will not track sessions when "autoTrackSessions" is disabled', async () => { + const events: EventDeliveryPayload[] = [] + const sessions: SessionDeliveryPayload[] = [] + const client = createClient(events, sessions, { autoTrackSessions: false }) + + const handler = () => 'abc' + + const event = { very: 'eventy' } + const context = { extremely: 'contextual' } + + const plugin = client.getPlugin('awsLambda') + + if (!plugin) { + throw new Error('Plugin was not loaded!') + } + + const bugsnagHandler = plugin.createHandler() + const wrappedHandler = bugsnagHandler(handler) + + expect(await wrappedHandler(event, context)).toBe('abc') + + expect(events).toHaveLength(0) + expect(sessions).toHaveLength(0) }) }) From 789784929ac6d94110904fca71ab7bbd65d7059d Mon Sep 17 00:00:00 2001 From: Joe Haines Date: Thu, 25 Feb 2021 17:08:10 +0000 Subject: [PATCH 27/65] Check for sessions in the MazeRunner tests --- test/aws-lambda/features/handled.feature | 4 ++++ test/aws-lambda/features/unhandled.feature | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/test/aws-lambda/features/handled.feature b/test/aws-lambda/features/handled.feature index 48136e21f..73ab82742 100644 --- a/test/aws-lambda/features/handled.feature +++ b/test/aws-lambda/features/handled.feature @@ -19,3 +19,7 @@ Scenario: handled exception in an async lambda And the "file" of stack frame 0 equals "handled-exception.js" And the event "metaData.AWS Lambda context.functionName" equals "AsyncHandledExceptionFunction" And the event "metaData.AWS Lambda context.awsRequestId" is not null + When I wait to receive a session + Then the session is valid for the session reporting API version "1" for the "Bugsnag Node" notifier + And the session "id" is not null + And the session "startedAt" is a timestamp diff --git a/test/aws-lambda/features/unhandled.feature b/test/aws-lambda/features/unhandled.feature index a9f3ed63d..42e773edb 100644 --- a/test/aws-lambda/features/unhandled.feature +++ b/test/aws-lambda/features/unhandled.feature @@ -23,3 +23,7 @@ Scenario: unhandled exception in an async lambda And the "file" of stack frame 0 equals "unhandled-exception.js" And the event "metaData.AWS Lambda context.functionName" equals "AsyncUnhandledExceptionFunction" And the event "metaData.AWS Lambda context.awsRequestId" is not null + When I wait to receive a session + Then the session is valid for the session reporting API version "1" for the "Bugsnag Node" notifier + And the session "id" is not null + And the session "startedAt" is a timestamp From 3689a480f49cb244629b599f801b63d28b6c50d7 Mon Sep 17 00:00:00 2001 From: Joe Haines Date: Fri, 26 Feb 2021 10:23:51 +0000 Subject: [PATCH 28/65] Calculate the duration for Lambda functions Our existing plugin-app-duration doesn't report the correct duration for Lambda functions because its 'appStart' is cached between invocations. This means the duration would essentially be the Lambda uptime, rather than the invocation time We now calculate the duration in the wrapped handler, which ensures it's fresh for each invocation --- packages/plugin-aws-lambda/src/index.js | 8 +++++ packages/plugin-aws-lambda/test/index.test.ts | 31 +++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/packages/plugin-aws-lambda/src/index.js b/packages/plugin-aws-lambda/src/index.js index 43f9f1529..c2fbcfd4a 100644 --- a/packages/plugin-aws-lambda/src/index.js +++ b/packages/plugin-aws-lambda/src/index.js @@ -26,6 +26,14 @@ function wrapHandler (client, flushTimeoutMs, handler) { } return async function (event, context) { + const startTime = new Date() + + client.addOnError(event => { + const endTime = new Date() + + event.app.duration = endTime - startTime + }) + client.addMetadata('AWS Lambda context', context) if (client._config.autoTrackSessions) { diff --git a/packages/plugin-aws-lambda/test/index.test.ts b/packages/plugin-aws-lambda/test/index.test.ts index 301c66061..a1798b987 100644 --- a/packages/plugin-aws-lambda/test/index.test.ts +++ b/packages/plugin-aws-lambda/test/index.test.ts @@ -514,4 +514,35 @@ describe('plugin: aws lambda', () => { expect(events).toHaveLength(0) expect(sessions).toHaveLength(0) }) + + it('sets the app.duration field on events', async () => { + const events: EventDeliveryPayload[] = [] + const sessions: SessionDeliveryPayload[] = [] + + const client = createClient(events, sessions) + + const handler = () => { throw new Error('oh no') } + + const event = { very: 'eventy' } + const context = { extremely: 'contextual' } + + const plugin = client.getPlugin('awsLambda') + + if (!plugin) { + throw new Error('Plugin was not loaded!') + } + + const bugsnagHandler = plugin.createHandler() + const wrappedHandler = bugsnagHandler(handler) + + await expect(() => wrappedHandler(event, context)).rejects.toThrow('oh no') + + expect(events).toHaveLength(1) + expect(sessions).toHaveLength(1) + + const duration = events[0].events[0].app.duration + + expect(duration).toBeGreaterThanOrEqual(0) + expect(duration).toBeLessThan(500) + }) }) From afb94ca4ae63e683a3372c4d8cf33c507867bdd2 Mon Sep 17 00:00:00 2001 From: Joe Haines Date: Tue, 2 Mar 2021 11:14:32 +0000 Subject: [PATCH 29/65] Add a reset method to plugin-app-duration --- packages/plugin-app-duration/app.js | 5 ++- packages/plugin-app-duration/test/app.test.ts | 34 +++++++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/packages/plugin-app-duration/app.js b/packages/plugin-app-duration/app.js index f987caef7..909a4e49d 100644 --- a/packages/plugin-app-duration/app.js +++ b/packages/plugin-app-duration/app.js @@ -1,4 +1,5 @@ -const appStart = new Date() +let appStart = new Date() +const reset = () => { appStart = new Date() } module.exports = { load: client => { @@ -7,5 +8,7 @@ module.exports = { event.app.duration = now - appStart }, true) + + return { reset } } } diff --git a/packages/plugin-app-duration/test/app.test.ts b/packages/plugin-app-duration/test/app.test.ts index 686eb2979..8e9f7cea2 100644 --- a/packages/plugin-app-duration/test/app.test.ts +++ b/packages/plugin-app-duration/test/app.test.ts @@ -1,5 +1,6 @@ import plugin from '../app' import Client from '@bugsnag/core/client' +import Event from '@bugsnag/core/event' describe('plugin-app-duration', () => { it('includes duration in event.app', done => { @@ -20,4 +21,37 @@ describe('plugin-app-duration', () => { client.notify(new Error('acbd')) }) + + it('can be restarted', async () => { + let appDurationCallback = (event: Event) => { throw new Error('Should never be called') } + + const event = { app: {} } as unknown as Event + const client = { + addOnError (callback: typeof appDurationCallback) { + appDurationCallback = callback + } + } + + const result = plugin.load(client) + + const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms)) + + await sleep(50) + + appDurationCallback(event) + expect(event.app.duration).toBeGreaterThanOrEqual(50) + + await sleep(50) + + appDurationCallback(event) + expect(event.app.duration).toBeGreaterThanOrEqual(100) + + result.reset() + + await sleep(25) + + appDurationCallback(event) + expect(event.app.duration).toBeGreaterThanOrEqual(25) + expect(event.app.duration).toBeLessThanOrEqual(100) + }) }) From eee8c863ef8be69a4614020ecd283a302392dcc1 Mon Sep 17 00:00:00 2001 From: Joe Haines Date: Tue, 2 Mar 2021 11:16:22 +0000 Subject: [PATCH 30/65] Give plugin-app-duration a name --- packages/plugin-app-duration/app.js | 1 + packages/plugin-app-duration/test/app.test.ts | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/packages/plugin-app-duration/app.js b/packages/plugin-app-duration/app.js index 909a4e49d..7d3c370bf 100644 --- a/packages/plugin-app-duration/app.js +++ b/packages/plugin-app-duration/app.js @@ -2,6 +2,7 @@ let appStart = new Date() const reset = () => { appStart = new Date() } module.exports = { + name: 'appDuration', load: client => { client.addOnError(event => { const now = new Date() diff --git a/packages/plugin-app-duration/test/app.test.ts b/packages/plugin-app-duration/test/app.test.ts index 8e9f7cea2..aebeacec2 100644 --- a/packages/plugin-app-duration/test/app.test.ts +++ b/packages/plugin-app-duration/test/app.test.ts @@ -22,6 +22,13 @@ describe('plugin-app-duration', () => { client.notify(new Error('acbd')) }) + it('has a name', () => { + expect(plugin.name).toBe('appDuration') + + const client = new Client({ apiKey: 'api_key', plugins: [plugin] }) + expect(client.getPlugin('appDuration')).toBeDefined() + }) + it('can be restarted', async () => { let appDurationCallback = (event: Event) => { throw new Error('Should never be called') } From 03514550c227826eec159f4326dbfd72abf1c771 Mon Sep 17 00:00:00 2001 From: Joe Haines Date: Tue, 2 Mar 2021 11:18:36 +0000 Subject: [PATCH 31/65] Reset the app duration between invocations --- packages/plugin-aws-lambda/src/index.js | 15 +++++---- packages/plugin-aws-lambda/test/index.test.ts | 31 ------------------- 2 files changed, 7 insertions(+), 39 deletions(-) diff --git a/packages/plugin-aws-lambda/src/index.js b/packages/plugin-aws-lambda/src/index.js index c2fbcfd4a..7bc1bce00 100644 --- a/packages/plugin-aws-lambda/src/index.js +++ b/packages/plugin-aws-lambda/src/index.js @@ -8,6 +8,13 @@ const BugsnagPluginAwsLambda = { bugsnagInFlight.trackInFlight(client) client._loadPlugin(BugsnagPluginBrowserSession) + // Reset the app duration between invocations, if the plugin is loaded + const appDurationPlugin = client.getPlugin('appDuration') + + if (appDurationPlugin) { + appDurationPlugin.reset() + } + return { createHandler ({ flushTimeoutMs = 2000 } = {}) { return wrapHandler.bind(null, client, flushTimeoutMs) @@ -26,14 +33,6 @@ function wrapHandler (client, flushTimeoutMs, handler) { } return async function (event, context) { - const startTime = new Date() - - client.addOnError(event => { - const endTime = new Date() - - event.app.duration = endTime - startTime - }) - client.addMetadata('AWS Lambda context', context) if (client._config.autoTrackSessions) { diff --git a/packages/plugin-aws-lambda/test/index.test.ts b/packages/plugin-aws-lambda/test/index.test.ts index a1798b987..301c66061 100644 --- a/packages/plugin-aws-lambda/test/index.test.ts +++ b/packages/plugin-aws-lambda/test/index.test.ts @@ -514,35 +514,4 @@ describe('plugin: aws lambda', () => { expect(events).toHaveLength(0) expect(sessions).toHaveLength(0) }) - - it('sets the app.duration field on events', async () => { - const events: EventDeliveryPayload[] = [] - const sessions: SessionDeliveryPayload[] = [] - - const client = createClient(events, sessions) - - const handler = () => { throw new Error('oh no') } - - const event = { very: 'eventy' } - const context = { extremely: 'contextual' } - - const plugin = client.getPlugin('awsLambda') - - if (!plugin) { - throw new Error('Plugin was not loaded!') - } - - const bugsnagHandler = plugin.createHandler() - const wrappedHandler = bugsnagHandler(handler) - - await expect(() => wrappedHandler(event, context)).rejects.toThrow('oh no') - - expect(events).toHaveLength(1) - expect(sessions).toHaveLength(1) - - const duration = events[0].events[0].app.duration - - expect(duration).toBeGreaterThanOrEqual(0) - expect(duration).toBeLessThan(500) - }) }) From 49fad3dfc802236e044fbf4620a315156fca823d Mon Sep 17 00:00:00 2001 From: Joe Haines Date: Tue, 2 Mar 2021 12:18:12 +0000 Subject: [PATCH 32/65] Fix arguments to Event.create --- packages/plugin-aws-lambda/src/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/plugin-aws-lambda/src/index.js b/packages/plugin-aws-lambda/src/index.js index 5e3a70681..fde7e0bbd 100644 --- a/packages/plugin-aws-lambda/src/index.js +++ b/packages/plugin-aws-lambda/src/index.js @@ -36,7 +36,7 @@ function wrapHandler (client, flushTimeoutMs, handler) { severityReason: { type: 'unhandledException' } } - const event = client.Event.create(err, true, handledState, 1) + const event = client.Event.create(err, true, handledState, 'aws lambda plugin', 1) client._notify(event) } From 1ae4502e167f24172d616b762ee7ccb637f7c19e Mon Sep 17 00:00:00 2001 From: Joe Haines Date: Fri, 26 Feb 2021 13:21:36 +0000 Subject: [PATCH 33/65] Simplify environment setup --- test/aws-lambda/features/handled.feature | 4 +--- test/aws-lambda/features/steps/aws-lambda-steps.rb | 8 ++++++++ test/aws-lambda/features/unhandled.feature | 4 +--- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/test/aws-lambda/features/handled.feature b/test/aws-lambda/features/handled.feature index 73ab82742..66639441d 100644 --- a/test/aws-lambda/features/handled.feature +++ b/test/aws-lambda/features/handled.feature @@ -1,9 +1,7 @@ Feature: Handled exceptions are reported correctly in lambda functions Scenario: handled exception in an async lambda - Given I store the api key in the environment variable "BUGSNAG_API_KEY" - And I store the notify endpoint in the environment variable "BUGSNAG_NOTIFY_ENDPOINT" - And I store the sessions endpoint in the environment variable "BUGSNAG_SESSIONS_ENDPOINT" + Given I setup the environment When I invoke the "AsyncHandledExceptionFunction" lambda in "features/fixtures/simple-app" with the "events/async/handled-exception.json" event Then the lambda response "body.message" equals "Did not crash!" And the lambda response "statusCode" equals 200 diff --git a/test/aws-lambda/features/steps/aws-lambda-steps.rb b/test/aws-lambda/features/steps/aws-lambda-steps.rb index 0fe2f4bad..1bcd38074 100644 --- a/test/aws-lambda/features/steps/aws-lambda-steps.rb +++ b/test/aws-lambda/features/steps/aws-lambda-steps.rb @@ -1,3 +1,11 @@ +Given('I setup the environment') do + steps %Q{ + Given I store the api key in the environment variable "BUGSNAG_API_KEY" + And I store the notify endpoint in the environment variable "BUGSNAG_NOTIFY_ENDPOINT" + And I store the sessions endpoint in the environment variable "BUGSNAG_SESSIONS_ENDPOINT" + } +end + Given('I store the notify endpoint in the environment variable {string}') do |name| step("I set environment variable '#{name}' to 'http://host.docker.internal:9339/notify'") end diff --git a/test/aws-lambda/features/unhandled.feature b/test/aws-lambda/features/unhandled.feature index 42e773edb..482385ae5 100644 --- a/test/aws-lambda/features/unhandled.feature +++ b/test/aws-lambda/features/unhandled.feature @@ -1,9 +1,7 @@ Feature: Unhandled exceptions are reported correctly in lambda functions Scenario: unhandled exception in an async lambda - Given I store the api key in the environment variable "BUGSNAG_API_KEY" - And I store the notify endpoint in the environment variable "BUGSNAG_NOTIFY_ENDPOINT" - And I store the sessions endpoint in the environment variable "BUGSNAG_SESSIONS_ENDPOINT" + Given I setup the environment When I invoke the "AsyncUnhandledExceptionFunction" lambda in "features/fixtures/simple-app" with the "events/async/unhandled-exception.json" event Then the lambda response "errorMessage" equals "Oh no!" And the lambda response "errorType" equals "Error" From e4f91d6c8af316f9ac1d77cbe9b31f87c657dc98 Mon Sep 17 00:00:00 2001 From: Joe Haines Date: Fri, 26 Feb 2021 13:22:01 +0000 Subject: [PATCH 34/65] Add simple tests against callback lambdas --- .../simple-app/callback/handled-exception.js | 24 ++++++++++ .../fixtures/simple-app/callback/package.json | 23 ++++++++++ .../callback/unhandled-exception.js | 19 ++++++++ .../events/callback/handled-exception.json | 46 +++++++++++++++++++ .../events/callback/unhandled-exception.json | 46 +++++++++++++++++++ .../fixtures/simple-app/template.yaml | 26 +++++++++++ test/aws-lambda/features/handled.feature | 22 +++++++++ test/aws-lambda/features/unhandled.feature | 26 +++++++++++ 8 files changed, 232 insertions(+) create mode 100644 test/aws-lambda/features/fixtures/simple-app/callback/handled-exception.js create mode 100644 test/aws-lambda/features/fixtures/simple-app/callback/package.json create mode 100644 test/aws-lambda/features/fixtures/simple-app/callback/unhandled-exception.js create mode 100644 test/aws-lambda/features/fixtures/simple-app/events/callback/handled-exception.json create mode 100644 test/aws-lambda/features/fixtures/simple-app/events/callback/unhandled-exception.json diff --git a/test/aws-lambda/features/fixtures/simple-app/callback/handled-exception.js b/test/aws-lambda/features/fixtures/simple-app/callback/handled-exception.js new file mode 100644 index 000000000..e1e6ee174 --- /dev/null +++ b/test/aws-lambda/features/fixtures/simple-app/callback/handled-exception.js @@ -0,0 +1,24 @@ +const Bugsnag = require('@bugsnag/js') +const BugsnagPluginAwsLambda = require('@bugsnag/plugin-aws-lambda') + +Bugsnag.start({ + apiKey: process.env.BUGSNAG_API_KEY, + endpoints: { + notify: process.env.BUGSNAG_NOTIFY_ENDPOINT, + sessions: process.env.BUGSNAG_SESSIONS_ENDPOINT + }, + plugins: [BugsnagPluginAwsLambda] +}) + +const bugsnagHandler = Bugsnag.getPlugin('awsLambda').createHandler() + +const handler = (event, context, callback) => { + Bugsnag.notify(new Error('Hello!')) + + callback(null, { + statusCode: 200, + body: JSON.stringify({ message: 'Did not crash!' }) + }) +} + +module.exports.lambdaHandler = bugsnagHandler(handler) diff --git a/test/aws-lambda/features/fixtures/simple-app/callback/package.json b/test/aws-lambda/features/fixtures/simple-app/callback/package.json new file mode 100644 index 000000000..a5c32db16 --- /dev/null +++ b/test/aws-lambda/features/fixtures/simple-app/callback/package.json @@ -0,0 +1,23 @@ +{ + "name": "simple-app", + "version": "1.0.0", + "description": "simple lambda app for testing", + "dependencies": { + "@bugsnag/core": "bugsnag-core.tgz", + "@bugsnag/delivery-node": "bugsnag-delivery-node.tgz", + "@bugsnag/in-flight": "bugsnag-in-flight.tgz", + "@bugsnag/js": "bugsnag-js.tgz", + "@bugsnag/node": "bugsnag-node.tgz", + "@bugsnag/plugin-app-duration": "bugsnag-plugin-app-duration.tgz", + "@bugsnag/plugin-aws-lambda": "bugsnag-plugin-aws-lambda.tgz", + "@bugsnag/plugin-contextualize": "bugsnag-plugin-contextualize.tgz", + "@bugsnag/plugin-intercept": "bugsnag-plugin-intercept.tgz", + "@bugsnag/plugin-node-device": "bugsnag-plugin-node-device.tgz", + "@bugsnag/plugin-node-in-project": "bugsnag-plugin-node-in-project.tgz", + "@bugsnag/plugin-node-surrounding-code": "bugsnag-plugin-node-surrounding-code.tgz", + "@bugsnag/plugin-node-uncaught-exception": "bugsnag-plugin-node-uncaught-exception.tgz", + "@bugsnag/plugin-node-unhandled-rejection": "bugsnag-plugin-node-unhandled-rejection.tgz", + "@bugsnag/plugin-server-session": "bugsnag-plugin-server-session.tgz", + "@bugsnag/plugin-strip-project-root": "bugsnag-plugin-strip-project-root.tgz" + } +} diff --git a/test/aws-lambda/features/fixtures/simple-app/callback/unhandled-exception.js b/test/aws-lambda/features/fixtures/simple-app/callback/unhandled-exception.js new file mode 100644 index 000000000..5768eee4f --- /dev/null +++ b/test/aws-lambda/features/fixtures/simple-app/callback/unhandled-exception.js @@ -0,0 +1,19 @@ +const Bugsnag = require('@bugsnag/js') +const BugsnagPluginAwsLambda = require('@bugsnag/plugin-aws-lambda') + +Bugsnag.start({ + apiKey: process.env.BUGSNAG_API_KEY, + endpoints: { + notify: process.env.BUGSNAG_NOTIFY_ENDPOINT, + sessions: process.env.BUGSNAG_SESSIONS_ENDPOINT + }, + plugins: [BugsnagPluginAwsLambda] +}) + +const bugsnagHandler = Bugsnag.getPlugin('awsLambda').createHandler() + +const handler = (event, context, callback) => { + callback(new Error('Oh no!')) +} + +module.exports.lambdaHandler = bugsnagHandler(handler) diff --git a/test/aws-lambda/features/fixtures/simple-app/events/callback/handled-exception.json b/test/aws-lambda/features/fixtures/simple-app/events/callback/handled-exception.json new file mode 100644 index 000000000..98e20b22c --- /dev/null +++ b/test/aws-lambda/features/fixtures/simple-app/events/callback/handled-exception.json @@ -0,0 +1,46 @@ +{ + "body": "", + "resource": "/{proxy+}", + "path": "/callback/handled/exception", + "httpMethod": "GET", + "isBase64Encoded": false, + "queryStringParameters": {}, + "multiValueQueryStringParameters": {}, + "pathParameters": {}, + "stageVariables": {}, + "headers": { + "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", + "Accept-Encoding": "gzip, deflate, sdch", + "Accept-Language": "en-US,en;q=0.8", + "Cache-Control": "max-age=0", + "Host": "1234567890.execute-api.us-east-1.amazonaws.com", + "Upgrade-Insecure-Requests": "1", + "User-Agent": "Custom User Agent String" + }, + "requestContext": { + "accountId": "123456789012", + "resourceId": "123456", + "stage": "prod", + "requestId": "c6af9ac6-7b61-11e6-9a41-93e8deadbeef", + "requestTime": "09/Apr/2015:12:34:56 +0000", + "requestTimeEpoch": 1428582896000, + "identity": { + "cognitoIdentityPoolId": null, + "accountId": null, + "cognitoIdentityId": null, + "caller": null, + "accessKey": null, + "sourceIp": "127.0.0.1", + "cognitoAuthenticationType": null, + "cognitoAuthenticationProvider": null, + "userArn": null, + "userAgent": "Custom User Agent String", + "user": null + }, + "path": "/prod/callback/handled/exception", + "resourcePath": "/{proxy+}", + "httpMethod": "GET", + "apiId": "1234567890", + "protocol": "HTTP/1.1" + } +} diff --git a/test/aws-lambda/features/fixtures/simple-app/events/callback/unhandled-exception.json b/test/aws-lambda/features/fixtures/simple-app/events/callback/unhandled-exception.json new file mode 100644 index 000000000..8055062cc --- /dev/null +++ b/test/aws-lambda/features/fixtures/simple-app/events/callback/unhandled-exception.json @@ -0,0 +1,46 @@ +{ + "body": "", + "resource": "/{proxy+}", + "path": "/callback/unhandled/exception", + "httpMethod": "GET", + "isBase64Encoded": false, + "queryStringParameters": {}, + "multiValueQueryStringParameters": {}, + "pathParameters": {}, + "stageVariables": {}, + "headers": { + "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", + "Accept-Encoding": "gzip, deflate, sdch", + "Accept-Language": "en-US,en;q=0.8", + "Cache-Control": "max-age=0", + "Host": "1234567890.execute-api.us-east-1.amazonaws.com", + "Upgrade-Insecure-Requests": "1", + "User-Agent": "Custom User Agent String" + }, + "requestContext": { + "accountId": "123456789012", + "resourceId": "123456", + "stage": "prod", + "requestId": "c6af9ac6-7b61-11e6-9a41-93e8deadbeef", + "requestTime": "09/Apr/2015:12:34:56 +0000", + "requestTimeEpoch": 1428582896000, + "identity": { + "cognitoIdentityPoolId": null, + "accountId": null, + "cognitoIdentityId": null, + "caller": null, + "accessKey": null, + "sourceIp": "127.0.0.1", + "cognitoAuthenticationType": null, + "cognitoAuthenticationProvider": null, + "userArn": null, + "userAgent": "Custom User Agent String", + "user": null + }, + "path": "/prod/callback/unhandled/exception", + "resourcePath": "/{proxy+}", + "httpMethod": "GET", + "apiId": "1234567890", + "protocol": "HTTP/1.1" + } +} diff --git a/test/aws-lambda/features/fixtures/simple-app/template.yaml b/test/aws-lambda/features/fixtures/simple-app/template.yaml index 34e33dc6a..830684283 100644 --- a/test/aws-lambda/features/fixtures/simple-app/template.yaml +++ b/test/aws-lambda/features/fixtures/simple-app/template.yaml @@ -37,3 +37,29 @@ Resources: Properties: Path: /async/handled/exception Method: get + + CallbackUnhandledExceptionFunction: + Type: AWS::Serverless::Function + Properties: + CodeUri: callback/ + Handler: unhandled-exception.lambdaHandler + Runtime: nodejs14.x + Events: + CallbackUnhandledException: + Type: Api + Properties: + Path: /callback/unhandled/exception + Method: get + + CallbackHandledExceptionFunction: + Type: AWS::Serverless::Function + Properties: + CodeUri: callback/ + Handler: handled-exception.lambdaHandler + Runtime: nodejs14.x + Events: + CallbackHandledException: + Type: Api + Properties: + Path: /callback/handled/exception + Method: get diff --git a/test/aws-lambda/features/handled.feature b/test/aws-lambda/features/handled.feature index 66639441d..f93e085c6 100644 --- a/test/aws-lambda/features/handled.feature +++ b/test/aws-lambda/features/handled.feature @@ -21,3 +21,25 @@ Scenario: handled exception in an async lambda Then the session is valid for the session reporting API version "1" for the "Bugsnag Node" notifier And the session "id" is not null And the session "startedAt" is a timestamp + +Scenario: handled exception in a callback lambda + Given I setup the environment + When I invoke the "CallbackHandledExceptionFunction" lambda in "features/fixtures/simple-app" with the "events/callback/handled-exception.json" event + Then the lambda response "body.message" equals "Did not crash!" + And the lambda response "statusCode" equals 200 + And the SAM exit code equals 0 + When I wait to receive an error + Then the error is valid for the error reporting API version "4" for the "Bugsnag Node" notifier + And the event "unhandled" is false + And the event "severity" equals "warning" + And the event "severityReason.type" equals "handledException" + And the exception "errorClass" equals "Error" + And the exception "message" equals "Hello!" + And the exception "type" equals "nodejs" + And the "file" of stack frame 0 equals "handled-exception.js" + And the event "metaData.AWS Lambda context.functionName" equals "CallbackHandledExceptionFunction" + And the event "metaData.AWS Lambda context.awsRequestId" is not null + When I wait to receive a session + Then the session is valid for the session reporting API version "1" for the "Bugsnag Node" notifier + And the session "id" is not null + And the session "startedAt" is a timestamp diff --git a/test/aws-lambda/features/unhandled.feature b/test/aws-lambda/features/unhandled.feature index 482385ae5..0619974be 100644 --- a/test/aws-lambda/features/unhandled.feature +++ b/test/aws-lambda/features/unhandled.feature @@ -25,3 +25,29 @@ Scenario: unhandled exception in an async lambda Then the session is valid for the session reporting API version "1" for the "Bugsnag Node" notifier And the session "id" is not null And the session "startedAt" is a timestamp + +Scenario: unhandled exception in an callback lambda + Given I setup the environment + When I invoke the "CallbackUnhandledExceptionFunction" lambda in "features/fixtures/simple-app" with the "events/callback/unhandled-exception.json" event + Then the lambda response "errorMessage" equals "Oh no!" + And the lambda response "errorType" equals "Error" + And the lambda response "trace" is an array with 7 elements + And the lambda response "trace.0" equals "Error: Oh no!" + And the lambda response "body" is null + And the lambda response "statusCode" is null + And the SAM exit code equals 0 + When I wait to receive an error + Then the error is valid for the error reporting API version "4" for the "Bugsnag Node" notifier + And the event "unhandled" is true + And the event "severity" equals "error" + And the event "severityReason.type" equals "unhandledException" + And the exception "errorClass" equals "Error" + And the exception "message" equals "Oh no!" + And the exception "type" equals "nodejs" + And the "file" of stack frame 0 equals "unhandled-exception.js" + And the event "metaData.AWS Lambda context.functionName" equals "CallbackUnhandledExceptionFunction" + And the event "metaData.AWS Lambda context.awsRequestId" is not null + When I wait to receive a session + Then the session is valid for the session reporting API version "1" for the "Bugsnag Node" notifier + And the session "id" is not null + And the session "startedAt" is a timestamp From 35540e9a376b3998af5b8362d8d01d3d263a50dc Mon Sep 17 00:00:00 2001 From: Joe Haines Date: Fri, 26 Feb 2021 15:23:30 +0000 Subject: [PATCH 35/65] Add tests for automatic session tracking --- .../simple-app/async/handled-exception.js | 3 +- .../simple-app/async/unhandled-exception.js | 3 +- .../simple-app/callback/handled-exception.js | 3 +- .../callback/unhandled-exception.js | 3 +- .../fixtures/simple-app/template.yaml | 1 + test/aws-lambda/features/sessions.feature | 29 +++++++++++++++++++ 6 files changed, 38 insertions(+), 4 deletions(-) create mode 100644 test/aws-lambda/features/sessions.feature diff --git a/test/aws-lambda/features/fixtures/simple-app/async/handled-exception.js b/test/aws-lambda/features/fixtures/simple-app/async/handled-exception.js index ce7a30115..2027f6ac9 100644 --- a/test/aws-lambda/features/fixtures/simple-app/async/handled-exception.js +++ b/test/aws-lambda/features/fixtures/simple-app/async/handled-exception.js @@ -7,7 +7,8 @@ Bugsnag.start({ notify: process.env.BUGSNAG_NOTIFY_ENDPOINT, sessions: process.env.BUGSNAG_SESSIONS_ENDPOINT }, - plugins: [BugsnagPluginAwsLambda] + plugins: [BugsnagPluginAwsLambda], + autoTrackSessions: process.env.BUGSNAG_AUTO_TRACK_SESSIONS !== 'false' }) const bugsnagHandler = Bugsnag.getPlugin('awsLambda').createHandler() diff --git a/test/aws-lambda/features/fixtures/simple-app/async/unhandled-exception.js b/test/aws-lambda/features/fixtures/simple-app/async/unhandled-exception.js index 37d4f6791..7b530c487 100644 --- a/test/aws-lambda/features/fixtures/simple-app/async/unhandled-exception.js +++ b/test/aws-lambda/features/fixtures/simple-app/async/unhandled-exception.js @@ -7,7 +7,8 @@ Bugsnag.start({ notify: process.env.BUGSNAG_NOTIFY_ENDPOINT, sessions: process.env.BUGSNAG_SESSIONS_ENDPOINT }, - plugins: [BugsnagPluginAwsLambda] + plugins: [BugsnagPluginAwsLambda], + autoTrackSessions: process.env.BUGSNAG_AUTO_TRACK_SESSIONS !== 'false' }) const bugsnagHandler = Bugsnag.getPlugin('awsLambda').createHandler() diff --git a/test/aws-lambda/features/fixtures/simple-app/callback/handled-exception.js b/test/aws-lambda/features/fixtures/simple-app/callback/handled-exception.js index e1e6ee174..afae8c14f 100644 --- a/test/aws-lambda/features/fixtures/simple-app/callback/handled-exception.js +++ b/test/aws-lambda/features/fixtures/simple-app/callback/handled-exception.js @@ -7,7 +7,8 @@ Bugsnag.start({ notify: process.env.BUGSNAG_NOTIFY_ENDPOINT, sessions: process.env.BUGSNAG_SESSIONS_ENDPOINT }, - plugins: [BugsnagPluginAwsLambda] + plugins: [BugsnagPluginAwsLambda], + autoTrackSessions: process.env.BUGSNAG_AUTO_TRACK_SESSIONS !== 'false' }) const bugsnagHandler = Bugsnag.getPlugin('awsLambda').createHandler() diff --git a/test/aws-lambda/features/fixtures/simple-app/callback/unhandled-exception.js b/test/aws-lambda/features/fixtures/simple-app/callback/unhandled-exception.js index 5768eee4f..500594ae4 100644 --- a/test/aws-lambda/features/fixtures/simple-app/callback/unhandled-exception.js +++ b/test/aws-lambda/features/fixtures/simple-app/callback/unhandled-exception.js @@ -7,7 +7,8 @@ Bugsnag.start({ notify: process.env.BUGSNAG_NOTIFY_ENDPOINT, sessions: process.env.BUGSNAG_SESSIONS_ENDPOINT }, - plugins: [BugsnagPluginAwsLambda] + plugins: [BugsnagPluginAwsLambda], + autoTrackSessions: process.env.BUGSNAG_AUTO_TRACK_SESSIONS !== 'false' }) const bugsnagHandler = Bugsnag.getPlugin('awsLambda').createHandler() diff --git a/test/aws-lambda/features/fixtures/simple-app/template.yaml b/test/aws-lambda/features/fixtures/simple-app/template.yaml index 830684283..cd4378a99 100644 --- a/test/aws-lambda/features/fixtures/simple-app/template.yaml +++ b/test/aws-lambda/features/fixtures/simple-app/template.yaml @@ -10,6 +10,7 @@ Globals: BUGSNAG_API_KEY: BUGSNAG_NOTIFY_ENDPOINT: BUGSNAG_SESSIONS_ENDPOINT: + BUGSNAG_AUTO_TRACK_SESSIONS: true Resources: AsyncUnhandledExceptionFunction: diff --git a/test/aws-lambda/features/sessions.feature b/test/aws-lambda/features/sessions.feature new file mode 100644 index 000000000..72c0fc610 --- /dev/null +++ b/test/aws-lambda/features/sessions.feature @@ -0,0 +1,29 @@ +Feature: Sessions are reported correctly in lambda functions + +Scenario: session in an async lambda + Given I setup the environment + When I invoke the "AsyncHandledExceptionFunction" lambda in "features/fixtures/simple-app" with the "events/async/handled-exception.json" event + And I wait to receive a session + Then the session is valid for the session reporting API version "1" for the "Bugsnag Node" notifier + And the session "id" is not null + And the session "startedAt" is a timestamp + +Scenario: session in a callback lambda + Given I setup the environment + When I invoke the "CallbackHandledExceptionFunction" lambda in "features/fixtures/simple-app" with the "events/callback/handled-exception.json" event + And I wait to receive a session + Then the session is valid for the session reporting API version "1" for the "Bugsnag Node" notifier + And the session "id" is not null + And the session "startedAt" is a timestamp + +Scenario: no session is sent when autoTrackSessions is false in an async lambda + Given I setup the environment + And I set environment variable "BUGSNAG_AUTO_TRACK_SESSIONS" to "false" + When I invoke the "AsyncHandledExceptionFunction" lambda in "features/fixtures/simple-app" with the "events/async/handled-exception.json" event + Then I should receive no sessions + +Scenario: no session is sent when autoTrackSessions is false in a callback lambda + Given I setup the environment + And I set environment variable "BUGSNAG_AUTO_TRACK_SESSIONS" to "false" + When I invoke the "CallbackHandledExceptionFunction" lambda in "features/fixtures/simple-app" with the "events/callback/handled-exception.json" event + Then I should receive no sessions From f6d7d45eba8bf36713ee5e75bd8d1e87a4650c9f Mon Sep 17 00:00:00 2001 From: Joe Haines Date: Mon, 1 Mar 2021 10:49:12 +0000 Subject: [PATCH 36/65] Add tests for autoDetectErrors --- .../simple-app/async/handled-exception.js | 1 + .../simple-app/async/unhandled-exception.js | 1 + .../simple-app/callback/handled-exception.js | 1 + .../callback/unhandled-exception.js | 1 + .../fixtures/simple-app/template.yaml | 1 + test/aws-lambda/features/handled.feature | 46 +++++++++++++++++++ test/aws-lambda/features/unhandled.feature | 12 +++++ 7 files changed, 63 insertions(+) diff --git a/test/aws-lambda/features/fixtures/simple-app/async/handled-exception.js b/test/aws-lambda/features/fixtures/simple-app/async/handled-exception.js index 2027f6ac9..92494cdb1 100644 --- a/test/aws-lambda/features/fixtures/simple-app/async/handled-exception.js +++ b/test/aws-lambda/features/fixtures/simple-app/async/handled-exception.js @@ -8,6 +8,7 @@ Bugsnag.start({ sessions: process.env.BUGSNAG_SESSIONS_ENDPOINT }, plugins: [BugsnagPluginAwsLambda], + autoDetectErrors: process.env.BUGSNAG_AUTO_DETECT_ERRORS !== 'false', autoTrackSessions: process.env.BUGSNAG_AUTO_TRACK_SESSIONS !== 'false' }) diff --git a/test/aws-lambda/features/fixtures/simple-app/async/unhandled-exception.js b/test/aws-lambda/features/fixtures/simple-app/async/unhandled-exception.js index 7b530c487..0357e500c 100644 --- a/test/aws-lambda/features/fixtures/simple-app/async/unhandled-exception.js +++ b/test/aws-lambda/features/fixtures/simple-app/async/unhandled-exception.js @@ -8,6 +8,7 @@ Bugsnag.start({ sessions: process.env.BUGSNAG_SESSIONS_ENDPOINT }, plugins: [BugsnagPluginAwsLambda], + autoDetectErrors: process.env.BUGSNAG_AUTO_DETECT_ERRORS !== 'false', autoTrackSessions: process.env.BUGSNAG_AUTO_TRACK_SESSIONS !== 'false' }) diff --git a/test/aws-lambda/features/fixtures/simple-app/callback/handled-exception.js b/test/aws-lambda/features/fixtures/simple-app/callback/handled-exception.js index afae8c14f..5d7fdd1b2 100644 --- a/test/aws-lambda/features/fixtures/simple-app/callback/handled-exception.js +++ b/test/aws-lambda/features/fixtures/simple-app/callback/handled-exception.js @@ -8,6 +8,7 @@ Bugsnag.start({ sessions: process.env.BUGSNAG_SESSIONS_ENDPOINT }, plugins: [BugsnagPluginAwsLambda], + autoDetectErrors: process.env.BUGSNAG_AUTO_DETECT_ERRORS !== 'false', autoTrackSessions: process.env.BUGSNAG_AUTO_TRACK_SESSIONS !== 'false' }) diff --git a/test/aws-lambda/features/fixtures/simple-app/callback/unhandled-exception.js b/test/aws-lambda/features/fixtures/simple-app/callback/unhandled-exception.js index 500594ae4..9585d8200 100644 --- a/test/aws-lambda/features/fixtures/simple-app/callback/unhandled-exception.js +++ b/test/aws-lambda/features/fixtures/simple-app/callback/unhandled-exception.js @@ -8,6 +8,7 @@ Bugsnag.start({ sessions: process.env.BUGSNAG_SESSIONS_ENDPOINT }, plugins: [BugsnagPluginAwsLambda], + autoDetectErrors: process.env.BUGSNAG_AUTO_DETECT_ERRORS !== 'false', autoTrackSessions: process.env.BUGSNAG_AUTO_TRACK_SESSIONS !== 'false' }) diff --git a/test/aws-lambda/features/fixtures/simple-app/template.yaml b/test/aws-lambda/features/fixtures/simple-app/template.yaml index cd4378a99..3c6f38e4e 100644 --- a/test/aws-lambda/features/fixtures/simple-app/template.yaml +++ b/test/aws-lambda/features/fixtures/simple-app/template.yaml @@ -11,6 +11,7 @@ Globals: BUGSNAG_NOTIFY_ENDPOINT: BUGSNAG_SESSIONS_ENDPOINT: BUGSNAG_AUTO_TRACK_SESSIONS: true + BUGSNAG_AUTO_DETECT_ERRORS: true Resources: AsyncUnhandledExceptionFunction: diff --git a/test/aws-lambda/features/handled.feature b/test/aws-lambda/features/handled.feature index f93e085c6..a1b39ad7a 100644 --- a/test/aws-lambda/features/handled.feature +++ b/test/aws-lambda/features/handled.feature @@ -43,3 +43,49 @@ Scenario: handled exception in a callback lambda Then the session is valid for the session reporting API version "1" for the "Bugsnag Node" notifier And the session "id" is not null And the session "startedAt" is a timestamp + +Scenario: handled exceptions are still reported in an async lambda when autoDetectErrors is false + Given I setup the environment + And I set environment variable "BUGSNAG_AUTO_DETECT_ERRORS" to "false" + When I invoke the "AsyncHandledExceptionFunction" lambda in "features/fixtures/simple-app" with the "events/async/handled-exception.json" event + Then the lambda response "body.message" equals "Did not crash!" + And the lambda response "statusCode" equals 200 + And the SAM exit code equals 0 + When I wait to receive an error + Then the error is valid for the error reporting API version "4" for the "Bugsnag Node" notifier + And the event "unhandled" is false + And the event "severity" equals "warning" + And the event "severityReason.type" equals "handledException" + And the exception "errorClass" equals "Error" + And the exception "message" equals "Hello!" + And the exception "type" equals "nodejs" + And the "file" of stack frame 0 equals "handled-exception.js" + And the event "metaData.AWS Lambda context.functionName" equals "AsyncHandledExceptionFunction" + And the event "metaData.AWS Lambda context.awsRequestId" is not null + When I wait to receive a session + Then the session is valid for the session reporting API version "1" for the "Bugsnag Node" notifier + And the session "id" is not null + And the session "startedAt" is a timestamp + +Scenario: handled exceptions are still reported in a callback lambda when autoDetectErrors is false + Given I setup the environment + And I set environment variable "BUGSNAG_AUTO_DETECT_ERRORS" to "false" + When I invoke the "CallbackHandledExceptionFunction" lambda in "features/fixtures/simple-app" with the "events/callback/handled-exception.json" event + Then the lambda response "body.message" equals "Did not crash!" + And the lambda response "statusCode" equals 200 + And the SAM exit code equals 0 + When I wait to receive an error + Then the error is valid for the error reporting API version "4" for the "Bugsnag Node" notifier + And the event "unhandled" is false + And the event "severity" equals "warning" + And the event "severityReason.type" equals "handledException" + And the exception "errorClass" equals "Error" + And the exception "message" equals "Hello!" + And the exception "type" equals "nodejs" + And the "file" of stack frame 0 equals "handled-exception.js" + And the event "metaData.AWS Lambda context.functionName" equals "CallbackHandledExceptionFunction" + And the event "metaData.AWS Lambda context.awsRequestId" is not null + When I wait to receive a session + Then the session is valid for the session reporting API version "1" for the "Bugsnag Node" notifier + And the session "id" is not null + And the session "startedAt" is a timestamp diff --git a/test/aws-lambda/features/unhandled.feature b/test/aws-lambda/features/unhandled.feature index 0619974be..dc9d3b807 100644 --- a/test/aws-lambda/features/unhandled.feature +++ b/test/aws-lambda/features/unhandled.feature @@ -51,3 +51,15 @@ Scenario: unhandled exception in an callback lambda Then the session is valid for the session reporting API version "1" for the "Bugsnag Node" notifier And the session "id" is not null And the session "startedAt" is a timestamp + +Scenario: no error is sent when autoDetectErrors is false in an async lambda + Given I setup the environment + And I set environment variable "BUGSNAG_AUTO_DETECT_ERRORS" to "false" + When I invoke the "AsyncUnhandledExceptionFunction" lambda in "features/fixtures/simple-app" with the "events/async/unhandled-exception.json" event + Then I should receive no errors + +Scenario: no error is sent when autoDetectErrors is false in a callback lambda + Given I setup the environment + And I set environment variable "BUGSNAG_AUTO_DETECT_ERRORS" to "false" + When I invoke the "CallbackUnhandledExceptionFunction" lambda in "features/fixtures/simple-app" with the "events/callback/unhandled-exception.json" event + Then I should receive no errors From fe585b51ff6d3fb29560871274eefcfb81548cd1 Mon Sep 17 00:00:00 2001 From: Joe Haines Date: Mon, 1 Mar 2021 17:44:11 +0000 Subject: [PATCH 37/65] Test on Node 12 as well as 14 --- .../fixtures/simple-app/template.yaml | 60 ++++++++++++++-- test/aws-lambda/features/handled.feature | 69 ++++++------------- test/aws-lambda/features/sessions.feature | 32 ++++----- test/aws-lambda/features/unhandled.feature | 55 +++++---------- 4 files changed, 111 insertions(+), 105 deletions(-) diff --git a/test/aws-lambda/features/fixtures/simple-app/template.yaml b/test/aws-lambda/features/fixtures/simple-app/template.yaml index 3c6f38e4e..ede315ef9 100644 --- a/test/aws-lambda/features/fixtures/simple-app/template.yaml +++ b/test/aws-lambda/features/fixtures/simple-app/template.yaml @@ -14,7 +14,7 @@ Globals: BUGSNAG_AUTO_DETECT_ERRORS: true Resources: - AsyncUnhandledExceptionFunction: + AsyncUnhandledExceptionFunctionNode14: Type: AWS::Serverless::Function Properties: CodeUri: async/ @@ -27,7 +27,7 @@ Resources: Path: /async/unhandled/exception Method: get - AsyncHandledExceptionFunction: + AsyncHandledExceptionFunctionNode14: Type: AWS::Serverless::Function Properties: CodeUri: async/ @@ -40,7 +40,7 @@ Resources: Path: /async/handled/exception Method: get - CallbackUnhandledExceptionFunction: + CallbackUnhandledExceptionFunctionNode14: Type: AWS::Serverless::Function Properties: CodeUri: callback/ @@ -53,7 +53,7 @@ Resources: Path: /callback/unhandled/exception Method: get - CallbackHandledExceptionFunction: + CallbackHandledExceptionFunctionNode14: Type: AWS::Serverless::Function Properties: CodeUri: callback/ @@ -65,3 +65,55 @@ Resources: Properties: Path: /callback/handled/exception Method: get + + AsyncUnhandledExceptionFunctionNode12: + Type: AWS::Serverless::Function + Properties: + CodeUri: async/ + Handler: unhandled-exception.lambdaHandler + Runtime: nodejs12.x + Events: + AsyncUnhandledException: + Type: Api + Properties: + Path: /async/unhandled/exception + Method: get + + AsyncHandledExceptionFunctionNode12: + Type: AWS::Serverless::Function + Properties: + CodeUri: async/ + Handler: handled-exception.lambdaHandler + Runtime: nodejs12.x + Events: + AsyncHandledException: + Type: Api + Properties: + Path: /async/handled/exception + Method: get + + CallbackUnhandledExceptionFunctionNode12: + Type: AWS::Serverless::Function + Properties: + CodeUri: callback/ + Handler: unhandled-exception.lambdaHandler + Runtime: nodejs12.x + Events: + CallbackUnhandledException: + Type: Api + Properties: + Path: /callback/unhandled/exception + Method: get + + CallbackHandledExceptionFunctionNode12: + Type: AWS::Serverless::Function + Properties: + CodeUri: callback/ + Handler: handled-exception.lambdaHandler + Runtime: nodejs12.x + Events: + CallbackHandledException: + Type: Api + Properties: + Path: /callback/handled/exception + Method: get diff --git a/test/aws-lambda/features/handled.feature b/test/aws-lambda/features/handled.feature index a1b39ad7a..a16659bfb 100644 --- a/test/aws-lambda/features/handled.feature +++ b/test/aws-lambda/features/handled.feature @@ -1,8 +1,8 @@ Feature: Handled exceptions are reported correctly in lambda functions -Scenario: handled exception in an async lambda +Scenario Outline: handled exceptions are reported Given I setup the environment - When I invoke the "AsyncHandledExceptionFunction" lambda in "features/fixtures/simple-app" with the "events/async/handled-exception.json" event + When I invoke the "" lambda in "features/fixtures/simple-app" with the "events//handled-exception.json" event Then the lambda response "body.message" equals "Did not crash!" And the lambda response "statusCode" equals 200 And the SAM exit code equals 0 @@ -15,39 +15,25 @@ Scenario: handled exception in an async lambda And the exception "message" equals "Hello!" And the exception "type" equals "nodejs" And the "file" of stack frame 0 equals "handled-exception.js" - And the event "metaData.AWS Lambda context.functionName" equals "AsyncHandledExceptionFunction" + And the event "metaData.AWS Lambda context.functionName" equals "" And the event "metaData.AWS Lambda context.awsRequestId" is not null + And the event "device.runtimeVersions.node" matches "^\.\d+\.\d+$" When I wait to receive a session Then the session is valid for the session reporting API version "1" for the "Bugsnag Node" notifier And the session "id" is not null And the session "startedAt" is a timestamp -Scenario: handled exception in a callback lambda - Given I setup the environment - When I invoke the "CallbackHandledExceptionFunction" lambda in "features/fixtures/simple-app" with the "events/callback/handled-exception.json" event - Then the lambda response "body.message" equals "Did not crash!" - And the lambda response "statusCode" equals 200 - And the SAM exit code equals 0 - When I wait to receive an error - Then the error is valid for the error reporting API version "4" for the "Bugsnag Node" notifier - And the event "unhandled" is false - And the event "severity" equals "warning" - And the event "severityReason.type" equals "handledException" - And the exception "errorClass" equals "Error" - And the exception "message" equals "Hello!" - And the exception "type" equals "nodejs" - And the "file" of stack frame 0 equals "handled-exception.js" - And the event "metaData.AWS Lambda context.functionName" equals "CallbackHandledExceptionFunction" - And the event "metaData.AWS Lambda context.awsRequestId" is not null - When I wait to receive a session - Then the session is valid for the session reporting API version "1" for the "Bugsnag Node" notifier - And the session "id" is not null - And the session "startedAt" is a timestamp + Examples: + | lambda | type | node-version | + | AsyncHandledExceptionFunctionNode14 | async | 14 | + | AsyncHandledExceptionFunctionNode12 | async | 12 | + | CallbackHandledExceptionFunctionNode14 | callback | 14 | + | CallbackHandledExceptionFunctionNode12 | callback | 12 | -Scenario: handled exceptions are still reported in an async lambda when autoDetectErrors is false +Scenario Outline: handled exceptions are still reported when autoDetectErrors is false Given I setup the environment And I set environment variable "BUGSNAG_AUTO_DETECT_ERRORS" to "false" - When I invoke the "AsyncHandledExceptionFunction" lambda in "features/fixtures/simple-app" with the "events/async/handled-exception.json" event + When I invoke the "" lambda in "features/fixtures/simple-app" with the "events//handled-exception.json" event Then the lambda response "body.message" equals "Did not crash!" And the lambda response "statusCode" equals 200 And the SAM exit code equals 0 @@ -60,32 +46,17 @@ Scenario: handled exceptions are still reported in an async lambda when autoDete And the exception "message" equals "Hello!" And the exception "type" equals "nodejs" And the "file" of stack frame 0 equals "handled-exception.js" - And the event "metaData.AWS Lambda context.functionName" equals "AsyncHandledExceptionFunction" + And the event "metaData.AWS Lambda context.functionName" equals "" And the event "metaData.AWS Lambda context.awsRequestId" is not null + And the event "device.runtimeVersions.node" matches "^\.\d+\.\d+$" When I wait to receive a session Then the session is valid for the session reporting API version "1" for the "Bugsnag Node" notifier And the session "id" is not null And the session "startedAt" is a timestamp -Scenario: handled exceptions are still reported in a callback lambda when autoDetectErrors is false - Given I setup the environment - And I set environment variable "BUGSNAG_AUTO_DETECT_ERRORS" to "false" - When I invoke the "CallbackHandledExceptionFunction" lambda in "features/fixtures/simple-app" with the "events/callback/handled-exception.json" event - Then the lambda response "body.message" equals "Did not crash!" - And the lambda response "statusCode" equals 200 - And the SAM exit code equals 0 - When I wait to receive an error - Then the error is valid for the error reporting API version "4" for the "Bugsnag Node" notifier - And the event "unhandled" is false - And the event "severity" equals "warning" - And the event "severityReason.type" equals "handledException" - And the exception "errorClass" equals "Error" - And the exception "message" equals "Hello!" - And the exception "type" equals "nodejs" - And the "file" of stack frame 0 equals "handled-exception.js" - And the event "metaData.AWS Lambda context.functionName" equals "CallbackHandledExceptionFunction" - And the event "metaData.AWS Lambda context.awsRequestId" is not null - When I wait to receive a session - Then the session is valid for the session reporting API version "1" for the "Bugsnag Node" notifier - And the session "id" is not null - And the session "startedAt" is a timestamp + Examples: + | lambda | type | node-version | + | AsyncHandledExceptionFunctionNode14 | async | 14 | + | AsyncHandledExceptionFunctionNode12 | async | 12 | + | CallbackHandledExceptionFunctionNode14 | callback | 14 | + | CallbackHandledExceptionFunctionNode12 | callback | 12 | diff --git a/test/aws-lambda/features/sessions.feature b/test/aws-lambda/features/sessions.feature index 72c0fc610..d2726ca8b 100644 --- a/test/aws-lambda/features/sessions.feature +++ b/test/aws-lambda/features/sessions.feature @@ -1,29 +1,29 @@ Feature: Sessions are reported correctly in lambda functions -Scenario: session in an async lambda +Scenario Outline: sessions are reported Given I setup the environment - When I invoke the "AsyncHandledExceptionFunction" lambda in "features/fixtures/simple-app" with the "events/async/handled-exception.json" event + When I invoke the "" lambda in "features/fixtures/simple-app" with the "events//handled-exception.json" event And I wait to receive a session Then the session is valid for the session reporting API version "1" for the "Bugsnag Node" notifier And the session "id" is not null And the session "startedAt" is a timestamp -Scenario: session in a callback lambda - Given I setup the environment - When I invoke the "CallbackHandledExceptionFunction" lambda in "features/fixtures/simple-app" with the "events/callback/handled-exception.json" event - And I wait to receive a session - Then the session is valid for the session reporting API version "1" for the "Bugsnag Node" notifier - And the session "id" is not null - And the session "startedAt" is a timestamp + Examples: + | lambda | type | + | AsyncUnhandledExceptionFunctionNode14 | async | + | AsyncUnhandledExceptionFunctionNode12 | async | + | CallbackUnhandledExceptionFunctionNode14 | callback | + | CallbackUnhandledExceptionFunctionNode12 | callback | -Scenario: no session is sent when autoTrackSessions is false in an async lambda +Scenario Outline: no session is sent when autoTrackSessions is false Given I setup the environment And I set environment variable "BUGSNAG_AUTO_TRACK_SESSIONS" to "false" - When I invoke the "AsyncHandledExceptionFunction" lambda in "features/fixtures/simple-app" with the "events/async/handled-exception.json" event + When I invoke the "" lambda in "features/fixtures/simple-app" with the "events//handled-exception.json" event Then I should receive no sessions -Scenario: no session is sent when autoTrackSessions is false in a callback lambda - Given I setup the environment - And I set environment variable "BUGSNAG_AUTO_TRACK_SESSIONS" to "false" - When I invoke the "CallbackHandledExceptionFunction" lambda in "features/fixtures/simple-app" with the "events/callback/handled-exception.json" event - Then I should receive no sessions + Examples: + | lambda | type | + | AsyncUnhandledExceptionFunctionNode14 | async | + | AsyncUnhandledExceptionFunctionNode12 | async | + | CallbackUnhandledExceptionFunctionNode14 | callback | + | CallbackUnhandledExceptionFunctionNode12 | callback | diff --git a/test/aws-lambda/features/unhandled.feature b/test/aws-lambda/features/unhandled.feature index dc9d3b807..cc7a739b0 100644 --- a/test/aws-lambda/features/unhandled.feature +++ b/test/aws-lambda/features/unhandled.feature @@ -1,11 +1,11 @@ Feature: Unhandled exceptions are reported correctly in lambda functions -Scenario: unhandled exception in an async lambda +Scenario Outline: unhandled exceptions are reported Given I setup the environment - When I invoke the "AsyncUnhandledExceptionFunction" lambda in "features/fixtures/simple-app" with the "events/async/unhandled-exception.json" event + When I invoke the "" lambda in "features/fixtures/simple-app" with the "events//unhandled-exception.json" event Then the lambda response "errorMessage" equals "Oh no!" And the lambda response "errorType" equals "Error" - And the lambda response "trace" is an array with 4 elements + And the lambda response "trace" is an array with elements And the lambda response "trace.0" equals "Error: Oh no!" And the lambda response "body" is null And the lambda response "statusCode" is null @@ -19,47 +19,30 @@ Scenario: unhandled exception in an async lambda And the exception "message" equals "Oh no!" And the exception "type" equals "nodejs" And the "file" of stack frame 0 equals "unhandled-exception.js" - And the event "metaData.AWS Lambda context.functionName" equals "AsyncUnhandledExceptionFunction" + And the event "metaData.AWS Lambda context.functionName" equals "" And the event "metaData.AWS Lambda context.awsRequestId" is not null + And the event "device.runtimeVersions.node" matches "^\.\d+\.\d+$" When I wait to receive a session Then the session is valid for the session reporting API version "1" for the "Bugsnag Node" notifier And the session "id" is not null And the session "startedAt" is a timestamp -Scenario: unhandled exception in an callback lambda - Given I setup the environment - When I invoke the "CallbackUnhandledExceptionFunction" lambda in "features/fixtures/simple-app" with the "events/callback/unhandled-exception.json" event - Then the lambda response "errorMessage" equals "Oh no!" - And the lambda response "errorType" equals "Error" - And the lambda response "trace" is an array with 7 elements - And the lambda response "trace.0" equals "Error: Oh no!" - And the lambda response "body" is null - And the lambda response "statusCode" is null - And the SAM exit code equals 0 - When I wait to receive an error - Then the error is valid for the error reporting API version "4" for the "Bugsnag Node" notifier - And the event "unhandled" is true - And the event "severity" equals "error" - And the event "severityReason.type" equals "unhandledException" - And the exception "errorClass" equals "Error" - And the exception "message" equals "Oh no!" - And the exception "type" equals "nodejs" - And the "file" of stack frame 0 equals "unhandled-exception.js" - And the event "metaData.AWS Lambda context.functionName" equals "CallbackUnhandledExceptionFunction" - And the event "metaData.AWS Lambda context.awsRequestId" is not null - When I wait to receive a session - Then the session is valid for the session reporting API version "1" for the "Bugsnag Node" notifier - And the session "id" is not null - And the session "startedAt" is a timestamp + Examples: + | lambda | type | node-version | trace-length | + | AsyncUnhandledExceptionFunctionNode14 | async | 14 | 4 | + | AsyncUnhandledExceptionFunctionNode12 | async | 12 | 4 | + | CallbackUnhandledExceptionFunctionNode14 | callback | 14 | 7 | + | CallbackUnhandledExceptionFunctionNode12 | callback | 12 | 7 | -Scenario: no error is sent when autoDetectErrors is false in an async lambda +Scenario Outline: no error is reported when autoDetectErrors is false Given I setup the environment And I set environment variable "BUGSNAG_AUTO_DETECT_ERRORS" to "false" - When I invoke the "AsyncUnhandledExceptionFunction" lambda in "features/fixtures/simple-app" with the "events/async/unhandled-exception.json" event + When I invoke the "" lambda in "features/fixtures/simple-app" with the "events//unhandled-exception.json" event Then I should receive no errors -Scenario: no error is sent when autoDetectErrors is false in a callback lambda - Given I setup the environment - And I set environment variable "BUGSNAG_AUTO_DETECT_ERRORS" to "false" - When I invoke the "CallbackUnhandledExceptionFunction" lambda in "features/fixtures/simple-app" with the "events/callback/unhandled-exception.json" event - Then I should receive no errors + Examples: + | lambda | type | + | AsyncUnhandledExceptionFunctionNode14 | async | + | AsyncUnhandledExceptionFunctionNode12 | async | + | CallbackUnhandledExceptionFunctionNode14 | callback | + | CallbackUnhandledExceptionFunctionNode12 | callback | From 1317b19fdcf9bb42b5afce9f4e3e94c07793f50d Mon Sep 17 00:00:00 2001 From: Joe Haines Date: Tue, 2 Mar 2021 14:02:49 +0000 Subject: [PATCH 38/65] Update to MazeRunner 4.11.1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This removes the need to hack around with Maze::Server 🎉 --- test/aws-lambda/Gemfile | 2 +- test/aws-lambda/Gemfile.lock | 10 ++--- test/aws-lambda/README.md | 4 +- test/aws-lambda/features/support/env.rb | 58 ------------------------- 4 files changed, 8 insertions(+), 66 deletions(-) diff --git a/test/aws-lambda/Gemfile b/test/aws-lambda/Gemfile index 1af5b5bef..4a6af8dc4 100644 --- a/test/aws-lambda/Gemfile +++ b/test/aws-lambda/Gemfile @@ -1,6 +1,6 @@ source 'https://rubygems.org' -gem 'bugsnag-maze-runner', git: 'https://github.com/bugsnag/maze-runner', tag: 'v4.9.1' +gem 'bugsnag-maze-runner', git: 'https://github.com/bugsnag/maze-runner', tag: 'v4.11.1' # Locally, you can run against Maze Runner branches and uncommitted changes: #gem 'bugsnag-maze-runner', path: '../../../maze-runner' diff --git a/test/aws-lambda/Gemfile.lock b/test/aws-lambda/Gemfile.lock index eca5cf10c..12b24dea3 100644 --- a/test/aws-lambda/Gemfile.lock +++ b/test/aws-lambda/Gemfile.lock @@ -1,9 +1,9 @@ GIT remote: https://github.com/bugsnag/maze-runner - revision: 8677bcf4c1ab27422084b19e7da8149ede954ded - tag: v4.9.1 + revision: b5925737b0c3db49ac38a5a68e6bfe38026ef508 + tag: v4.11.1 specs: - bugsnag-maze-runner (4.9.1) + bugsnag-maze-runner (4.11.1) appium_lib (~> 11.2.0) boring (~> 0.1.0) cucumber (~> 3.1.2) @@ -54,7 +54,7 @@ GEM eventmachine (>= 0.12.0) websocket-driver (>= 0.5.1) gherkin (5.1.0) - minitest (5.14.3) + minitest (5.14.4) multi_json (1.15.0) multi_test (0.1.2) nokogiri (1.11.1-x86_64-darwin) @@ -82,4 +82,4 @@ DEPENDENCIES bugsnag-maze-runner! BUNDLED WITH - 2.2.10 + 2.2.11 diff --git a/test/aws-lambda/README.md b/test/aws-lambda/README.md index a3500c031..56f69fa52 100644 --- a/test/aws-lambda/README.md +++ b/test/aws-lambda/README.md @@ -10,10 +10,10 @@ ## Running the tests -Run Maze Runner: +Run Maze Runner with the `--bind-address` option: ```sh -$ bundle exec maze-runner +$ bundle exec maze-runner --bind-address=0.0.0.0 ``` This will build all of the fixtures before running the tests diff --git a/test/aws-lambda/features/support/env.rb b/test/aws-lambda/features/support/env.rb index 52546aa86..677a8f427 100644 --- a/test/aws-lambda/features/support/env.rb +++ b/test/aws-lambda/features/support/env.rb @@ -1,61 +1,3 @@ -require 'maze/server' - -# FIXME: this is a temporary workaround that binds the Maze::Server to 0.0.0.0 -# so that it is reachable from the docker containers started by AWS SAM -# This can be removed when PLAT-6116 is done -module Maze - class Server - class << self - def start - attempts = 0 - $logger.info "Maze Runner v#{Maze::VERSION}" - $logger.info 'Starting mock server' - loop do - @thread = Thread.new do - server = WEBrick::HTTPServer.new( - BindAddress: '0.0.0.0', - Port: PORT, - Logger: $logger, - AccessLog: [] - ) - - # Mount a block to respond to all requests with status:200 - server.mount_proc '/' do |_request, response| - $logger.info 'Received request on server root, responding with 200' - response.header['Access-Control-Allow-Origin'] = '*' - response.body = 'Maze runner received request' - response.status = 200 - end - - # When adding more endpoints, be sure to update the 'I should receive no requests' step - server.mount '/notify', Servlet, errors - server.mount '/sessions', Servlet, sessions - server.mount '/builds', Servlet, builds - # server.mount '/logs', LogServlet - server.start - rescue StandardError => e - $logger.warn "Failed to start mock server: #{e.message}" - ensure - server&.shutdown - end - - # Need a short sleep here as a dying thread is still alive momentarily - sleep 1 - break if running? - - # Bail out after 3 attempts - attempts += 1 - raise 'Too many failed attempts to start mock server' if attempts == 3 - - # Failed to start - sleep before retrying - $logger.info 'Retrying in 5 seconds' - sleep 5 - end - end - end - end -end - `command -v sam` if $? != 0 From d7e92f6f18f9e53bd5b693a53d95840f93723c0a Mon Sep 17 00:00:00 2001 From: Joe Haines Date: Tue, 2 Mar 2021 14:03:56 +0000 Subject: [PATCH 39/65] Improve session tests --- test/aws-lambda/features/sessions.feature | 24 ++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/test/aws-lambda/features/sessions.feature b/test/aws-lambda/features/sessions.feature index d2726ca8b..aa75d04f6 100644 --- a/test/aws-lambda/features/sessions.feature +++ b/test/aws-lambda/features/sessions.feature @@ -7,19 +7,29 @@ Scenario Outline: sessions are reported Then the session is valid for the session reporting API version "1" for the "Bugsnag Node" notifier And the session "id" is not null And the session "startedAt" is a timestamp + When I wait to receive an error + Then the error is valid for the error reporting API version "4" for the "Bugsnag Node" notifier + And the event "session.events.handled" equals + And the event "session.events.unhandled" equals Examples: - | lambda | type | - | AsyncUnhandledExceptionFunctionNode14 | async | - | AsyncUnhandledExceptionFunctionNode12 | async | - | CallbackUnhandledExceptionFunctionNode14 | callback | - | CallbackUnhandledExceptionFunctionNode12 | callback | + | lambda | type | handled-count | unhandled-count | + | AsyncUnhandledExceptionFunctionNode14 | async | 0 | 1 | + | AsyncUnhandledExceptionFunctionNode12 | async | 0 | 1 | + | CallbackUnhandledExceptionFunctionNode14 | callback | 0 | 1 | + | CallbackUnhandledExceptionFunctionNode12 | callback | 0 | 1 | + | AsyncHandledExceptionFunctionNode14 | async | 1 | 0 | + | AsyncHandledExceptionFunctionNode12 | async | 1 | 0 | + | CallbackHandledExceptionFunctionNode14 | callback | 1 | 0 | + | CallbackHandledExceptionFunctionNode12 | callback | 1 | 0 | Scenario Outline: no session is sent when autoTrackSessions is false Given I setup the environment And I set environment variable "BUGSNAG_AUTO_TRACK_SESSIONS" to "false" When I invoke the "" lambda in "features/fixtures/simple-app" with the "events//handled-exception.json" event Then I should receive no sessions + When I wait to receive an error + Then the error is valid for the error reporting API version "4" for the "Bugsnag Node" notifier Examples: | lambda | type | @@ -27,3 +37,7 @@ Scenario Outline: no session is sent when autoTrackSessions is false | AsyncUnhandledExceptionFunctionNode12 | async | | CallbackUnhandledExceptionFunctionNode14 | callback | | CallbackUnhandledExceptionFunctionNode12 | callback | + | AsyncHandledExceptionFunctionNode14 | async | + | AsyncHandledExceptionFunctionNode12 | async | + | CallbackHandledExceptionFunctionNode14 | callback | + | CallbackHandledExceptionFunctionNode12 | callback | From 274eebf6c29cf6fc3ff2db21e9c381af45b30b5e Mon Sep 17 00:00:00 2001 From: Joe Haines Date: Tue, 2 Mar 2021 14:31:33 +0000 Subject: [PATCH 40/65] Add tests for a callback lambda that throws --- .../callback/thrown-unhandled-exception.js | 21 +++++++++++++++ .../fixtures/simple-app/template.yaml | 26 +++++++++++++++++++ test/aws-lambda/features/unhandled.feature | 26 +++++++++++-------- 3 files changed, 62 insertions(+), 11 deletions(-) create mode 100644 test/aws-lambda/features/fixtures/simple-app/callback/thrown-unhandled-exception.js diff --git a/test/aws-lambda/features/fixtures/simple-app/callback/thrown-unhandled-exception.js b/test/aws-lambda/features/fixtures/simple-app/callback/thrown-unhandled-exception.js new file mode 100644 index 000000000..55c5f5124 --- /dev/null +++ b/test/aws-lambda/features/fixtures/simple-app/callback/thrown-unhandled-exception.js @@ -0,0 +1,21 @@ +const Bugsnag = require('@bugsnag/js') +const BugsnagPluginAwsLambda = require('@bugsnag/plugin-aws-lambda') + +Bugsnag.start({ + apiKey: process.env.BUGSNAG_API_KEY, + endpoints: { + notify: process.env.BUGSNAG_NOTIFY_ENDPOINT, + sessions: process.env.BUGSNAG_SESSIONS_ENDPOINT + }, + plugins: [BugsnagPluginAwsLambda], + autoDetectErrors: process.env.BUGSNAG_AUTO_DETECT_ERRORS !== 'false', + autoTrackSessions: process.env.BUGSNAG_AUTO_TRACK_SESSIONS !== 'false' +}) + +const bugsnagHandler = Bugsnag.getPlugin('awsLambda').createHandler() + +const handler = (event, context, callback) => { + throw new Error('Oh no!') +} + +module.exports.lambdaHandler = bugsnagHandler(handler) diff --git a/test/aws-lambda/features/fixtures/simple-app/template.yaml b/test/aws-lambda/features/fixtures/simple-app/template.yaml index ede315ef9..6965e865d 100644 --- a/test/aws-lambda/features/fixtures/simple-app/template.yaml +++ b/test/aws-lambda/features/fixtures/simple-app/template.yaml @@ -53,6 +53,19 @@ Resources: Path: /callback/unhandled/exception Method: get + CallbackThrownUnhandledExceptionFunctionNode14: + Type: AWS::Serverless::Function + Properties: + CodeUri: callback/ + Handler: thrown-unhandled-exception.lambdaHandler + Runtime: nodejs14.x + Events: + CallbackThrownUnhandledException: + Type: Api + Properties: + Path: /callback/thrown/unhandled/exception + Method: get + CallbackHandledExceptionFunctionNode14: Type: AWS::Serverless::Function Properties: @@ -105,6 +118,19 @@ Resources: Path: /callback/unhandled/exception Method: get + CallbackThrownUnhandledExceptionFunctionNode12: + Type: AWS::Serverless::Function + Properties: + CodeUri: callback/ + Handler: thrown-unhandled-exception.lambdaHandler + Runtime: nodejs12.x + Events: + CallbackThrownUnhandledException: + Type: Api + Properties: + Path: /callback/thrown/unhandled/exception + Method: get + CallbackHandledExceptionFunctionNode12: Type: AWS::Serverless::Function Properties: diff --git a/test/aws-lambda/features/unhandled.feature b/test/aws-lambda/features/unhandled.feature index cc7a739b0..b203fdede 100644 --- a/test/aws-lambda/features/unhandled.feature +++ b/test/aws-lambda/features/unhandled.feature @@ -18,7 +18,7 @@ Scenario Outline: unhandled exceptions are reported And the exception "errorClass" equals "Error" And the exception "message" equals "Oh no!" And the exception "type" equals "nodejs" - And the "file" of stack frame 0 equals "unhandled-exception.js" + And the "file" of stack frame 0 equals "" And the event "metaData.AWS Lambda context.functionName" equals "" And the event "metaData.AWS Lambda context.awsRequestId" is not null And the event "device.runtimeVersions.node" matches "^\.\d+\.\d+$" @@ -28,11 +28,13 @@ Scenario Outline: unhandled exceptions are reported And the session "startedAt" is a timestamp Examples: - | lambda | type | node-version | trace-length | - | AsyncUnhandledExceptionFunctionNode14 | async | 14 | 4 | - | AsyncUnhandledExceptionFunctionNode12 | async | 12 | 4 | - | CallbackUnhandledExceptionFunctionNode14 | callback | 14 | 7 | - | CallbackUnhandledExceptionFunctionNode12 | callback | 12 | 7 | + | lambda | type | file | node-version | trace-length | + | AsyncUnhandledExceptionFunctionNode14 | async | unhandled-exception.js | 14 | 4 | + | AsyncUnhandledExceptionFunctionNode12 | async | unhandled-exception.js | 12 | 4 | + | CallbackUnhandledExceptionFunctionNode14 | callback | unhandled-exception.js | 14 | 7 | + | CallbackUnhandledExceptionFunctionNode12 | callback | unhandled-exception.js | 12 | 7 | + | CallbackThrownUnhandledExceptionFunctionNode14 | callback | thrown-unhandled-exception.js | 14 | 7 | + | CallbackThrownUnhandledExceptionFunctionNode12 | callback | thrown-unhandled-exception.js | 12 | 7 | Scenario Outline: no error is reported when autoDetectErrors is false Given I setup the environment @@ -41,8 +43,10 @@ Scenario Outline: no error is reported when autoDetectErrors is false Then I should receive no errors Examples: - | lambda | type | - | AsyncUnhandledExceptionFunctionNode14 | async | - | AsyncUnhandledExceptionFunctionNode12 | async | - | CallbackUnhandledExceptionFunctionNode14 | callback | - | CallbackUnhandledExceptionFunctionNode12 | callback | + | lambda | type | + | AsyncUnhandledExceptionFunctionNode14 | async | + | AsyncUnhandledExceptionFunctionNode12 | async | + | CallbackUnhandledExceptionFunctionNode14 | callback | + | CallbackUnhandledExceptionFunctionNode12 | callback | + | CallbackThrownUnhandledExceptionFunctionNode14 | callback | + | CallbackThrownUnhandledExceptionFunctionNode12 | callback | From 5f3f626425d16c56dd967d95bf848764ccb5b34b Mon Sep 17 00:00:00 2001 From: Joe Haines Date: Tue, 2 Mar 2021 14:46:47 +0000 Subject: [PATCH 41/65] Add throw callback fixture to session tests --- test/aws-lambda/features/sessions.feature | 42 ++++++++++++----------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/test/aws-lambda/features/sessions.feature b/test/aws-lambda/features/sessions.feature index aa75d04f6..422df46fd 100644 --- a/test/aws-lambda/features/sessions.feature +++ b/test/aws-lambda/features/sessions.feature @@ -2,7 +2,7 @@ Feature: Sessions are reported correctly in lambda functions Scenario Outline: sessions are reported Given I setup the environment - When I invoke the "" lambda in "features/fixtures/simple-app" with the "events//handled-exception.json" event + When I invoke the "" lambda in "features/fixtures/simple-app" And I wait to receive a session Then the session is valid for the session reporting API version "1" for the "Bugsnag Node" notifier And the session "id" is not null @@ -13,31 +13,33 @@ Scenario Outline: sessions are reported And the event "session.events.unhandled" equals Examples: - | lambda | type | handled-count | unhandled-count | - | AsyncUnhandledExceptionFunctionNode14 | async | 0 | 1 | - | AsyncUnhandledExceptionFunctionNode12 | async | 0 | 1 | - | CallbackUnhandledExceptionFunctionNode14 | callback | 0 | 1 | - | CallbackUnhandledExceptionFunctionNode12 | callback | 0 | 1 | - | AsyncHandledExceptionFunctionNode14 | async | 1 | 0 | - | AsyncHandledExceptionFunctionNode12 | async | 1 | 0 | - | CallbackHandledExceptionFunctionNode14 | callback | 1 | 0 | - | CallbackHandledExceptionFunctionNode12 | callback | 1 | 0 | + | lambda | handled-count | unhandled-count | + | AsyncUnhandledExceptionFunctionNode14 | 0 | 1 | + | AsyncUnhandledExceptionFunctionNode12 | 0 | 1 | + | CallbackUnhandledExceptionFunctionNode14 | 0 | 1 | + | CallbackUnhandledExceptionFunctionNode12 | 0 | 1 | + | CallbackThrownUnhandledExceptionFunctionNode14 | 0 | 1 | + | CallbackThrownUnhandledExceptionFunctionNode12 | 0 | 1 | + | AsyncHandledExceptionFunctionNode14 | 1 | 0 | + | AsyncHandledExceptionFunctionNode12 | 1 | 0 | + | CallbackHandledExceptionFunctionNode14 | 1 | 0 | + | CallbackHandledExceptionFunctionNode12 | 1 | 0 | Scenario Outline: no session is sent when autoTrackSessions is false Given I setup the environment And I set environment variable "BUGSNAG_AUTO_TRACK_SESSIONS" to "false" - When I invoke the "" lambda in "features/fixtures/simple-app" with the "events//handled-exception.json" event + When I invoke the "" lambda in "features/fixtures/simple-app" Then I should receive no sessions When I wait to receive an error Then the error is valid for the error reporting API version "4" for the "Bugsnag Node" notifier Examples: - | lambda | type | - | AsyncUnhandledExceptionFunctionNode14 | async | - | AsyncUnhandledExceptionFunctionNode12 | async | - | CallbackUnhandledExceptionFunctionNode14 | callback | - | CallbackUnhandledExceptionFunctionNode12 | callback | - | AsyncHandledExceptionFunctionNode14 | async | - | AsyncHandledExceptionFunctionNode12 | async | - | CallbackHandledExceptionFunctionNode14 | callback | - | CallbackHandledExceptionFunctionNode12 | callback | + | lambda | + | AsyncUnhandledExceptionFunctionNode14 | + | AsyncUnhandledExceptionFunctionNode12 | + | CallbackUnhandledExceptionFunctionNode14 | + | CallbackUnhandledExceptionFunctionNode12 | + | AsyncHandledExceptionFunctionNode14 | + | AsyncHandledExceptionFunctionNode12 | + | CallbackHandledExceptionFunctionNode14 | + | CallbackHandledExceptionFunctionNode12 | From 760a71939fb0b2bd263af57fb66e62dd0d5afa8a Mon Sep 17 00:00:00 2001 From: Joe Haines Date: Tue, 2 Mar 2021 14:47:10 +0000 Subject: [PATCH 42/65] Reduce MazeRunner wait times We don't need to wait long because the SAM invoke call is synchronous, so a request should always be sent before the wait step is even run We could probably even reduce these to a 0 second wait, but 10 seconds isn't that long and gives a bit of safety in case something changes in the future --- test/aws-lambda/features/support/env.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/aws-lambda/features/support/env.rb b/test/aws-lambda/features/support/env.rb index 677a8f427..0bf428b19 100644 --- a/test/aws-lambda/features/support/env.rb +++ b/test/aws-lambda/features/support/env.rb @@ -22,3 +22,5 @@ end Maze.config.enforce_bugsnag_integrity = false +Maze.config.receive_no_requests_wait = 10 +Maze.config.receive_requests_wait = 10 From 75867f758874af78112826216513314e5b6a91ae Mon Sep 17 00:00:00 2001 From: Joe Haines Date: Tue, 2 Mar 2021 15:27:28 +0000 Subject: [PATCH 43/65] Build in some leeway to app duration tests On GH actions sometimes the timeout can be invoked slightly too soon, e.g. after 24ms instead of 25 --- packages/plugin-app-duration/test/app.test.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/plugin-app-duration/test/app.test.ts b/packages/plugin-app-duration/test/app.test.ts index aebeacec2..7f6911d16 100644 --- a/packages/plugin-app-duration/test/app.test.ts +++ b/packages/plugin-app-duration/test/app.test.ts @@ -46,19 +46,19 @@ describe('plugin-app-duration', () => { await sleep(50) appDurationCallback(event) - expect(event.app.duration).toBeGreaterThanOrEqual(50) + expect(event.app.duration).toBeGreaterThanOrEqual(45) await sleep(50) appDurationCallback(event) - expect(event.app.duration).toBeGreaterThanOrEqual(100) + expect(event.app.duration).toBeGreaterThanOrEqual(90) result.reset() await sleep(25) appDurationCallback(event) - expect(event.app.duration).toBeGreaterThanOrEqual(25) - expect(event.app.duration).toBeLessThanOrEqual(100) + expect(event.app.duration).toBeGreaterThanOrEqual(20) + expect(event.app.duration).toBeLessThanOrEqual(90) }) }) From 5dc628b543d6cdaf6955b426c0419f4aa913f4a0 Mon Sep 17 00:00:00 2001 From: Joe Haines Date: Tue, 2 Mar 2021 16:05:39 +0000 Subject: [PATCH 44/65] Fix browser tests using removed constant --- test/browser/features/support/env.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/browser/features/support/env.rb b/test/browser/features/support/env.rb index 88bb63448..9d90d5d98 100644 --- a/test/browser/features/support/env.rb +++ b/test/browser/features/support/env.rb @@ -20,8 +20,8 @@ def get_test_url path host = ENV['HOST'] - notify = "http://#{ENV['API_HOST']}:#{Maze::Server::PORT}/notify" - sessions = "http://#{ENV['API_HOST']}:#{Maze::Server::PORT}/sessions" + notify = "http://#{ENV['API_HOST']}:#{Maze.config.port}/notify" + sessions = "http://#{ENV['API_HOST']}:#{Maze.config.port}/sessions" "http://#{host}:#{FIXTURES_SERVER_PORT}#{path}?NOTIFY=#{notify}&SESSIONS=#{sessions}&API_KEY=#{$api_key}" end From 9b12f9a398fa697a29d48bf122f03b0199312cff Mon Sep 17 00:00:00 2001 From: Joe Haines Date: Mon, 1 Mar 2021 16:34:36 +0000 Subject: [PATCH 45/65] Handle promise rejections --- packages/plugin-aws-lambda/package.json | 3 +- packages/plugin-aws-lambda/src/index.js | 17 ++++++ .../simple-app/async/promise-rejection.js | 26 +++++++++ .../simple-app/callback/promise-rejection.js | 26 +++++++++ .../events/async/promise-rejection.json | 46 ++++++++++++++++ .../events/callback/promise-rejection.json | 46 ++++++++++++++++ .../fixtures/simple-app/template.yaml | 52 ++++++++++++++++++ .../features/promise-rejection.feature | 55 +++++++++++++++++++ 8 files changed, 270 insertions(+), 1 deletion(-) create mode 100644 test/aws-lambda/features/fixtures/simple-app/async/promise-rejection.js create mode 100644 test/aws-lambda/features/fixtures/simple-app/callback/promise-rejection.js create mode 100644 test/aws-lambda/features/fixtures/simple-app/events/async/promise-rejection.json create mode 100644 test/aws-lambda/features/fixtures/simple-app/events/callback/promise-rejection.json create mode 100644 test/aws-lambda/features/promise-rejection.feature diff --git a/packages/plugin-aws-lambda/package.json b/packages/plugin-aws-lambda/package.json index e289aafe5..e8a5fda4c 100644 --- a/packages/plugin-aws-lambda/package.json +++ b/packages/plugin-aws-lambda/package.json @@ -25,7 +25,8 @@ "license": "MIT", "dependencies": { "@bugsnag/in-flight": "^7.7.0", - "@bugsnag/plugin-browser-session": "^7.7.0" + "@bugsnag/plugin-browser-session": "^7.7.0", + "@bugsnag/plugin-node-unhandled-rejection": "^7.7.0" }, "devDependencies": { "@bugsnag/core": "^7.7.0" diff --git a/packages/plugin-aws-lambda/src/index.js b/packages/plugin-aws-lambda/src/index.js index a9917cbdf..04790c684 100644 --- a/packages/plugin-aws-lambda/src/index.js +++ b/packages/plugin-aws-lambda/src/index.js @@ -15,6 +15,23 @@ const BugsnagPluginAwsLambda = { appDurationPlugin.reset() } + // AWS add a default unhandledRejection listener that forcefully exits the + // process. This breaks reporting of unhandled rejections, so we have to + // remove all existing listeners and call them after we handle the rejection + if (client._config.autoDetectErrors && client._config.enabledErrorTypes.unhandledRejections) { + const listeners = process.listeners('unhandledRejection') + process.removeAllListeners('unhandledRejection') + + const BugsnagPluginUnhandledRejection = require('@bugsnag/plugin-node-unhandled-rejection') + client._loadPlugin(BugsnagPluginUnhandledRejection) + + const originalOnUnhandledRejection = client._config.onUnhandledRejection + client._config.onUnhandledRejection = (reason, event, logger) => { + originalOnUnhandledRejection(reason, event, logger) + listeners.forEach(listener => { listener(reason) }) + } + } + return { createHandler ({ flushTimeoutMs = 2000 } = {}) { return wrapHandler.bind(null, client, flushTimeoutMs) diff --git a/test/aws-lambda/features/fixtures/simple-app/async/promise-rejection.js b/test/aws-lambda/features/fixtures/simple-app/async/promise-rejection.js new file mode 100644 index 000000000..b37603d9f --- /dev/null +++ b/test/aws-lambda/features/fixtures/simple-app/async/promise-rejection.js @@ -0,0 +1,26 @@ +const Bugsnag = require('@bugsnag/js') +const BugsnagPluginAwsLambda = require('@bugsnag/plugin-aws-lambda') + +Bugsnag.start({ + apiKey: process.env.BUGSNAG_API_KEY, + endpoints: { + notify: process.env.BUGSNAG_NOTIFY_ENDPOINT, + sessions: process.env.BUGSNAG_SESSIONS_ENDPOINT + }, + plugins: [BugsnagPluginAwsLambda], + autoDetectErrors: process.env.BUGSNAG_AUTO_DETECT_ERRORS !== 'false', + autoTrackSessions: process.env.BUGSNAG_AUTO_TRACK_SESSIONS !== 'false' +}) + +const bugsnagHandler = Bugsnag.getPlugin('awsLambda').createHandler() + +const handler = async (event, context) => { + Promise.reject(new Error('yikes')) + + return { + statusCode: 200, + body: JSON.stringify({ message: 'Did not crash!' }) + } +} + +module.exports.lambdaHandler = bugsnagHandler(handler) diff --git a/test/aws-lambda/features/fixtures/simple-app/callback/promise-rejection.js b/test/aws-lambda/features/fixtures/simple-app/callback/promise-rejection.js new file mode 100644 index 000000000..500133350 --- /dev/null +++ b/test/aws-lambda/features/fixtures/simple-app/callback/promise-rejection.js @@ -0,0 +1,26 @@ +const Bugsnag = require('@bugsnag/js') +const BugsnagPluginAwsLambda = require('@bugsnag/plugin-aws-lambda') + +Bugsnag.start({ + apiKey: process.env.BUGSNAG_API_KEY, + endpoints: { + notify: process.env.BUGSNAG_NOTIFY_ENDPOINT, + sessions: process.env.BUGSNAG_SESSIONS_ENDPOINT + }, + plugins: [BugsnagPluginAwsLambda], + autoDetectErrors: process.env.BUGSNAG_AUTO_DETECT_ERRORS !== 'false', + autoTrackSessions: process.env.BUGSNAG_AUTO_TRACK_SESSIONS !== 'false' +}) + +const bugsnagHandler = Bugsnag.getPlugin('awsLambda').createHandler() + +const handler = (event, context, callback) => { + Promise.reject(new Error('yikes')) + + callback(null, { + statusCode: 200, + body: JSON.stringify({ message: 'Did not crash!' }) + }) +} + +module.exports.lambdaHandler = bugsnagHandler(handler) diff --git a/test/aws-lambda/features/fixtures/simple-app/events/async/promise-rejection.json b/test/aws-lambda/features/fixtures/simple-app/events/async/promise-rejection.json new file mode 100644 index 000000000..882eba811 --- /dev/null +++ b/test/aws-lambda/features/fixtures/simple-app/events/async/promise-rejection.json @@ -0,0 +1,46 @@ +{ + "body": "", + "resource": "/{proxy+}", + "path": "/async/promise/rejection", + "httpMethod": "GET", + "isBase64Encoded": false, + "queryStringParameters": {}, + "multiValueQueryStringParameters": {}, + "pathParameters": {}, + "stageVariables": {}, + "headers": { + "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", + "Accept-Encoding": "gzip, deflate, sdch", + "Accept-Language": "en-US,en;q=0.8", + "Cache-Control": "max-age=0", + "Host": "1234567890.execute-api.us-east-1.amazonaws.com", + "Upgrade-Insecure-Requests": "1", + "User-Agent": "Custom User Agent String" + }, + "requestContext": { + "accountId": "123456789012", + "resourceId": "123456", + "stage": "prod", + "requestId": "c6af9ac6-7b61-11e6-9a41-93e8deadbeef", + "requestTime": "09/Apr/2015:12:34:56 +0000", + "requestTimeEpoch": 1428582896000, + "identity": { + "cognitoIdentityPoolId": null, + "accountId": null, + "cognitoIdentityId": null, + "caller": null, + "accessKey": null, + "sourceIp": "127.0.0.1", + "cognitoAuthenticationType": null, + "cognitoAuthenticationProvider": null, + "userArn": null, + "userAgent": "Custom User Agent String", + "user": null + }, + "path": "/prod/async/promise/rejection", + "resourcePath": "/{proxy+}", + "httpMethod": "GET", + "apiId": "1234567890", + "protocol": "HTTP/1.1" + } +} diff --git a/test/aws-lambda/features/fixtures/simple-app/events/callback/promise-rejection.json b/test/aws-lambda/features/fixtures/simple-app/events/callback/promise-rejection.json new file mode 100644 index 000000000..7efbcb170 --- /dev/null +++ b/test/aws-lambda/features/fixtures/simple-app/events/callback/promise-rejection.json @@ -0,0 +1,46 @@ +{ + "body": "", + "resource": "/{proxy+}", + "path": "/callback/promise/rejection", + "httpMethod": "GET", + "isBase64Encoded": false, + "queryStringParameters": {}, + "multiValueQueryStringParameters": {}, + "pathParameters": {}, + "stageVariables": {}, + "headers": { + "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", + "Accept-Encoding": "gzip, deflate, sdch", + "Accept-Language": "en-US,en;q=0.8", + "Cache-Control": "max-age=0", + "Host": "1234567890.execute-api.us-east-1.amazonaws.com", + "Upgrade-Insecure-Requests": "1", + "User-Agent": "Custom User Agent String" + }, + "requestContext": { + "accountId": "123456789012", + "resourceId": "123456", + "stage": "prod", + "requestId": "c6af9ac6-7b61-11e6-9a41-93e8deadbeef", + "requestTime": "09/Apr/2015:12:34:56 +0000", + "requestTimeEpoch": 1428582896000, + "identity": { + "cognitoIdentityPoolId": null, + "accountId": null, + "cognitoIdentityId": null, + "caller": null, + "accessKey": null, + "sourceIp": "127.0.0.1", + "cognitoAuthenticationType": null, + "cognitoAuthenticationProvider": null, + "userArn": null, + "userAgent": "Custom User Agent String", + "user": null + }, + "path": "/prod/callback/promise/rejection", + "resourcePath": "/{proxy+}", + "httpMethod": "GET", + "apiId": "1234567890", + "protocol": "HTTP/1.1" + } +} diff --git a/test/aws-lambda/features/fixtures/simple-app/template.yaml b/test/aws-lambda/features/fixtures/simple-app/template.yaml index 6965e865d..c5772d3b4 100644 --- a/test/aws-lambda/features/fixtures/simple-app/template.yaml +++ b/test/aws-lambda/features/fixtures/simple-app/template.yaml @@ -40,6 +40,19 @@ Resources: Path: /async/handled/exception Method: get + AsyncPromiseRejectionFunctionNode14: + Type: AWS::Serverless::Function + Properties: + CodeUri: async/ + Handler: promise-rejection.lambdaHandler + Runtime: nodejs14.x + Events: + AsyncPromiseRejection: + Type: Api + Properties: + Path: /async/promise/rejection + Method: get + CallbackUnhandledExceptionFunctionNode14: Type: AWS::Serverless::Function Properties: @@ -79,6 +92,19 @@ Resources: Path: /callback/handled/exception Method: get + CallbackPromiseRejectionFunctionNode14: + Type: AWS::Serverless::Function + Properties: + CodeUri: callback/ + Handler: promise-rejection.lambdaHandler + Runtime: nodejs14.x + Events: + CallbackPromiseRejection: + Type: Api + Properties: + Path: /callback/promise/rejection + Method: get + AsyncUnhandledExceptionFunctionNode12: Type: AWS::Serverless::Function Properties: @@ -105,6 +131,19 @@ Resources: Path: /async/handled/exception Method: get + AsyncPromiseRejectionFunctionNode12: + Type: AWS::Serverless::Function + Properties: + CodeUri: async/ + Handler: promise-rejection.lambdaHandler + Runtime: nodejs12.x + Events: + AsyncPromiseRejection: + Type: Api + Properties: + Path: /async/promise/rejection + Method: get + CallbackUnhandledExceptionFunctionNode12: Type: AWS::Serverless::Function Properties: @@ -143,3 +182,16 @@ Resources: Properties: Path: /callback/handled/exception Method: get + + CallbackPromiseRejectionFunctionNode12: + Type: AWS::Serverless::Function + Properties: + CodeUri: callback/ + Handler: promise-rejection.lambdaHandler + Runtime: nodejs12.x + Events: + CallbackPromiseRejection: + Type: Api + Properties: + Path: /callback/promise/rejection + Method: get diff --git a/test/aws-lambda/features/promise-rejection.feature b/test/aws-lambda/features/promise-rejection.feature new file mode 100644 index 000000000..2f1eb0277 --- /dev/null +++ b/test/aws-lambda/features/promise-rejection.feature @@ -0,0 +1,55 @@ +Feature: Unhandled promise rejections are reported correctly in lambda functions + +Scenario Outline: unhandled promise rejections are reported + Given I setup the environment + When I invoke the "" lambda in "features/fixtures/simple-app" with the "events//promise-rejection.json" event + Then the lambda response "errorMessage" equals "Error: yikes" + And the lambda response "errorType" equals "Runtime.UnhandledPromiseRejection" + And the lambda response "trace" is an array with 11 elements + And the lambda response "trace.0" equals "Runtime.UnhandledPromiseRejection: Error: yikes" + And the lambda response "body" is null + And the lambda response "statusCode" is null + And the SAM exit code equals 0 + When I wait to receive an error + Then the error is valid for the error reporting API version "4" for the "Bugsnag Node" notifier + And the event "unhandled" is true + And the event "severity" equals "error" + And the event "severityReason.type" equals "unhandledPromiseRejection" + And the exception "errorClass" equals "Error" + And the exception "message" equals "yikes" + And the exception "type" equals "nodejs" + And the "file" of stack frame 0 equals "promise-rejection.js" + And the event "metaData.AWS Lambda context.functionName" equals "" + And the event "metaData.AWS Lambda context.awsRequestId" is not null + And the event "device.runtimeVersions.node" matches "^\.\d+\.\d+$" + When I wait to receive a session + Then the session is valid for the session reporting API version "1" for the "Bugsnag Node" notifier + And the session "id" is not null + And the session "startedAt" is a timestamp + + Examples: + | lambda | type | node-version | + | AsyncPromiseRejectionFunctionNode14 | async | 14 | + | AsyncPromiseRejectionFunctionNode12 | async | 12 | + | CallbackPromiseRejectionFunctionNode14 | callback | 14 | + | CallbackPromiseRejectionFunctionNode12 | callback | 12 | + +Scenario Outline: unhandled promise rejections are not reported when autoDetectErrors is false + Given I setup the environment + And I set environment variable "BUGSNAG_AUTO_DETECT_ERRORS" to "false" + When I invoke the "" lambda in "features/fixtures/simple-app" with the "events//promise-rejection.json" event + Then the lambda response "errorMessage" equals "Error: yikes" + And the lambda response "errorType" equals "Runtime.UnhandledPromiseRejection" + And the lambda response "trace" is an array with 6 elements + And the lambda response "trace.0" equals "Runtime.UnhandledPromiseRejection: Error: yikes" + And the lambda response "body" is null + And the lambda response "statusCode" is null + And the SAM exit code equals 0 + And I should receive no errors + + Examples: + | lambda | type | + | AsyncPromiseRejectionFunctionNode14 | async | + | AsyncPromiseRejectionFunctionNode12 | async | + | CallbackPromiseRejectionFunctionNode14 | callback | + | CallbackPromiseRejectionFunctionNode12 | callback | From 9a3364e6e83d9754b1e6713e0d848aca47051029 Mon Sep 17 00:00:00 2001 From: Joe Haines Date: Tue, 2 Mar 2021 16:52:18 +0000 Subject: [PATCH 46/65] Avoid depending on unhandled rejection plugin This relies on the unhandled rejection plugin returning a promise when it's done and prepending its listener so that it's always first in the list Knowing both of these things, we can await it in a new listener and call all of the old listeners. This has the same behaviour as before but removes the explicit dependency on the unhandled rejection plugin --- packages/plugin-aws-lambda/package.json | 3 +- packages/plugin-aws-lambda/src/index.js | 15 ++-- .../test/unhandled-rejection.test.ts | 85 +++++++++++++++++++ .../unhandled-rejection.js | 18 +++- .../features/promise-rejection.feature | 2 +- 5 files changed, 108 insertions(+), 15 deletions(-) diff --git a/packages/plugin-aws-lambda/package.json b/packages/plugin-aws-lambda/package.json index e8a5fda4c..e289aafe5 100644 --- a/packages/plugin-aws-lambda/package.json +++ b/packages/plugin-aws-lambda/package.json @@ -25,8 +25,7 @@ "license": "MIT", "dependencies": { "@bugsnag/in-flight": "^7.7.0", - "@bugsnag/plugin-browser-session": "^7.7.0", - "@bugsnag/plugin-node-unhandled-rejection": "^7.7.0" + "@bugsnag/plugin-browser-session": "^7.7.0" }, "devDependencies": { "@bugsnag/core": "^7.7.0" diff --git a/packages/plugin-aws-lambda/src/index.js b/packages/plugin-aws-lambda/src/index.js index 04790c684..cc27c54e5 100644 --- a/packages/plugin-aws-lambda/src/index.js +++ b/packages/plugin-aws-lambda/src/index.js @@ -22,14 +22,13 @@ const BugsnagPluginAwsLambda = { const listeners = process.listeners('unhandledRejection') process.removeAllListeners('unhandledRejection') - const BugsnagPluginUnhandledRejection = require('@bugsnag/plugin-node-unhandled-rejection') - client._loadPlugin(BugsnagPluginUnhandledRejection) - - const originalOnUnhandledRejection = client._config.onUnhandledRejection - client._config.onUnhandledRejection = (reason, event, logger) => { - originalOnUnhandledRejection(reason, event, logger) - listeners.forEach(listener => { listener(reason) }) - } + // This relies on our unhandled rejection plugin adding its listener first + // using process.prependListener, so we can call it first instead of AWS' + process.on('unhandledRejection', async (reason, promise) => { + for (const listener of listeners) { + await listener.call(process, reason, promise) + } + }) } return { diff --git a/packages/plugin-node-unhandled-rejection/test/unhandled-rejection.test.ts b/packages/plugin-node-unhandled-rejection/test/unhandled-rejection.test.ts index cedf313d5..be8465a0f 100644 --- a/packages/plugin-node-unhandled-rejection/test/unhandled-rejection.test.ts +++ b/packages/plugin-node-unhandled-rejection/test/unhandled-rejection.test.ts @@ -88,4 +88,89 @@ describe('plugin: node unhandled rejection handler', () => { })) process.listeners('unhandledRejection')[0](new Error('never gonna catch me'), Promise.resolve()) }) + + it('should return a promise that resolves after the onUnhandledRejection callback is called', async () => { + try { + const options = { + apiKey: 'api_key', + onUnhandledRejection: jest.fn(), + plugins: [plugin] + } + + const pluginSchema = { + ...schema, + onUnhandledRejection: { + validate: (val: unknown) => typeof val === 'function', + message: 'should be a function', + defaultValue: () => {} + } + } + + const client = new Client(options, pluginSchema) + + client._setDelivery(client => ({ + sendEvent: (payload, cb) => cb(), + sendSession: (payload, cb) => cb() + })) + + const listener = process.listeners('unhandledRejection')[0] + + expect(options.onUnhandledRejection).not.toHaveBeenCalled() + + await listener(new Error('never gonna catch me'), Promise.resolve()) + + expect(options.onUnhandledRejection).toHaveBeenCalledTimes(1) + } finally { + plugin.destroy() + } + }) + + it('should prepend its listener (Node 6+)', async () => { + // Skip this test on Node 4/5 as prependListener doesn't exist + if (process.version.startsWith('v4.') || process.version.startsWith('v5.')) { + return + } + + const listener = () => {} + + try { + process.on('unhandledRejection', listener) + + const listenersBefore = process.listeners('unhandledRejection') + + expect(listenersBefore).toHaveLength(1) + expect(listenersBefore[0]).toBe(listener) + + const options = { + apiKey: 'api_key', + onUnhandledRejection: jest.fn(), + plugins: [plugin] + } + + const pluginSchema = { + ...schema, + onUnhandledRejection: { + validate: (val: unknown) => typeof val === 'function', + message: 'should be a function', + defaultValue: () => {} + } + } + + const client = new Client(options, pluginSchema) + + client._setDelivery(client => ({ + sendEvent: (payload, cb) => cb(), + sendSession: (payload, cb) => cb() + })) + + const listenersAfter = process.listeners('unhandledRejection') + + expect(listenersAfter).toHaveLength(2) + expect(listenersAfter[0]).not.toBe(listener) + expect(listenersAfter[1]).toBe(listener) + } finally { + process.removeListener('unhandledRejection', listener) + plugin.destroy() + } + }) }) diff --git a/packages/plugin-node-unhandled-rejection/unhandled-rejection.js b/packages/plugin-node-unhandled-rejection/unhandled-rejection.js index d67aa837a..20ac5530d 100644 --- a/packages/plugin-node-unhandled-rejection/unhandled-rejection.js +++ b/packages/plugin-node-unhandled-rejection/unhandled-rejection.js @@ -8,12 +8,22 @@ module.exports = { unhandled: true, severityReason: { type: 'unhandledPromiseRejection' } }, 'unhandledRejection handler', 1) - client._notify(event, () => {}, (e, event) => { - if (e) client._logger.error('Failed to send event to Bugsnag') - client._config.onUnhandledRejection(err, event, client._logger) + + return new Promise(resolve => { + client._notify(event, () => {}, (e, event) => { + if (e) client._logger.error('Failed to send event to Bugsnag') + client._config.onUnhandledRejection(err, event, client._logger) + resolve() + }) }) } - process.on('unhandledRejection', _handler) + + // Prepend the listener if we can (Node 6+) + if (process.prependListener) { + process.prependListener('unhandledRejection', _handler) + } else { + process.on('unhandledRejection', _handler) + } }, destroy: () => { process.removeListener('unhandledRejection', _handler) diff --git a/test/aws-lambda/features/promise-rejection.feature b/test/aws-lambda/features/promise-rejection.feature index 2f1eb0277..24261798d 100644 --- a/test/aws-lambda/features/promise-rejection.feature +++ b/test/aws-lambda/features/promise-rejection.feature @@ -5,7 +5,7 @@ Scenario Outline: unhandled promise rejections are reported When I invoke the "" lambda in "features/fixtures/simple-app" with the "events//promise-rejection.json" event Then the lambda response "errorMessage" equals "Error: yikes" And the lambda response "errorType" equals "Runtime.UnhandledPromiseRejection" - And the lambda response "trace" is an array with 11 elements + And the lambda response "trace" is an array with 4 elements And the lambda response "trace.0" equals "Runtime.UnhandledPromiseRejection: Error: yikes" And the lambda response "body" is null And the lambda response "statusCode" is null From 51a4422e2a84b2c9732272a9aacf39c34a6e0db3 Mon Sep 17 00:00:00 2001 From: Joe Haines Date: Wed, 3 Mar 2021 13:54:37 +0000 Subject: [PATCH 47/65] Automatically notify when the lambda may timeout We can never be 100% sure that a timeout will happen because we need to notify with enough time for the event to be delivered. Therefore this is a best effort with a generous default value (1000ms) that errs on the side of delivering the warning at the cost of possible false positives The default value can be changed by providing "lambdaTimeoutNotifyMs" to createHandler. This is useful in cases where the lambda function is very quick to execute and so can have a very short timeout without necessarily being at risk of timing out If set to "0" we don't bother adding the warning at all. This is largely arbitrary as a value of 0 would never be triggered (because the lambda would timeout at the same time) --- packages/plugin-aws-lambda/src/index.js | 53 ++++- .../src/lambda-timeout-approaching.js | 11 + packages/plugin-aws-lambda/test/index.test.ts | 221 ++++++++++++++++++ .../types/bugsnag-plugin-aws-lambda.d.ts | 1 + 4 files changed, 283 insertions(+), 3 deletions(-) create mode 100644 packages/plugin-aws-lambda/src/lambda-timeout-approaching.js diff --git a/packages/plugin-aws-lambda/src/index.js b/packages/plugin-aws-lambda/src/index.js index cc27c54e5..67c0e379c 100644 --- a/packages/plugin-aws-lambda/src/index.js +++ b/packages/plugin-aws-lambda/src/index.js @@ -1,5 +1,11 @@ const bugsnagInFlight = require('@bugsnag/in-flight') const BugsnagPluginBrowserSession = require('@bugsnag/plugin-browser-session') +const LambdaTimeoutApproaching = require('./lambda-timeout-approaching') + +// JS timers use a signed 32 bit integer for the millisecond parameter. SAM's +// "local invoke" has a bug that means it exceeds this amount, resulting in +// warnings. See https://github.com/aws/aws-sam-cli/issues/2519 +const MAX_TIMER_VALUE = Math.pow(2, 31) - 1 const BugsnagPluginAwsLambda = { name: 'awsLambda', @@ -32,14 +38,14 @@ const BugsnagPluginAwsLambda = { } return { - createHandler ({ flushTimeoutMs = 2000 } = {}) { - return wrapHandler.bind(null, client, flushTimeoutMs) + createHandler ({ flushTimeoutMs = 2000, lambdaTimeoutNotifyMs = 1000 } = {}) { + return wrapHandler.bind(null, client, flushTimeoutMs, lambdaTimeoutNotifyMs) } } } } -function wrapHandler (client, flushTimeoutMs, handler) { +function wrapHandler (client, flushTimeoutMs, lambdaTimeoutNotifyMs, handler) { let _handler = handler if (handler.length > 2) { @@ -49,6 +55,43 @@ function wrapHandler (client, flushTimeoutMs, handler) { } return async function (event, context) { + let lambdaTimeout + + // Guard against the "getRemainingTimeInMillis" being missing. This should + // never happen but could when unit testing + if (typeof context.getRemainingTimeInMillis === 'function' && + lambdaTimeoutNotifyMs > 0 + ) { + // Clamp the timeout value between 0 and MAX_TIMER_VALUE + const timeoutMs = Math.min( + Math.max( + context.getRemainingTimeInMillis() - lambdaTimeoutNotifyMs, + 0 + ), + MAX_TIMER_VALUE + ) + + lambdaTimeout = setTimeout(function () { + const handledState = { + severity: 'warning', + unhandled: true, + severityReason: { type: 'log' } + } + + const event = client.Event.create( + new LambdaTimeoutApproaching(context.getRemainingTimeInMillis()), + true, + handledState, + 'aws lambda plugin', + 0 + ) + + event.context = context.functionName || 'Lambda timeout approaching' + + client._notify(event) + }, timeoutMs) + } + client.addMetadata('AWS Lambda context', context) if (client._config.autoTrackSessions) { @@ -72,6 +115,10 @@ function wrapHandler (client, flushTimeoutMs, handler) { throw err } finally { + if (lambdaTimeout) { + clearTimeout(lambdaTimeout) + } + try { await bugsnagInFlight.flush(flushTimeoutMs) } catch (err) { diff --git a/packages/plugin-aws-lambda/src/lambda-timeout-approaching.js b/packages/plugin-aws-lambda/src/lambda-timeout-approaching.js new file mode 100644 index 000000000..7b8198062 --- /dev/null +++ b/packages/plugin-aws-lambda/src/lambda-timeout-approaching.js @@ -0,0 +1,11 @@ +module.exports = class LambdaTimeoutApproaching extends Error { + constructor (remainingMs) { + const message = `Lambda will timeout in ${remainingMs}ms` + + super(message) + + this.name = 'LambdaTimeoutApproaching' + this.message = message + this.stack = [] + } +} diff --git a/packages/plugin-aws-lambda/test/index.test.ts b/packages/plugin-aws-lambda/test/index.test.ts index 301c66061..0532d8cca 100644 --- a/packages/plugin-aws-lambda/test/index.test.ts +++ b/packages/plugin-aws-lambda/test/index.test.ts @@ -24,6 +24,16 @@ const createClient = (events: EventDeliveryPayload[], sessions: SessionDeliveryP return client } +const DEFAULT_REMAINING_MS = 250 +let getRemainingTimeInMillis: jest.MockedFunction<() => number> + +beforeEach(() => { + getRemainingTimeInMillis = jest.fn() + .mockReturnValueOnce(DEFAULT_REMAINING_MS) + .mockReturnValueOnce(DEFAULT_REMAINING_MS / 2) + .mockImplementationOnce(() => { throw new Error('unexpected call to "getRemainingTimeInMillis"') }) +}) + describe('plugin: aws lambda', () => { it('has a name', () => { expect(BugsnagPluginAwsLambda.name).toBe('awsLambda') @@ -514,4 +524,215 @@ describe('plugin: aws lambda', () => { expect(events).toHaveLength(0) expect(sessions).toHaveLength(0) }) + + it('notifies when it is close to timing out (async)', async () => { + const events: EventDeliveryPayload[] = [] + const sessions: SessionDeliveryPayload[] = [] + + const client = createClient(events, sessions) + + const handler = async (event: any, context: any) => new Promise(resolve => { + setTimeout(() => resolve('xyz'), DEFAULT_REMAINING_MS + 100) + }) + + const event = { very: 'eventy' } + const context = { extremely: 'contextual', getRemainingTimeInMillis } + + const plugin = client.getPlugin('awsLambda') + + if (!plugin) { + throw new Error('Plugin was not loaded!') + } + + const bugsnagHandler = plugin.createHandler() + const wrappedHandler = bugsnagHandler(handler) + + expect(events).toHaveLength(0) + expect(sessions).toHaveLength(0) + + expect(await wrappedHandler(event, context)).toBe('xyz') + + expect(events).toHaveLength(1) + expect(events[0].events).toHaveLength(1) + expect(events[0].events[0].errors).toHaveLength(1) + expect(events[0].events[0].context).toBe('Lambda timeout approaching') + + const expectedError = { + errorClass: 'LambdaTimeoutApproaching', + errorMessage: `Lambda will timeout in ${DEFAULT_REMAINING_MS / 2}ms`, + stacktrace: [], + type: 'nodejs' + } + + expect(events[0].events[0].errors[0]).toEqual(expectedError) + + expect(sessions).toHaveLength(1) + }) + + it('notifies when it is close to timing out (callback)', async () => { + const events: EventDeliveryPayload[] = [] + const sessions: SessionDeliveryPayload[] = [] + + const client = createClient(events, sessions) + + const handler = (event: any, context: any, callback: any) => new Promise(resolve => { + setTimeout(() => callback(null, 'xyz'), DEFAULT_REMAINING_MS + 100) + }) + + const event = { very: 'eventy' } + const context = { extremely: 'contextual', getRemainingTimeInMillis } + + const plugin = client.getPlugin('awsLambda') + + if (!plugin) { + throw new Error('Plugin was not loaded!') + } + + const bugsnagHandler = plugin.createHandler() + const wrappedHandler = bugsnagHandler(handler) + + expect(events).toHaveLength(0) + expect(sessions).toHaveLength(0) + + expect(await wrappedHandler(event, context)).toBe('xyz') + + expect(events).toHaveLength(1) + expect(events[0].events).toHaveLength(1) + expect(events[0].events[0].errors).toHaveLength(1) + expect(events[0].events[0].context).toBe('Lambda timeout approaching') + + const expectedError = { + errorClass: 'LambdaTimeoutApproaching', + errorMessage: `Lambda will timeout in ${DEFAULT_REMAINING_MS / 2}ms`, + stacktrace: [], + type: 'nodejs' + } + + expect(events[0].events[0].errors[0]).toEqual(expectedError) + + expect(sessions).toHaveLength(1) + }) + + it('uses the function name as the event context when present', async () => { + const events: EventDeliveryPayload[] = [] + const sessions: SessionDeliveryPayload[] = [] + + const client = createClient(events, sessions) + + const handler = async (event: any, context: any) => new Promise(resolve => { + setTimeout(() => resolve('xyz'), DEFAULT_REMAINING_MS + 100) + }) + + const event = { very: 'eventy' } + const context = { functionName: 'MyCoolAndGoodLambdaFunction', getRemainingTimeInMillis } + + const plugin = client.getPlugin('awsLambda') + + if (!plugin) { + throw new Error('Plugin was not loaded!') + } + + const bugsnagHandler = plugin.createHandler() + const wrappedHandler = bugsnagHandler(handler) + + expect(events).toHaveLength(0) + expect(sessions).toHaveLength(0) + + expect(await wrappedHandler(event, context)).toBe('xyz') + + expect(events).toHaveLength(1) + expect(events[0].events[0].errors[0].errorClass).toBe('LambdaTimeoutApproaching') + expect(events[0].events[0].errors[0].errorMessage).toBe(`Lambda will timeout in ${DEFAULT_REMAINING_MS / 2}ms`) + expect(events[0].events[0].errors[0].stacktrace).toHaveLength(0) + expect(events[0].events[0].context).toBe('MyCoolAndGoodLambdaFunction') + + expect(sessions).toHaveLength(1) + }) + + it('allows the "lambdaTimeoutNotifyMs" to be changed', async () => { + // With 6 seconds remaining and a resolve timeout of 500ms, the timeout + // warning will never be triggered unless the custom "lambdaTimeoutNotifyMs" + // takes effect + const superLongWaitMs = 6000 + const resolveTimeoutMs = 500 + const lambdaTimeoutNotifyMs = superLongWaitMs - (resolveTimeoutMs / 2) + + getRemainingTimeInMillis = jest.fn() + .mockReturnValueOnce(superLongWaitMs) + .mockReturnValueOnce(superLongWaitMs - lambdaTimeoutNotifyMs) + .mockImplementationOnce(() => { throw new Error('unexpected call to "getRemainingTimeInMillis"') }) + + const events: EventDeliveryPayload[] = [] + const sessions: SessionDeliveryPayload[] = [] + + const client = createClient(events, sessions) + + const handler = async (event: any, context: any) => new Promise(resolve => { + setTimeout(() => resolve('xyz'), resolveTimeoutMs) + }) + + const event = { very: 'eventy' } + const context = { extremely: 'contextual', getRemainingTimeInMillis } + + const plugin = client.getPlugin('awsLambda') + + if (!plugin) { + throw new Error('Plugin was not loaded!') + } + + const bugsnagHandler = plugin.createHandler({ lambdaTimeoutNotifyMs }) + const wrappedHandler = bugsnagHandler(handler) + + expect(events).toHaveLength(0) + expect(sessions).toHaveLength(0) + + expect(await wrappedHandler(event, context)).toBe('xyz') + + expect(events).toHaveLength(1) + expect(events[0].events).toHaveLength(1) + expect(events[0].events[0].errors).toHaveLength(1) + expect(events[0].events[0].context).toBe('Lambda timeout approaching') + + const expectedError = { + errorClass: 'LambdaTimeoutApproaching', + errorMessage: `Lambda will timeout in ${resolveTimeoutMs / 2}ms`, + stacktrace: [], + type: 'nodejs' + } + + expect(events[0].events[0].errors[0]).toEqual(expectedError) + + expect(sessions).toHaveLength(1) + }) + + it('does not notify if "lambdaTimeoutNotifyMs" is 0', async () => { + const events: EventDeliveryPayload[] = [] + const sessions: SessionDeliveryPayload[] = [] + + const client = createClient(events, sessions) + + const handler = async (event: any, context: any) => new Promise(resolve => { + setTimeout(() => resolve('xyz'), 100) + }) + + const event = { very: 'eventy' } + const context = { extremely: 'contextual', getRemainingTimeInMillis } + + const plugin = client.getPlugin('awsLambda') + + if (!plugin) { + throw new Error('Plugin was not loaded!') + } + + const bugsnagHandler = plugin.createHandler({ lambdaTimeoutNotifyMs: 0 }) + const wrappedHandler = bugsnagHandler(handler) + + expect(events).toHaveLength(0) + expect(sessions).toHaveLength(0) + + expect(await wrappedHandler(event, context)).toBe('xyz') + + expect(events).toHaveLength(0) + expect(sessions).toHaveLength(1) + }) }) diff --git a/packages/plugin-aws-lambda/types/bugsnag-plugin-aws-lambda.d.ts b/packages/plugin-aws-lambda/types/bugsnag-plugin-aws-lambda.d.ts index 231a06cc6..5b0b0e1a3 100644 --- a/packages/plugin-aws-lambda/types/bugsnag-plugin-aws-lambda.d.ts +++ b/packages/plugin-aws-lambda/types/bugsnag-plugin-aws-lambda.d.ts @@ -10,6 +10,7 @@ export type BugsnagPluginAwsLambdaHandler = (handler: AsyncHandler|CallbackHandl export interface BugsnagPluginAwsLambdaConfiguration { flushTimeoutMs?: number + lambdaTimeoutNotifyMs?: number } export interface BugsnagPluginAwsLambdaResult { From 67031aeb9cf7b3a4dad4d0467c58b3d215b3e298 Mon Sep 17 00:00:00 2001 From: Joe Haines Date: Thu, 4 Mar 2021 10:44:46 +0000 Subject: [PATCH 48/65] Simplify the MAX_TIMER_VALUE logic There's no point adding the timeout at all if it's too big as it will never trigger --- packages/plugin-aws-lambda/src/index.js | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/packages/plugin-aws-lambda/src/index.js b/packages/plugin-aws-lambda/src/index.js index 67c0e379c..fbc818432 100644 --- a/packages/plugin-aws-lambda/src/index.js +++ b/packages/plugin-aws-lambda/src/index.js @@ -60,16 +60,10 @@ function wrapHandler (client, flushTimeoutMs, lambdaTimeoutNotifyMs, handler) { // Guard against the "getRemainingTimeInMillis" being missing. This should // never happen but could when unit testing if (typeof context.getRemainingTimeInMillis === 'function' && - lambdaTimeoutNotifyMs > 0 + lambdaTimeoutNotifyMs > 0 && + lambdaTimeoutNotifyMs <= MAX_TIMER_VALUE ) { - // Clamp the timeout value between 0 and MAX_TIMER_VALUE - const timeoutMs = Math.min( - Math.max( - context.getRemainingTimeInMillis() - lambdaTimeoutNotifyMs, - 0 - ), - MAX_TIMER_VALUE - ) + const timeoutMs = context.getRemainingTimeInMillis() - lambdaTimeoutNotifyMs lambdaTimeout = setTimeout(function () { const handledState = { From a5e103aa4f9f80c15ebd3312541f2aae2dc7d707 Mon Sep 17 00:00:00 2001 From: Joe Haines Date: Thu, 4 Mar 2021 12:04:07 +0000 Subject: [PATCH 49/65] Prevent warnings when run with AWS SAM --- packages/plugin-aws-lambda/src/index.js | 43 +++++++++++++------------ 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/packages/plugin-aws-lambda/src/index.js b/packages/plugin-aws-lambda/src/index.js index fbc818432..c79296e9d 100644 --- a/packages/plugin-aws-lambda/src/index.js +++ b/packages/plugin-aws-lambda/src/index.js @@ -60,30 +60,31 @@ function wrapHandler (client, flushTimeoutMs, lambdaTimeoutNotifyMs, handler) { // Guard against the "getRemainingTimeInMillis" being missing. This should // never happen but could when unit testing if (typeof context.getRemainingTimeInMillis === 'function' && - lambdaTimeoutNotifyMs > 0 && - lambdaTimeoutNotifyMs <= MAX_TIMER_VALUE + lambdaTimeoutNotifyMs > 0 ) { const timeoutMs = context.getRemainingTimeInMillis() - lambdaTimeoutNotifyMs - lambdaTimeout = setTimeout(function () { - const handledState = { - severity: 'warning', - unhandled: true, - severityReason: { type: 'log' } - } - - const event = client.Event.create( - new LambdaTimeoutApproaching(context.getRemainingTimeInMillis()), - true, - handledState, - 'aws lambda plugin', - 0 - ) - - event.context = context.functionName || 'Lambda timeout approaching' - - client._notify(event) - }, timeoutMs) + if (timeoutMs <= MAX_TIMER_VALUE) { + lambdaTimeout = setTimeout(function () { + const handledState = { + severity: 'warning', + unhandled: true, + severityReason: { type: 'log' } + } + + const event = client.Event.create( + new LambdaTimeoutApproaching(context.getRemainingTimeInMillis()), + true, + handledState, + 'aws lambda plugin', + 0 + ) + + event.context = context.functionName || 'Lambda timeout approaching' + + client._notify(event) + }, timeoutMs) + } } client.addMetadata('AWS Lambda context', context) From 10b75b42f617b2c394d5201fce847c0150f0c443 Mon Sep 17 00:00:00 2001 From: Steve Kirkland Date: Thu, 4 Mar 2021 13:14:38 +0000 Subject: [PATCH 50/65] Remove setting of bind-address --- .buildkite/browser-pipeline.yml | 17 ----------------- .buildkite/node-pipeline.yml | 8 ++------ 2 files changed, 2 insertions(+), 23 deletions(-) diff --git a/.buildkite/browser-pipeline.yml b/.buildkite/browser-pipeline.yml index 209cb97d8..9a03fd845 100644 --- a/.buildkite/browser-pipeline.yml +++ b/.buildkite/browser-pipeline.yml @@ -28,7 +28,6 @@ steps: command: - --farm=bs - --browser=chrome_43 - - --bind-address=0.0.0.0 concurrency: 5 concurrency_group: 'browserstack' @@ -43,7 +42,6 @@ steps: command: - --farm=bs - --browser=chrome_61 - - --bind-address=0.0.0.0 concurrency: 5 concurrency_group: 'browserstack' @@ -58,7 +56,6 @@ steps: command: - --farm=bs - --browser=chrome_latest - - --bind-address=0.0.0.0 concurrency: 5 concurrency_group: 'browserstack' @@ -73,7 +70,6 @@ steps: command: - --farm=bs - --browser=ie_8 - - --bind-address=0.0.0.0 concurrency: 5 concurrency_group: 'browserstack' @@ -88,7 +84,6 @@ steps: command: - --farm=bs - --browser=ie_9 - - --bind-address=0.0.0.0 concurrency: 5 concurrency_group: 'browserstack' @@ -103,7 +98,6 @@ steps: command: - --farm=bs - --browser=ie_10 - - --bind-address=0.0.0.0 concurrency: 5 concurrency_group: 'browserstack' @@ -118,7 +112,6 @@ steps: command: - --farm=bs - --browser=ie_11 - - --bind-address=0.0.0.0 env: HOST: 'localhost' # IE11 needs the host set to localhost for some reason!ß concurrency: 5 @@ -135,7 +128,6 @@ steps: command: - --farm=bs - --browser=edge_14 - - --bind-address=0.0.0.0 concurrency: 5 concurrency_group: 'browserstack' @@ -150,7 +142,6 @@ steps: command: - --farm=bs - --browser=edge_15 - - --bind-address=0.0.0.0 concurrency: 5 concurrency_group: 'browserstack' @@ -165,7 +156,6 @@ steps: command: - --farm=bs - --browser=safari_6 - - --bind-address=0.0.0.0 concurrency: 5 concurrency_group: 'browserstack' @@ -180,7 +170,6 @@ steps: command: - --farm=bs - --browser=safari_10 - - --bind-address=0.0.0.0 concurrency: 5 concurrency_group: 'browserstack' @@ -195,7 +184,6 @@ steps: command: - --farm=bs - --browser=safari_13 - - --bind-address=0.0.0.0 concurrency: 5 concurrency_group: 'browserstack' @@ -210,7 +198,6 @@ steps: command: - --farm=bs - --browser=iphone_7 - - --bind-address=0.0.0.0 env: HOST: "bs-local.com" concurrency: 5 @@ -227,7 +214,6 @@ steps: command: - --farm=bs - --browser=android_s8 - - --bind-address=0.0.0.0 concurrency: 5 concurrency_group: 'browserstack' @@ -242,7 +228,6 @@ steps: command: - --farm=bs - --browser=firefox_30 - - --bind-address=0.0.0.0 concurrency: 5 concurrency_group: 'browserstack' @@ -257,7 +242,6 @@ steps: command: - --farm=bs - --browser=firefox_56 - - --bind-address=0.0.0.0 concurrency: 5 concurrency_group: 'browserstack' @@ -272,6 +256,5 @@ steps: command: - --farm=bs - --browser=firefox_latest - - --bind-address=0.0.0.0 concurrency: 5 concurrency_group: 'browserstack' diff --git a/.buildkite/node-pipeline.yml b/.buildkite/node-pipeline.yml index 9e9e24db3..38107d7a8 100644 --- a/.buildkite/node-pipeline.yml +++ b/.buildkite/node-pipeline.yml @@ -25,7 +25,7 @@ steps: run: node-maze-runner use-aliases: true verbose: true - command: ["--bind-address=0.0.0.0", "-e", "koa.feature", "-e", "koa-1x.feature", "-e", "webpack.feature"] + command: ["-e", "koa.feature", "-e", "koa-1x.feature", "-e", "webpack.feature"] env: NODE_VERSION: "4" @@ -37,7 +37,7 @@ steps: run: node-maze-runner use-aliases: true verbose: true - command: ["--bind-address=0.0.0.0", "-e", "koa.feature", "-e", "koa-1x.feature"] + command: ["-e", "koa.feature", "-e", "koa-1x.feature"] env: NODE_VERSION: "6" @@ -49,7 +49,6 @@ steps: run: node-maze-runner use-aliases: true verbose: true - command: ["--bind-address=0.0.0.0"] env: NODE_VERSION: "8" @@ -61,7 +60,6 @@ steps: run: node-maze-runner use-aliases: true verbose: true - command: ["--bind-address=0.0.0.0"] env: NODE_VERSION: "10" @@ -73,7 +71,6 @@ steps: run: node-maze-runner use-aliases: true verbose: true - command: ["--bind-address=0.0.0.0"] env: NODE_VERSION: "12" @@ -85,6 +82,5 @@ steps: run: node-maze-runner use-aliases: true verbose: true - command: ["--bind-address=0.0.0.0"] env: NODE_VERSION: "14" From d86cbf90e622be9ce56298a5955dbea39b0b1577 Mon Sep 17 00:00:00 2001 From: Joe Haines Date: Thu, 4 Mar 2021 14:59:40 +0000 Subject: [PATCH 51/65] Remove unnecessary this.message assignment This is already handled by the 'super' call --- packages/plugin-aws-lambda/src/lambda-timeout-approaching.js | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/plugin-aws-lambda/src/lambda-timeout-approaching.js b/packages/plugin-aws-lambda/src/lambda-timeout-approaching.js index 7b8198062..ad48611bc 100644 --- a/packages/plugin-aws-lambda/src/lambda-timeout-approaching.js +++ b/packages/plugin-aws-lambda/src/lambda-timeout-approaching.js @@ -5,7 +5,6 @@ module.exports = class LambdaTimeoutApproaching extends Error { super(message) this.name = 'LambdaTimeoutApproaching' - this.message = message this.stack = [] } } From d1e34a0563898f4346e26dffc7e97083b4f02cdb Mon Sep 17 00:00:00 2001 From: Dan Skinner Date: Mon, 8 Mar 2021 09:50:07 +0000 Subject: [PATCH 52/65] test(plugin-koa): add request.httpVersion assertion --- test/node/features/koa.feature | 1 + 1 file changed, 1 insertion(+) diff --git a/test/node/features/koa.feature b/test/node/features/koa.feature index ddbb689ac..f6ed0188c 100644 --- a/test/node/features/koa.feature +++ b/test/node/features/koa.feature @@ -103,3 +103,4 @@ Scenario: adding body to request metadata And the "file" of stack frame 0 equals "scenarios/app.js" And the event "request.body.data" equals "in_request_body" And the event "request.httpMethod" equals "POST" + And the event "request.httpVersion" equals "1.1" From 6d533f5537be1fdf15432e41b52a6a5f2ecb9fdd Mon Sep 17 00:00:00 2001 From: Joe Haines Date: Mon, 8 Mar 2021 10:21:50 +0000 Subject: [PATCH 53/65] Pin RN Navigation dependency versions --- .../fixtures/app/react_navigation_js/install.sh | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/test/react-native/features/fixtures/app/react_navigation_js/install.sh b/test/react-native/features/fixtures/app/react_navigation_js/install.sh index 7ab8cfe66..62c82aa3e 100755 --- a/test/react-native/features/fixtures/app/react_navigation_js/install.sh +++ b/test/react-native/features/fixtures/app/react_navigation_js/install.sh @@ -1,9 +1,9 @@ npm i @bugsnag/plugin-react-navigation@$BUGSNAG_VERSION --registry=$REGISTRY_URL -npm i @react-native-community/masked-view --registry=$REGISTRY_URL -npm i @react-navigation/native --registry=$REGISTRY_URL -npm i @react-navigation/stack --registry=$REGISTRY_URL -npm i react-native-gesture-handler --registry=$REGISTRY_URL -npm i react-native-reanimated --registry=$REGISTRY_URL -npm i react-native-safe-area-context --registry=$REGISTRY_URL -npm i react-native-screens --registry=$REGISTRY_URL \ No newline at end of file +npm i @react-native-community/masked-view@^0.1 --registry=$REGISTRY_URL +npm i @react-navigation/native@^5.9 --registry=$REGISTRY_URL +npm i @react-navigation/stack@^5.14 --registry=$REGISTRY_URL +npm i react-native-gesture-handler@^1.10 --registry=$REGISTRY_URL +npm i react-native-reanimated@^1.13 --registry=$REGISTRY_URL +npm i react-native-safe-area-context@^3.1 --registry=$REGISTRY_URL +npm i react-native-screens@^2.18 --registry=$REGISTRY_URL From 441a3617f8ac543b647360cf6a82b797a46b8b2a Mon Sep 17 00:00:00 2001 From: Joe Haines Date: Thu, 4 Mar 2021 14:22:42 +0000 Subject: [PATCH 54/65] Avoid starting a session if one already exists This change prevents multiple sessions from being started if separate plugins both call 'startSession' by making 'resumeSession' do nothing if a session already exists --- packages/plugin-browser-session/session.js | 12 ++++++++++-- packages/plugin-express/src/express.js | 4 ++-- packages/plugin-koa/src/koa.js | 8 ++++---- packages/plugin-koa/test/koa.test.ts | 4 ++-- packages/plugin-restify/src/restify.js | 4 ++-- packages/plugin-server-session/session.js | 12 ++++++++++-- 6 files changed, 30 insertions(+), 14 deletions(-) diff --git a/packages/plugin-browser-session/session.js b/packages/plugin-browser-session/session.js index 0313debe4..fdee3c102 100644 --- a/packages/plugin-browser-session/session.js +++ b/packages/plugin-browser-session/session.js @@ -31,13 +31,21 @@ const sessionDelegate = { return sessionClient }, resumeSession: (client) => { + // Do nothing if there's already an active session + if (client._session) { + return client + } + + // If we have a paused session then make it the active session if (client._pausedSession) { client._session = client._pausedSession client._pausedSession = null + return client - } else { - return client.startSession() } + + // Otherwise start a new session + return client.startSession() }, pauseSession: (client) => { client._pausedSession = client._session diff --git a/packages/plugin-express/src/express.js b/packages/plugin-express/src/express.js index 80c486457..c2f72b7a1 100644 --- a/packages/plugin-express/src/express.js +++ b/packages/plugin-express/src/express.js @@ -18,8 +18,8 @@ module.exports = { const dom = domain.create() // Get a client to be scoped to this request. If sessions are enabled, use the - // startSession() call to get a session client, otherwise, clone the existing client. - const requestClient = client._config.autoTrackSessions ? client.startSession() : clone(client) + // resumeSession() call to get a session client, otherwise, clone the existing client. + const requestClient = client._config.autoTrackSessions ? client.resumeSession() : clone(client) // attach it to the request req.bugsnag = requestClient diff --git a/packages/plugin-koa/src/koa.js b/packages/plugin-koa/src/koa.js index 4c9daf4f8..a696df70d 100644 --- a/packages/plugin-koa/src/koa.js +++ b/packages/plugin-koa/src/koa.js @@ -15,8 +15,8 @@ module.exports = { load: client => { const requestHandler = async (ctx, next) => { // Get a client to be scoped to this request. If sessions are enabled, use the - // startSession() call to get a session client, otherwise, clone the existing client. - const requestClient = client._config.autoTrackSessions ? client.startSession() : clone(client) + // resumeSession() call to get a session client, otherwise, clone the existing client. + const requestClient = client._config.autoTrackSessions ? client.resumeSession() : clone(client) ctx.bugsnag = requestClient @@ -47,8 +47,8 @@ module.exports = { requestHandler.v1 = function * (next) { // Get a client to be scoped to this request. If sessions are enabled, use the - // startSession() call to get a session client, otherwise, clone the existing client. - const requestClient = client._config.autoTrackSessions ? client.startSession() : clone(client) + // resumeSession() call to get a session client, otherwise, clone the existing client. + const requestClient = client._config.autoTrackSessions ? client.resumeSession() : clone(client) this.bugsnag = requestClient diff --git a/packages/plugin-koa/test/koa.test.ts b/packages/plugin-koa/test/koa.test.ts index 8320e3d16..ad46abc04 100644 --- a/packages/plugin-koa/test/koa.test.ts +++ b/packages/plugin-koa/test/koa.test.ts @@ -7,7 +7,7 @@ describe('plugin: koa', () => { c._sessionDelegate = { startSession: () => c, pauseSession: () => {}, - resumeSession: () => {} + resumeSession: () => c } // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const middleware = c.getPlugin('koa')! @@ -23,7 +23,7 @@ describe('plugin: koa', () => { c._sessionDelegate = { startSession: () => c, pauseSession: () => {}, - resumeSession: () => {} + resumeSession: () => c } // eslint-disable-next-line @typescript-eslint/no-non-null-assertion diff --git a/packages/plugin-restify/src/restify.js b/packages/plugin-restify/src/restify.js index 58f61c308..65bd4461b 100644 --- a/packages/plugin-restify/src/restify.js +++ b/packages/plugin-restify/src/restify.js @@ -17,8 +17,8 @@ module.exports = { const dom = domain.create() // Get a client to be scoped to this request. If sessions are enabled, use the - // startSession() call to get a session client, otherwise, clone the existing client. - const requestClient = client._config.autoTrackSessions ? client.startSession() : clone(client) + // resumeSession() call to get a session client, otherwise, clone the existing client. + const requestClient = client._config.autoTrackSessions ? client.resumeSession() : clone(client) // attach it to the request req.bugsnag = requestClient diff --git a/packages/plugin-server-session/session.js b/packages/plugin-server-session/session.js index 491548a11..75234833e 100644 --- a/packages/plugin-server-session/session.js +++ b/packages/plugin-server-session/session.js @@ -22,13 +22,21 @@ module.exports = { client._session = null }, resumeSession: (client) => { + // Do nothing if there's already an active session + if (client._session) { + return client + } + + // If we have a paused session then make it the active session if (client._pausedSession) { client._session = client._pausedSession client._pausedSession = null + return client - } else { - return client.startSession() } + + // Otherwise start a new session + return client.startSession() } } }, From 582d0fbcfd1598e7d83a1148ed592b7ed214d795 Mon Sep 17 00:00:00 2001 From: Joe Haines Date: Thu, 4 Mar 2021 09:26:39 +0000 Subject: [PATCH 55/65] Add MazeRunner tests for serverless-express --- test/aws-lambda/Gemfile | 2 +- test/aws-lambda/Gemfile.lock | 6 +- test/aws-lambda/README.md | 8 +++ .../serverless-express-app/app/app.js | 52 ++++++++++++++++ .../serverless-express-app/app/index.js | 22 +++++++ .../serverless-express-app/app/package.json | 26 ++++++++ .../events/handled.json | 51 ++++++++++++++++ .../serverless-express-app/events/index.json | 51 ++++++++++++++++ .../events/promise-rejection.json | 51 ++++++++++++++++ .../events/unhandled-async.json | 51 ++++++++++++++++ .../events/unhandled-next.json | 51 ++++++++++++++++ .../events/unhandled.json | 51 ++++++++++++++++ .../serverless-express-app/template.yaml | 41 +++++++++++++ test/aws-lambda/features/handled.feature | 28 +++++++++ .../features/promise-rejection.feature | 32 ++++++++++ .../features/scripts/build-fixtures | 13 ++-- test/aws-lambda/features/sessions.feature | 2 + .../features/steps/aws-lambda-steps.rb | 12 +--- test/aws-lambda/features/unhandled.feature | 61 +++++++++++++++++++ 19 files changed, 592 insertions(+), 19 deletions(-) create mode 100644 test/aws-lambda/features/fixtures/serverless-express-app/app/app.js create mode 100644 test/aws-lambda/features/fixtures/serverless-express-app/app/index.js create mode 100644 test/aws-lambda/features/fixtures/serverless-express-app/app/package.json create mode 100644 test/aws-lambda/features/fixtures/serverless-express-app/events/handled.json create mode 100644 test/aws-lambda/features/fixtures/serverless-express-app/events/index.json create mode 100644 test/aws-lambda/features/fixtures/serverless-express-app/events/promise-rejection.json create mode 100644 test/aws-lambda/features/fixtures/serverless-express-app/events/unhandled-async.json create mode 100644 test/aws-lambda/features/fixtures/serverless-express-app/events/unhandled-next.json create mode 100644 test/aws-lambda/features/fixtures/serverless-express-app/events/unhandled.json create mode 100644 test/aws-lambda/features/fixtures/serverless-express-app/template.yaml diff --git a/test/aws-lambda/Gemfile b/test/aws-lambda/Gemfile index 4a6af8dc4..77dcf3d46 100644 --- a/test/aws-lambda/Gemfile +++ b/test/aws-lambda/Gemfile @@ -1,6 +1,6 @@ source 'https://rubygems.org' -gem 'bugsnag-maze-runner', git: 'https://github.com/bugsnag/maze-runner', tag: 'v4.11.1' +gem 'bugsnag-maze-runner', git: 'https://github.com/bugsnag/maze-runner', tag: 'v4.12.1' # Locally, you can run against Maze Runner branches and uncommitted changes: #gem 'bugsnag-maze-runner', path: '../../../maze-runner' diff --git a/test/aws-lambda/Gemfile.lock b/test/aws-lambda/Gemfile.lock index 12b24dea3..d290c374e 100644 --- a/test/aws-lambda/Gemfile.lock +++ b/test/aws-lambda/Gemfile.lock @@ -1,9 +1,9 @@ GIT remote: https://github.com/bugsnag/maze-runner - revision: b5925737b0c3db49ac38a5a68e6bfe38026ef508 - tag: v4.11.1 + revision: b9397574529d141ba07a8050cf7deb2213fda3b8 + tag: v4.12.1 specs: - bugsnag-maze-runner (4.11.1) + bugsnag-maze-runner (4.12.0) appium_lib (~> 11.2.0) boring (~> 0.1.0) cucumber (~> 3.1.2) diff --git a/test/aws-lambda/README.md b/test/aws-lambda/README.md index 56f69fa52..893710b16 100644 --- a/test/aws-lambda/README.md +++ b/test/aws-lambda/README.md @@ -17,3 +17,11 @@ $ bundle exec maze-runner --bind-address=0.0.0.0 ``` This will build all of the fixtures before running the tests + +### Running tests for a specific fixture + +All tests are tagged with the name of the fixture they run against. For example, to run only the tests that use the "[serverless-express-app](./features/fixtures/serverless-express-app)" fixture: + +```sh +$ bundle exec maze-runner --bind-address=0.0.0.0 --tags @serverless-express-app +``` diff --git a/test/aws-lambda/features/fixtures/serverless-express-app/app/app.js b/test/aws-lambda/features/fixtures/serverless-express-app/app/app.js new file mode 100644 index 000000000..939dc28bb --- /dev/null +++ b/test/aws-lambda/features/fixtures/serverless-express-app/app/app.js @@ -0,0 +1,52 @@ +const Bugsnag = require('@bugsnag/js') +const express = require('express') + +const app = express() + +const middleware = Bugsnag.getPlugin('express') +app.use(middleware.requestHandler) + +app.get('/', (request, response) => { + response.json({ message: 'hello world!' }) +}) + +app.get('/handled', (request, response) => { + Bugsnag.notify('no crashing here') + + response.json({ message: 'did not crash :)' }) +}) + +app.get('/unhandled', (request, response) => { + throw new Error('broken :(') +}) + +app.get('/unhandled-async', (request, response) => { + setTimeout(function () { + throw new Error('busted') + }, 100) +}) + +app.get('/unhandled-next', (request, response, next) => { + next(new Error('borked')) +}) + +app.get('/promise-rejection', (request, response) => { + Promise.reject(new Error('abc')) + + response.json({ message: 'xyz' }) +}) + +app.use(middleware.errorHandler) + +// Replace the built-in error handler with one that returns JSON. This allows us +// to assert against it much more easily than the default HTML response +app.use((error, request, response, next) => { + response.status(500) + response.json({ + message: error.message, + type: error.constructor.name, + stacktrace: error.stack.split('\n') + }) +}) + +module.exports = app diff --git a/test/aws-lambda/features/fixtures/serverless-express-app/app/index.js b/test/aws-lambda/features/fixtures/serverless-express-app/app/index.js new file mode 100644 index 000000000..20f9c3948 --- /dev/null +++ b/test/aws-lambda/features/fixtures/serverless-express-app/app/index.js @@ -0,0 +1,22 @@ +const Bugsnag = require('@bugsnag/js') +const BugsnagPluginAwsLambda = require('@bugsnag/plugin-aws-lambda') +const BugsnagPluginExpress = require('@bugsnag/plugin-express') +const serverlessExpress = require('@vendia/serverless-express') + +Bugsnag.start({ + apiKey: process.env.BUGSNAG_API_KEY, + plugins: [BugsnagPluginAwsLambda, BugsnagPluginExpress], + endpoints: { + notify: process.env.BUGSNAG_NOTIFY_ENDPOINT, + sessions: process.env.BUGSNAG_SESSIONS_ENDPOINT + }, + autoDetectErrors: process.env.BUGSNAG_AUTO_DETECT_ERRORS !== 'false', + autoTrackSessions: process.env.BUGSNAG_AUTO_TRACK_SESSIONS !== 'false' +}) + +const app = require('./app') + +const bugsnagHandler = Bugsnag.getPlugin('awsLambda').createHandler() +const serverlessExpressHandler = serverlessExpress({ app }) + +exports.lambdaHandler = bugsnagHandler(serverlessExpressHandler) diff --git a/test/aws-lambda/features/fixtures/serverless-express-app/app/package.json b/test/aws-lambda/features/fixtures/serverless-express-app/app/package.json new file mode 100644 index 000000000..77b667114 --- /dev/null +++ b/test/aws-lambda/features/fixtures/serverless-express-app/app/package.json @@ -0,0 +1,26 @@ +{ + "name": "serverless-express-app", + "version": "1.2.3", + "dependencies": { + "@vendia/serverless-express": "^4.3.3", + "express": "^4.17.1", + + "@bugsnag/core": "bugsnag-core.tgz", + "@bugsnag/delivery-node": "bugsnag-delivery-node.tgz", + "@bugsnag/in-flight": "bugsnag-in-flight.tgz", + "@bugsnag/js": "bugsnag-js.tgz", + "@bugsnag/node": "bugsnag-node.tgz", + "@bugsnag/plugin-app-duration": "bugsnag-plugin-app-duration.tgz", + "@bugsnag/plugin-aws-lambda": "bugsnag-plugin-aws-lambda.tgz", + "@bugsnag/plugin-contextualize": "bugsnag-plugin-contextualize.tgz", + "@bugsnag/plugin-express": "bugsnag-plugin-express.tgz", + "@bugsnag/plugin-intercept": "bugsnag-plugin-intercept.tgz", + "@bugsnag/plugin-node-device": "bugsnag-plugin-node-device.tgz", + "@bugsnag/plugin-node-in-project": "bugsnag-plugin-node-in-project.tgz", + "@bugsnag/plugin-node-surrounding-code": "bugsnag-plugin-node-surrounding-code.tgz", + "@bugsnag/plugin-node-uncaught-exception": "bugsnag-plugin-node-uncaught-exception.tgz", + "@bugsnag/plugin-node-unhandled-rejection": "bugsnag-plugin-node-unhandled-rejection.tgz", + "@bugsnag/plugin-server-session": "bugsnag-plugin-server-session.tgz", + "@bugsnag/plugin-strip-project-root": "bugsnag-plugin-strip-project-root.tgz" + } +} diff --git a/test/aws-lambda/features/fixtures/serverless-express-app/events/handled.json b/test/aws-lambda/features/fixtures/serverless-express-app/events/handled.json new file mode 100644 index 000000000..8dd1a0b23 --- /dev/null +++ b/test/aws-lambda/features/fixtures/serverless-express-app/events/handled.json @@ -0,0 +1,51 @@ +{ + "resource": "/handled", + "path": "/handled", + "httpMethod": "GET", + "headers": { + "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", + "Accept-Encoding": "gzip, deflate, sdch", + "Accept-Language": "en-US,en;q=0.8", + "Cache-Control": "max-age=0", + "CloudFront-Forwarded-Proto": "https", + "CloudFront-Is-Desktop-Viewer": "true", + "CloudFront-Is-Mobile-Viewer": "false", + "CloudFront-Is-SmartTV-Viewer": "false", + "CloudFront-Is-Tablet-Viewer": "false", + "CloudFront-Viewer-Country": "US", + "Host": "1234567890.execute-api.us-east-1.amazonaws.com", + "Upgrade-Insecure-Requests": "1", + "User-Agent": "Custom User Agent String", + "Via": "1.1 08f323deadbeefa7af34d5feb414ce27.cloudfront.net (CloudFront)", + "X-Amz-Cf-Id": "cDehVQoZnx43VYQb9j2-nvCh-9z396Uhbp027Y2JvkCPNLmGJHqlaA==", + "X-Forwarded-For": "127.0.0.1, 127.0.0.2", + "X-Forwarded-Port": "443", + "X-Forwarded-Proto": "https" + }, + "requestContext": { + "accountId": "123456789012", + "resourceId": "123456", + "stage": "prod", + "requestId": "c6af9ac6-7b61-11e6-9a41-93e8deadbeef", + "requestTime": "09/Apr/2015:12:34:56 +0000", + "requestTimeEpoch": 1428582896000, + "identity": { + "cognitoIdentityPoolId": null, + "accountId": null, + "cognitoIdentityId": null, + "caller": null, + "accessKey": null, + "sourceIp": "127.0.0.1", + "cognitoAuthenticationType": null, + "cognitoAuthenticationProvider": null, + "userArn": null, + "userAgent": "Custom User Agent String", + "user": null + }, + "path": "/prod/handled", + "resourcePath": "/handled", + "httpMethod": "GET", + "apiId": "1234567890", + "protocol": "HTTP/1.1" + } +} diff --git a/test/aws-lambda/features/fixtures/serverless-express-app/events/index.json b/test/aws-lambda/features/fixtures/serverless-express-app/events/index.json new file mode 100644 index 000000000..c5c1ce903 --- /dev/null +++ b/test/aws-lambda/features/fixtures/serverless-express-app/events/index.json @@ -0,0 +1,51 @@ +{ + "resource": "/", + "path": "/", + "httpMethod": "GET", + "headers": { + "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", + "Accept-Encoding": "gzip, deflate, sdch", + "Accept-Language": "en-US,en;q=0.8", + "Cache-Control": "max-age=0", + "CloudFront-Forwarded-Proto": "https", + "CloudFront-Is-Desktop-Viewer": "true", + "CloudFront-Is-Mobile-Viewer": "false", + "CloudFront-Is-SmartTV-Viewer": "false", + "CloudFront-Is-Tablet-Viewer": "false", + "CloudFront-Viewer-Country": "US", + "Host": "1234567890.execute-api.us-east-1.amazonaws.com", + "Upgrade-Insecure-Requests": "1", + "User-Agent": "Custom User Agent String", + "Via": "1.1 08f323deadbeefa7af34d5feb414ce27.cloudfront.net (CloudFront)", + "X-Amz-Cf-Id": "cDehVQoZnx43VYQb9j2-nvCh-9z396Uhbp027Y2JvkCPNLmGJHqlaA==", + "X-Forwarded-For": "127.0.0.1, 127.0.0.2", + "X-Forwarded-Port": "443", + "X-Forwarded-Proto": "https" + }, + "requestContext": { + "accountId": "123456789012", + "resourceId": "123456", + "stage": "prod", + "requestId": "c6af9ac6-7b61-11e6-9a41-93e8deadbeef", + "requestTime": "09/Apr/2015:12:34:56 +0000", + "requestTimeEpoch": 1428582896000, + "identity": { + "cognitoIdentityPoolId": null, + "accountId": null, + "cognitoIdentityId": null, + "caller": null, + "accessKey": null, + "sourceIp": "127.0.0.1", + "cognitoAuthenticationType": null, + "cognitoAuthenticationProvider": null, + "userArn": null, + "userAgent": "Custom User Agent String", + "user": null + }, + "path": "/prod/", + "resourcePath": "/", + "httpMethod": "GET", + "apiId": "1234567890", + "protocol": "HTTP/1.1" + } +} diff --git a/test/aws-lambda/features/fixtures/serverless-express-app/events/promise-rejection.json b/test/aws-lambda/features/fixtures/serverless-express-app/events/promise-rejection.json new file mode 100644 index 000000000..9ed94bcc0 --- /dev/null +++ b/test/aws-lambda/features/fixtures/serverless-express-app/events/promise-rejection.json @@ -0,0 +1,51 @@ +{ + "resource": "/promise-rejection", + "path": "/promise-rejection", + "httpMethod": "GET", + "headers": { + "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", + "Accept-Encoding": "gzip, deflate, sdch", + "Accept-Language": "en-US,en;q=0.8", + "Cache-Control": "max-age=0", + "CloudFront-Forwarded-Proto": "https", + "CloudFront-Is-Desktop-Viewer": "true", + "CloudFront-Is-Mobile-Viewer": "false", + "CloudFront-Is-SmartTV-Viewer": "false", + "CloudFront-Is-Tablet-Viewer": "false", + "CloudFront-Viewer-Country": "US", + "Host": "1234567890.execute-api.us-east-1.amazonaws.com", + "Upgrade-Insecure-Requests": "1", + "User-Agent": "Custom User Agent String", + "Via": "1.1 08f323deadbeefa7af34d5feb414ce27.cloudfront.net (CloudFront)", + "X-Amz-Cf-Id": "cDehVQoZnx43VYQb9j2-nvCh-9z396Uhbp027Y2JvkCPNLmGJHqlaA==", + "X-Forwarded-For": "127.0.0.1, 127.0.0.2", + "X-Forwarded-Port": "443", + "X-Forwarded-Proto": "https" + }, + "requestContext": { + "accountId": "123456789012", + "resourceId": "123456", + "stage": "prod", + "requestId": "c6af9ac6-7b61-11e6-9a41-93e8deadbeef", + "requestTime": "09/Apr/2015:12:34:56 +0000", + "requestTimeEpoch": 1428582896000, + "identity": { + "cognitoIdentityPoolId": null, + "accountId": null, + "cognitoIdentityId": null, + "caller": null, + "accessKey": null, + "sourceIp": "127.0.0.1", + "cognitoAuthenticationType": null, + "cognitoAuthenticationProvider": null, + "userArn": null, + "userAgent": "Custom User Agent String", + "user": null + }, + "path": "/prod/promise-rejection", + "resourcePath": "/promise-rejection", + "httpMethod": "GET", + "apiId": "1234567890", + "protocol": "HTTP/1.1" + } +} diff --git a/test/aws-lambda/features/fixtures/serverless-express-app/events/unhandled-async.json b/test/aws-lambda/features/fixtures/serverless-express-app/events/unhandled-async.json new file mode 100644 index 000000000..9486bd80b --- /dev/null +++ b/test/aws-lambda/features/fixtures/serverless-express-app/events/unhandled-async.json @@ -0,0 +1,51 @@ +{ + "resource": "/unhandled-async", + "path": "/unhandled-async", + "httpMethod": "GET", + "headers": { + "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", + "Accept-Encoding": "gzip, deflate, sdch", + "Accept-Language": "en-US,en;q=0.8", + "Cache-Control": "max-age=0", + "CloudFront-Forwarded-Proto": "https", + "CloudFront-Is-Desktop-Viewer": "true", + "CloudFront-Is-Mobile-Viewer": "false", + "CloudFront-Is-SmartTV-Viewer": "false", + "CloudFront-Is-Tablet-Viewer": "false", + "CloudFront-Viewer-Country": "US", + "Host": "1234567890.execute-api.us-east-1.amazonaws.com", + "Upgrade-Insecure-Requests": "1", + "User-Agent": "Custom User Agent String", + "Via": "1.1 08f323deadbeefa7af34d5feb414ce27.cloudfront.net (CloudFront)", + "X-Amz-Cf-Id": "cDehVQoZnx43VYQb9j2-nvCh-9z396Uhbp027Y2JvkCPNLmGJHqlaA==", + "X-Forwarded-For": "127.0.0.1, 127.0.0.2", + "X-Forwarded-Port": "443", + "X-Forwarded-Proto": "https" + }, + "requestContext": { + "accountId": "123456789012", + "resourceId": "123456", + "stage": "prod", + "requestId": "c6af9ac6-7b61-11e6-9a41-93e8deadbeef", + "requestTime": "09/Apr/2015:12:34:56 +0000", + "requestTimeEpoch": 1428582896000, + "identity": { + "cognitoIdentityPoolId": null, + "accountId": null, + "cognitoIdentityId": null, + "caller": null, + "accessKey": null, + "sourceIp": "127.0.0.1", + "cognitoAuthenticationType": null, + "cognitoAuthenticationProvider": null, + "userArn": null, + "userAgent": "Custom User Agent String", + "user": null + }, + "path": "/prod/unhandled-async", + "resourcePath": "/unhandled-async", + "httpMethod": "GET", + "apiId": "1234567890", + "protocol": "HTTP/1.1" + } +} diff --git a/test/aws-lambda/features/fixtures/serverless-express-app/events/unhandled-next.json b/test/aws-lambda/features/fixtures/serverless-express-app/events/unhandled-next.json new file mode 100644 index 000000000..7a31bae60 --- /dev/null +++ b/test/aws-lambda/features/fixtures/serverless-express-app/events/unhandled-next.json @@ -0,0 +1,51 @@ +{ + "resource": "/unhandled-next", + "path": "/unhandled-next", + "httpMethod": "GET", + "headers": { + "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", + "Accept-Encoding": "gzip, deflate, sdch", + "Accept-Language": "en-US,en;q=0.8", + "Cache-Control": "max-age=0", + "CloudFront-Forwarded-Proto": "https", + "CloudFront-Is-Desktop-Viewer": "true", + "CloudFront-Is-Mobile-Viewer": "false", + "CloudFront-Is-SmartTV-Viewer": "false", + "CloudFront-Is-Tablet-Viewer": "false", + "CloudFront-Viewer-Country": "US", + "Host": "1234567890.execute-api.us-east-1.amazonaws.com", + "Upgrade-Insecure-Requests": "1", + "User-Agent": "Custom User Agent String", + "Via": "1.1 08f323deadbeefa7af34d5feb414ce27.cloudfront.net (CloudFront)", + "X-Amz-Cf-Id": "cDehVQoZnx43VYQb9j2-nvCh-9z396Uhbp027Y2JvkCPNLmGJHqlaA==", + "X-Forwarded-For": "127.0.0.1, 127.0.0.2", + "X-Forwarded-Port": "443", + "X-Forwarded-Proto": "https" + }, + "requestContext": { + "accountId": "123456789012", + "resourceId": "123456", + "stage": "prod", + "requestId": "c6af9ac6-7b61-11e6-9a41-93e8deadbeef", + "requestTime": "09/Apr/2015:12:34:56 +0000", + "requestTimeEpoch": 1428582896000, + "identity": { + "cognitoIdentityPoolId": null, + "accountId": null, + "cognitoIdentityId": null, + "caller": null, + "accessKey": null, + "sourceIp": "127.0.0.1", + "cognitoAuthenticationType": null, + "cognitoAuthenticationProvider": null, + "userArn": null, + "userAgent": "Custom User Agent String", + "user": null + }, + "path": "/prod/unhandled-next", + "resourcePath": "/unhandled-next", + "httpMethod": "GET", + "apiId": "1234567890", + "protocol": "HTTP/1.1" + } +} diff --git a/test/aws-lambda/features/fixtures/serverless-express-app/events/unhandled.json b/test/aws-lambda/features/fixtures/serverless-express-app/events/unhandled.json new file mode 100644 index 000000000..f0701e88d --- /dev/null +++ b/test/aws-lambda/features/fixtures/serverless-express-app/events/unhandled.json @@ -0,0 +1,51 @@ +{ + "resource": "/unhandled", + "path": "/unhandled", + "httpMethod": "GET", + "headers": { + "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", + "Accept-Encoding": "gzip, deflate, sdch", + "Accept-Language": "en-US,en;q=0.8", + "Cache-Control": "max-age=0", + "CloudFront-Forwarded-Proto": "https", + "CloudFront-Is-Desktop-Viewer": "true", + "CloudFront-Is-Mobile-Viewer": "false", + "CloudFront-Is-SmartTV-Viewer": "false", + "CloudFront-Is-Tablet-Viewer": "false", + "CloudFront-Viewer-Country": "US", + "Host": "1234567890.execute-api.us-east-1.amazonaws.com", + "Upgrade-Insecure-Requests": "1", + "User-Agent": "Custom User Agent String", + "Via": "1.1 08f323deadbeefa7af34d5feb414ce27.cloudfront.net (CloudFront)", + "X-Amz-Cf-Id": "cDehVQoZnx43VYQb9j2-nvCh-9z396Uhbp027Y2JvkCPNLmGJHqlaA==", + "X-Forwarded-For": "127.0.0.1, 127.0.0.2", + "X-Forwarded-Port": "443", + "X-Forwarded-Proto": "https" + }, + "requestContext": { + "accountId": "123456789012", + "resourceId": "123456", + "stage": "prod", + "requestId": "c6af9ac6-7b61-11e6-9a41-93e8deadbeef", + "requestTime": "09/Apr/2015:12:34:56 +0000", + "requestTimeEpoch": 1428582896000, + "identity": { + "cognitoIdentityPoolId": null, + "accountId": null, + "cognitoIdentityId": null, + "caller": null, + "accessKey": null, + "sourceIp": "127.0.0.1", + "cognitoAuthenticationType": null, + "cognitoAuthenticationProvider": null, + "userArn": null, + "userAgent": "Custom User Agent String", + "user": null + }, + "path": "/prod/unhandled", + "resourcePath": "/unhandled", + "httpMethod": "GET", + "apiId": "1234567890", + "protocol": "HTTP/1.1" + } +} diff --git a/test/aws-lambda/features/fixtures/serverless-express-app/template.yaml b/test/aws-lambda/features/fixtures/serverless-express-app/template.yaml new file mode 100644 index 000000000..f868413d7 --- /dev/null +++ b/test/aws-lambda/features/fixtures/serverless-express-app/template.yaml @@ -0,0 +1,41 @@ +AWSTemplateFormatVersion: '2010-09-09' +Transform: AWS::Serverless-2016-10-31 +Description: serverless-express-app + +Globals: + Function: + Timeout: 30 + Environment: + Variables: + BUGSNAG_API_KEY: + BUGSNAG_NOTIFY_ENDPOINT: + BUGSNAG_SESSIONS_ENDPOINT: + BUGSNAG_AUTO_TRACK_SESSIONS: true + BUGSNAG_AUTO_DETECT_ERRORS: true + +Resources: + ExpressApi: + Type: AWS::Serverless::Api + Properties: + StageName: prod + BinaryMediaTypes: ['*/*'] + + ExpressFunction: + Type: AWS::Serverless::Function + Properties: + CodeUri: app/ + Handler: index.lambdaHandler + Runtime: nodejs14.x + Events: + ProxyApiRoot: + Type: Api + Properties: + RestApiId: !Ref ExpressApi + Path: / + Method: ANY + ProxyApiGreedy: + Type: Api + Properties: + RestApiId: !Ref ExpressApi + Path: /{proxy+} + Method: ANY diff --git a/test/aws-lambda/features/handled.feature b/test/aws-lambda/features/handled.feature index a16659bfb..4850ee4c7 100644 --- a/test/aws-lambda/features/handled.feature +++ b/test/aws-lambda/features/handled.feature @@ -1,5 +1,6 @@ Feature: Handled exceptions are reported correctly in lambda functions +@simple-app Scenario Outline: handled exceptions are reported Given I setup the environment When I invoke the "" lambda in "features/fixtures/simple-app" with the "events//handled-exception.json" event @@ -30,6 +31,7 @@ Scenario Outline: handled exceptions are reported | CallbackHandledExceptionFunctionNode14 | callback | 14 | | CallbackHandledExceptionFunctionNode12 | callback | 12 | +@simple-app Scenario Outline: handled exceptions are still reported when autoDetectErrors is false Given I setup the environment And I set environment variable "BUGSNAG_AUTO_DETECT_ERRORS" to "false" @@ -60,3 +62,29 @@ Scenario Outline: handled exceptions are still reported when autoDetectErrors is | AsyncHandledExceptionFunctionNode12 | async | 12 | | CallbackHandledExceptionFunctionNode14 | callback | 14 | | CallbackHandledExceptionFunctionNode12 | callback | 12 | + +@serverless-express-app +Scenario: handled exceptions are reported when using serverless-express + Given I setup the environment + When I invoke the "ExpressFunction" lambda in "features/fixtures/serverless-express-app" with the "events/handled.json" event + Then the lambda response "body.message" equals "did not crash :)" + And the lambda response "statusCode" equals 200 + And the SAM exit code equals 0 + When I wait to receive an error + Then the error is valid for the error reporting API version "4" for the "Bugsnag Node" notifier + And the event "unhandled" is false + And the event "severity" equals "warning" + And the event "severityReason.type" equals "handledException" + And the exception "errorClass" equals "Error" + And the exception "message" equals "no crashing here" + And the exception "type" equals "nodejs" + And the "file" of stack frame 0 equals "app.js" + And the event "metaData.AWS Lambda context.functionName" equals "ExpressFunction" + And the event "metaData.AWS Lambda context.awsRequestId" is not null + And the event "device.runtimeVersions.node" matches "^14\.\d+\.\d+$" + When I wait to receive a session + Then the session is valid for the session reporting API version "1" for the "Bugsnag Node" notifier + And the session "id" is not null + And the session "startedAt" is a timestamp + And the event "session.events.handled" equals 1 + And the event "session.events.unhandled" equals 0 diff --git a/test/aws-lambda/features/promise-rejection.feature b/test/aws-lambda/features/promise-rejection.feature index 24261798d..858a494be 100644 --- a/test/aws-lambda/features/promise-rejection.feature +++ b/test/aws-lambda/features/promise-rejection.feature @@ -1,5 +1,6 @@ Feature: Unhandled promise rejections are reported correctly in lambda functions +@simple-app Scenario Outline: unhandled promise rejections are reported Given I setup the environment When I invoke the "" lambda in "features/fixtures/simple-app" with the "events//promise-rejection.json" event @@ -34,6 +35,7 @@ Scenario Outline: unhandled promise rejections are reported | CallbackPromiseRejectionFunctionNode14 | callback | 14 | | CallbackPromiseRejectionFunctionNode12 | callback | 12 | +@simple-app Scenario Outline: unhandled promise rejections are not reported when autoDetectErrors is false Given I setup the environment And I set environment variable "BUGSNAG_AUTO_DETECT_ERRORS" to "false" @@ -53,3 +55,33 @@ Scenario Outline: unhandled promise rejections are not reported when autoDetectE | AsyncPromiseRejectionFunctionNode12 | async | | CallbackPromiseRejectionFunctionNode14 | callback | | CallbackPromiseRejectionFunctionNode12 | callback | + +@serverless-express-app +Scenario: promise rejections are reported when using serverless-express + Given I setup the environment + When I invoke the "ExpressFunction" lambda in "features/fixtures/serverless-express-app" with the "events/promise-rejection.json" event + Then the lambda response "errorMessage" equals "Error: abc" + And the lambda response "errorType" equals "Runtime.UnhandledPromiseRejection" + And the lambda response "trace" is an array with 4 elements + And the lambda response "trace.0" equals "Runtime.UnhandledPromiseRejection: Error: abc" + And the lambda response "body" is null + And the lambda response "statusCode" is null + And the SAM exit code equals 0 + When I wait to receive an error + Then the error is valid for the error reporting API version "4" for the "Bugsnag Node" notifier + And the event "unhandled" is true + And the event "severity" equals "error" + And the event "severityReason.type" equals "unhandledPromiseRejection" + And the exception "errorClass" equals "Error" + And the exception "message" equals "abc" + And the exception "type" equals "nodejs" + And the "file" of stack frame 0 equals "app.js" + And the event "metaData.AWS Lambda context.functionName" equals "ExpressFunction" + And the event "metaData.AWS Lambda context.awsRequestId" is not null + And the event "device.runtimeVersions.node" matches "^14\.\d+\.\d+$" + When I wait to receive a session + Then the session is valid for the session reporting API version "1" for the "Bugsnag Node" notifier + And the session "id" is not null + And the session "startedAt" is a timestamp + And the event "session.events.handled" equals 0 + And the event "session.events.unhandled" equals 1 diff --git a/test/aws-lambda/features/scripts/build-fixtures b/test/aws-lambda/features/scripts/build-fixtures index 349a5edb8..9bb8cf0b8 100755 --- a/test/aws-lambda/features/scripts/build-fixtures +++ b/test/aws-lambda/features/scripts/build-fixtures @@ -23,6 +23,7 @@ BUGSNAG_PACKAGES = [ "plugin-app-duration", "plugin-aws-lambda", "plugin-contextualize", + "plugin-express", "plugin-intercept", "plugin-node-device", "plugin-node-in-project", @@ -91,10 +92,12 @@ def install_and_build parsed_template = YAML.safe_load(File.read(template)) - # Find all of the directories we need to install the packages in - destinations = parsed_template.fetch("Resources").values.map do |resource| - "#{fixture}/#{resource.fetch("Properties").fetch("CodeUri")}" - end.uniq + # Find all of the directories we need to install the packages in using the + # CodeUri of the "AWS::Serverless::Function" resources + destinations = parsed_template.fetch("Resources").values + .filter { |resource| resource["Type"] == "AWS::Serverless::Function" } + .map { |resource| "#{fixture}/#{resource.fetch("Properties").fetch("CodeUri")}" } + .uniq begin # Install the packages into each of the destination directories @@ -115,7 +118,7 @@ def install_and_build raise "Failed to build #{fixture_name}" unless success end ensure - return if DEBUG + next if DEBUG # Remove the packages from the destination directories as they aren't # needed anymore. A built lambda has them in its node_modules directory diff --git a/test/aws-lambda/features/sessions.feature b/test/aws-lambda/features/sessions.feature index 422df46fd..49c69e77d 100644 --- a/test/aws-lambda/features/sessions.feature +++ b/test/aws-lambda/features/sessions.feature @@ -1,5 +1,6 @@ Feature: Sessions are reported correctly in lambda functions +@simple-app Scenario Outline: sessions are reported Given I setup the environment When I invoke the "" lambda in "features/fixtures/simple-app" @@ -25,6 +26,7 @@ Scenario Outline: sessions are reported | CallbackHandledExceptionFunctionNode14 | 1 | 0 | | CallbackHandledExceptionFunctionNode12 | 1 | 0 | +@simple-app Scenario Outline: no session is sent when autoTrackSessions is false Given I setup the environment And I set environment variable "BUGSNAG_AUTO_TRACK_SESSIONS" to "false" diff --git a/test/aws-lambda/features/steps/aws-lambda-steps.rb b/test/aws-lambda/features/steps/aws-lambda-steps.rb index 1bcd38074..dbc8e28d1 100644 --- a/test/aws-lambda/features/steps/aws-lambda-steps.rb +++ b/test/aws-lambda/features/steps/aws-lambda-steps.rb @@ -1,15 +1,7 @@ Given('I setup the environment') do steps %Q{ Given I store the api key in the environment variable "BUGSNAG_API_KEY" - And I store the notify endpoint in the environment variable "BUGSNAG_NOTIFY_ENDPOINT" - And I store the sessions endpoint in the environment variable "BUGSNAG_SESSIONS_ENDPOINT" + And I set environment variable "BUGSNAG_NOTIFY_ENDPOINT" to "http://host.docker.internal:9339/notify" + And I set environment variable "BUGSNAG_SESSIONS_ENDPOINT" to "http://host.docker.internal:9339/sessions" } end - -Given('I store the notify endpoint in the environment variable {string}') do |name| - step("I set environment variable '#{name}' to 'http://host.docker.internal:9339/notify'") -end - -Given('I store the sessions endpoint in the environment variable {string}') do |name| - step("I set environment variable '#{name}' to 'http://host.docker.internal:9339/sessions'") -end diff --git a/test/aws-lambda/features/unhandled.feature b/test/aws-lambda/features/unhandled.feature index b203fdede..a1fb4394a 100644 --- a/test/aws-lambda/features/unhandled.feature +++ b/test/aws-lambda/features/unhandled.feature @@ -1,5 +1,6 @@ Feature: Unhandled exceptions are reported correctly in lambda functions +@simple-app Scenario Outline: unhandled exceptions are reported Given I setup the environment When I invoke the "" lambda in "features/fixtures/simple-app" with the "events//unhandled-exception.json" event @@ -36,6 +37,7 @@ Scenario Outline: unhandled exceptions are reported | CallbackThrownUnhandledExceptionFunctionNode14 | callback | thrown-unhandled-exception.js | 14 | 7 | | CallbackThrownUnhandledExceptionFunctionNode12 | callback | thrown-unhandled-exception.js | 12 | 7 | +@simple-app Scenario Outline: no error is reported when autoDetectErrors is false Given I setup the environment And I set environment variable "BUGSNAG_AUTO_DETECT_ERRORS" to "false" @@ -50,3 +52,62 @@ Scenario Outline: no error is reported when autoDetectErrors is false | CallbackUnhandledExceptionFunctionNode12 | callback | | CallbackThrownUnhandledExceptionFunctionNode14 | callback | | CallbackThrownUnhandledExceptionFunctionNode12 | callback | + +@serverless-express-app +Scenario Outline: unhandled exceptions are reported when using serverless-express + Given I setup the environment + When I invoke the "ExpressFunction" lambda in "features/fixtures/serverless-express-app" with the "events/.json" event + Then the lambda response "body.message" equals "" + And the lambda response "body.type" equals "Error" + And the lambda response "body.stacktrace" is an array with 11 elements + And the lambda response "body.stacktrace.0" equals "Error: " + And the lambda response "statusCode" equals 500 + And the SAM exit code equals 0 + When I wait to receive an error + Then the error is valid for the error reporting API version "4" for the "Bugsnag Node" notifier + And the event "unhandled" is true + And the event "severity" equals "error" + And the event "severityReason.type" equals "unhandledErrorMiddleware" + And the exception "errorClass" equals "Error" + And the exception "message" equals "" + And the exception "type" equals "nodejs" + And the "file" of stack frame 0 equals "app.js" + And the event "metaData.AWS Lambda context.functionName" equals "ExpressFunction" + And the event "metaData.AWS Lambda context.awsRequestId" is not null + And the event "device.runtimeVersions.node" matches "^14\.\d+\.\d+$" + When I wait to receive a session + Then the session is valid for the session reporting API version "1" for the "Bugsnag Node" notifier + And the session "id" is not null + And the session "startedAt" is a timestamp + And the event "session.events.handled" equals 0 + And the event "session.events.unhandled" equals 1 + + Examples: + | event-name | message | + | unhandled | broken :( | + | unhandled-next | borked | + +@serverless-express-app +Scenario: unhandled asynchronous exceptions are reported when using serverless-express + Given I setup the environment + When I invoke the "ExpressFunction" lambda in "features/fixtures/serverless-express-app" with the "events/unhandled-async.json" event + Then the lambda response is empty + And the SAM exit code equals 0 + When I wait to receive an error + Then the error is valid for the error reporting API version "4" for the "Bugsnag Node" notifier + And the event "unhandled" is true + And the event "severity" equals "error" + And the event "severityReason.type" equals "unhandledErrorMiddleware" + And the exception "errorClass" equals "Error" + And the exception "message" equals "busted" + And the exception "type" equals "nodejs" + And the "file" of stack frame 0 equals "app.js" + And the event "metaData.AWS Lambda context.functionName" equals "ExpressFunction" + And the event "metaData.AWS Lambda context.awsRequestId" is not null + And the event "device.runtimeVersions.node" matches "^14\.\d+\.\d+$" + When I wait to receive a session + Then the session is valid for the session reporting API version "1" for the "Bugsnag Node" notifier + And the session "id" is not null + And the session "startedAt" is a timestamp + And the event "session.events.handled" equals 0 + And the event "session.events.unhandled" equals 1 From 8b0ebce39b75844b7babb5e042c85c2d15951747 Mon Sep 17 00:00:00 2001 From: Karl Stenerud Date: Mon, 8 Mar 2021 12:57:06 +0100 Subject: [PATCH 56/65] Update ios vendoring script to match PLAT-5777 requirements --- packages/react-native/update-ios.sh | 168 ++++++++++++++++++++++++---- 1 file changed, 144 insertions(+), 24 deletions(-) diff --git a/packages/react-native/update-ios.sh b/packages/react-native/update-ios.sh index 74b620848..83c91eb28 100755 --- a/packages/react-native/update-ios.sh +++ b/packages/react-native/update-ios.sh @@ -1,25 +1,145 @@ #!/usr/bin/env bash -set -e - -IOS_REPO_DIR=../../../bugsnag-cocoa -IOS_DST=ios/vendor/bugsnag-cocoa - -echo "Clearing out ${IOS_DST}" -rm -rf $IOS_DST - -mkdir $IOS_DST - -echo "Copying vendor code from $IOS_REPO_DIR" -rsync --delete -al "$IOS_REPO_DIR/CHANGELOG.md" "$IOS_DST/CHANGELOG.md" -rsync --delete -al "$IOS_REPO_DIR/UPGRADING.md" "$IOS_DST/UPGRADING.md" -rsync --delete -al "$IOS_REPO_DIR/VERSION" "$IOS_DST/VERSION" -rsync --delete -al "$IOS_REPO_DIR/README.md" "$IOS_DST/README.md" -rsync --delete -al "$IOS_REPO_DIR/ORGANIZATION.md" "$IOS_DST/ORGANIZATION.md" -rsync --delete -al "$IOS_REPO_DIR/Bugsnag/" "$IOS_DST/Bugsnag/" -rsync --delete -al "$IOS_REPO_DIR/Framework/" "$IOS_DST/Framework/" -rsync --delete -al "$IOS_REPO_DIR/Bugsnag.xcodeproj/" "$IOS_DST/Bugsnag.xcodeproj/" -rsync --delete -al "$IOS_REPO_DIR/Bugsnag.podspec.json" "$IOS_DST/Bugsnag.podspec.json" - -echo "Recording version" -rm -rf ./ios/.bugsnag-cocoa-version -echo $(cd $IOS_REPO_DIR && git rev-parse HEAD) >> ./ios/.bugsnag-cocoa-version + +# ---------- +# Setup Code +# ---------- + +set -euo pipefail + +SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd) +SCRIPT_NAME="${0##*/}" +TEMP_DIR= + +cleanup() { + if [ "$TEMP_DIR" != "" ]; then + rm -rf "$TEMP_DIR" + fi +} +trap cleanup EXIT + +print_help() { + echo "Usage: $SCRIPT_NAME [options]" + echo + echo "Options:" + echo " --version (example: $SCRIPT_NAME --version 6.7.0)" + echo " --local (example: $SCRIPT_NAME --local ../../../bugsnag-cocoa)" + echo " --sha (example: $SCRIPT_NAME --sha c8210a3)" +} + +error_bad_opt(){ + echo "$SCRIPT_NAME: invalid option '$1'"; + echo + print_help + exit 1 +} + +error_missing_field(){ + echo "$SCRIPT_NAME: $1 missing required field"; + echo + print_help + exit 1 +} + +MODE=unset +GIT_TAG= +BUGSNAG_COCOA_REPO_DIR= + +# BSD-friendly getopt-style supporting longopt +while [ $# -gt 0 ]; do + case $1 in + --) shift; break;; + -*) case $1 in + --version) + if [ $# -lt 2 ]; then error_missing_field $1; fi + MODE=version + GIT_TAG=v$2 + shift;; + --local) + if [ $# -lt 2 ]; then error_missing_field $1; fi + MODE=local + BUGSNAG_COCOA_REPO_DIR="$2" + shift;; + --sha) + if [ $# -lt 2 ]; then error_missing_field $1; fi + MODE=sha; + GIT_TAG=$2; + shift;; + -*) + error_bad_opt $1;; + esac;; + *) error_bad_opt $1;; + esac + shift +done + +# ----------- +# Script Code +# ----------- + +revendor_from_dir() { + local dst_dir="$SCRIPT_DIR/ios/vendor/bugsnag-cocoa" + local src_dir="$1" + if [ ! -d "$src_dir" ]; then + echo "Source directory not found: $src_dir" + exit 1 + fi + if [ ! -d "$src_dir/Bugsnag.xcodeproj" ]; then + echo "Source directory doesn't look like the bugsnag-cocoa repo: $src_dir" + exit 1 + fi + + src_dir="$(cd "$src_dir" && pwd)" + + echo "Rebuilding vendor dir ${dst_dir}" + rm -rf "$dst_dir" + mkdir "$dst_dir" + + echo "Copying vendor code from $src_dir" + rsync --delete -al "$src_dir/CHANGELOG.md" "$dst_dir/CHANGELOG.md" + rsync --delete -al "$src_dir/UPGRADING.md" "$dst_dir/UPGRADING.md" + rsync --delete -al "$src_dir/VERSION" "$dst_dir/VERSION" + rsync --delete -al "$src_dir/README.md" "$dst_dir/README.md" + rsync --delete -al "$src_dir/ORGANIZATION.md" "$dst_dir/ORGANIZATION.md" + rsync --delete -al "$src_dir/Bugsnag/" "$dst_dir/Bugsnag/" + rsync --delete -al "$src_dir/Framework/" "$dst_dir/Framework/" + rsync --delete -al "$src_dir/Bugsnag.xcodeproj/" "$dst_dir/Bugsnag.xcodeproj/" + rsync --delete -al "$src_dir/Bugsnag.podspec.json" "$dst_dir/Bugsnag.podspec.json" + + echo "Recording version" + rm -rf "$SCRIPT_DIR/ios/.bugsnag-cocoa-version" + echo $(cd "$src_dir" && git rev-parse HEAD) >> "$SCRIPT_DIR/ios/.bugsnag-cocoa-version" +} + +revendor_from_clean_repo() { + local tag=$1 + echo "Checking out https://github.com/bugsnag/bugsnag-cocoa.git with tag $tag" + TEMP_DIR="$(mktemp -d)" + pushd "$TEMP_DIR" >/dev/null + git clone https://github.com/bugsnag/bugsnag-cocoa.git + cd bugsnag-cocoa + git checkout $tag || exit 1 + popd >/dev/null + revendor_from_dir "$TEMP_DIR/bugsnag-cocoa" +} + +case $MODE in + unset) + print_help + exit 1 + ;; + version) + revendor_from_clean_repo $GIT_TAG + ;; + sha) + revendor_from_clean_repo $GIT_TAG + ;; + local) + revendor_from_dir "$BUGSNAG_COCOA_REPO_DIR" + ;; + *) + echo "BUG: Invalid MODE: $MODE" + exit 1 + ;; +esac + +echo "Update complete!" From 1a1d4c551d83b77a4f34db55b1233ea954ddbb2c Mon Sep 17 00:00:00 2001 From: Joe Haines Date: Tue, 9 Mar 2021 10:52:38 +0000 Subject: [PATCH 57/65] Ignore the Gemfile.lock CI doesn't have an up-to-date bundler version, but it's not really worth updating as we have a single pinned dependency (so it's essentially locked even without a lockfile) --- test/aws-lambda/.gitignore | 5 +++ test/aws-lambda/Gemfile.lock | 85 ------------------------------------ 2 files changed, 5 insertions(+), 85 deletions(-) delete mode 100644 test/aws-lambda/Gemfile.lock diff --git a/test/aws-lambda/.gitignore b/test/aws-lambda/.gitignore index e87177b93..74390a5d8 100644 --- a/test/aws-lambda/.gitignore +++ b/test/aws-lambda/.gitignore @@ -1,3 +1,8 @@ .aws-sam node_modules/ .tgz + +# Ignore the gem lockfile due to Bundler version differences on CI +# We don't really need this anyway because we have one dependency (MazeRunner) +# and we pin it to a specific tag, so it's effectively locked +/Gemfile.lock diff --git a/test/aws-lambda/Gemfile.lock b/test/aws-lambda/Gemfile.lock deleted file mode 100644 index d290c374e..000000000 --- a/test/aws-lambda/Gemfile.lock +++ /dev/null @@ -1,85 +0,0 @@ -GIT - remote: https://github.com/bugsnag/maze-runner - revision: b9397574529d141ba07a8050cf7deb2213fda3b8 - tag: v4.12.1 - specs: - bugsnag-maze-runner (4.12.0) - appium_lib (~> 11.2.0) - boring (~> 0.1.0) - cucumber (~> 3.1.2) - cucumber-expressions (~> 6.0.0) - curb (~> 0.9.6) - gherkin (~> 5.1.0) - minitest (~> 5.0) - optimist (~> 3.0.1) - os (~> 1.0.0) - rake (~> 12.3.3) - selenium-webdriver (~> 3.11) - test-unit (~> 3.3.0) - -GEM - remote: https://rubygems.org/ - specs: - appium_lib (11.2.0) - appium_lib_core (~> 4.1) - nokogiri (~> 1.8, >= 1.8.1) - tomlrb (~> 1.1) - appium_lib_core (4.4.1) - faye-websocket (~> 0.11.0) - selenium-webdriver (~> 3.14, >= 3.14.1) - backports (3.20.2) - boring (0.1.0) - builder (3.2.4) - childprocess (3.0.0) - cucumber (3.1.2) - builder (>= 2.1.2) - cucumber-core (~> 3.2.0) - cucumber-expressions (~> 6.0.1) - cucumber-wire (~> 0.0.1) - diff-lcs (~> 1.3) - gherkin (~> 5.1.0) - multi_json (>= 1.7.5, < 2.0) - multi_test (>= 0.1.2) - cucumber-core (3.2.1) - backports (>= 3.8.0) - cucumber-tag_expressions (~> 1.1.0) - gherkin (~> 5.0) - cucumber-expressions (6.0.1) - cucumber-tag_expressions (1.1.1) - cucumber-wire (0.0.1) - curb (0.9.11) - diff-lcs (1.4.4) - eventmachine (1.2.7) - faye-websocket (0.11.0) - eventmachine (>= 0.12.0) - websocket-driver (>= 0.5.1) - gherkin (5.1.0) - minitest (5.14.4) - multi_json (1.15.0) - multi_test (0.1.2) - nokogiri (1.11.1-x86_64-darwin) - racc (~> 1.4) - optimist (3.0.1) - os (1.0.1) - power_assert (2.0.0) - racc (1.5.2) - rake (12.3.3) - rubyzip (2.3.0) - selenium-webdriver (3.142.7) - childprocess (>= 0.5, < 4.0) - rubyzip (>= 1.2.2) - test-unit (3.3.9) - power_assert - tomlrb (1.3.0) - websocket-driver (0.7.3) - websocket-extensions (>= 0.1.0) - websocket-extensions (0.1.5) - -PLATFORMS - x86_64-darwin-19 - -DEPENDENCIES - bugsnag-maze-runner! - -BUNDLED WITH - 2.2.11 From 19a2027c93a8fa4ed42c1af3e0bfd0d10945ab2d Mon Sep 17 00:00:00 2001 From: Joe Haines Date: Tue, 9 Mar 2021 10:54:30 +0000 Subject: [PATCH 58/65] Add AWS Lambda tests to Buildkite pipeline --- .buildkite/pipeline.yml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/.buildkite/pipeline.yml b/.buildkite/pipeline.yml index 78307a0b5..57ac1f43e 100644 --- a/.buildkite/pipeline.yml +++ b/.buildkite/pipeline.yml @@ -85,6 +85,18 @@ steps: message: '${BUILDKITE_MESSAGE}' async: true + - label: ':aws-lambda: AWS Lambda tests' + timeout_in_minutes: 35 + agents: + queue: 'opensource-mac-aws-sam' + commands: + # force the NPM registry as the default on CI is artifactory, which can't + # currently install from our lockfile + - npm ci --registry https://registry.npmjs.org + - cd test/aws-lambda + - bundle install + - bundle exec maze-runner + # # Core tests and checks # From 85c15bd19fcdd15163be6f1bf6af8d476de83772 Mon Sep 17 00:00:00 2001 From: Joe Haines Date: Tue, 9 Mar 2021 14:52:08 +0000 Subject: [PATCH 59/65] Ensure we skip the correct number of frames --- packages/plugin-vue/src/vue.js | 2 +- packages/plugin-vue/src/vue2.js | 2 +- packages/plugin-vue/test/index.test.ts | 66 ++++++++++++++++++++++++++ 3 files changed, 68 insertions(+), 2 deletions(-) diff --git a/packages/plugin-vue/src/vue.js b/packages/plugin-vue/src/vue.js index ed254686b..f65b90ae5 100644 --- a/packages/plugin-vue/src/vue.js +++ b/packages/plugin-vue/src/vue.js @@ -3,7 +3,7 @@ module.exports = (app, client) => { const handler = (err, vm, info) => { const handledState = { severity: 'error', unhandled: true, severityReason: { type: 'unhandledException' } } - const event = client.Event.create(err, true, handledState, 1) + const event = client.Event.create(err, true, handledState, 'vue error handler', 1) event.addMetadata('vue', { errorInfo: ErrorTypeStrings[info] || info, diff --git a/packages/plugin-vue/src/vue2.js b/packages/plugin-vue/src/vue2.js index 5241fa840..d4c1ca357 100644 --- a/packages/plugin-vue/src/vue2.js +++ b/packages/plugin-vue/src/vue2.js @@ -3,7 +3,7 @@ module.exports = (Vue, client) => { const handler = (err, vm, info) => { const handledState = { severity: 'error', unhandled: true, severityReason: { type: 'unhandledException' } } - const event = client.Event.create(err, true, handledState, 1) + const event = client.Event.create(err, true, handledState, 'vue error handler', 1) event.addMetadata('vue', { errorInfo: info, diff --git a/packages/plugin-vue/test/index.test.ts b/packages/plugin-vue/test/index.test.ts index aaea15321..d058f6a18 100644 --- a/packages/plugin-vue/test/index.test.ts +++ b/packages/plugin-vue/test/index.test.ts @@ -41,6 +41,7 @@ describe('bugsnag vue', () => { sendEvent: (payload) => { expect(payload.events[0].errors[0].errorClass).toBe('Error') expect(payload.events[0].errors[0].errorMessage).toBe('oops') + expect(payload.events[0].errors[0].stacktrace[0].file).toBe(__filename) expect(payload.events[0]._metadata.vue).toBeDefined() expect(payload.events[0]._metadata.vue.component).toBe('MyComponent') expect(payload.events[0]._metadata.vue.errorInfo).toBe('render function') @@ -131,6 +132,43 @@ describe('bugsnag vue', () => { errorHandler(new Error('oops'), { $options: {} }, 1) }) + it('tolerates string "errors"', done => { + const mockVueApp: Vue3App = { + use: (plugin) => { + plugin.install(mockVueApp) + }, + config: { errorHandler: undefined } + } + + const client = new Client({ apiKey: 'API_KEYYY', plugins: [new BugsnagVuePlugin()] }) + + // eslint-disable-next-line + mockVueApp.use(client.getPlugin('vue')!) + + client._setDelivery(client => ({ + sendEvent: (payload) => { + expect(payload.events[0].errors[0].errorClass).toBe('Error') + expect(payload.events[0].errors[0].errorMessage).toBe('oops') + + // ensure the top-most stack frame has an accurate file; it should be + // this test file as that is where the error string was created + expect(payload.events[0].errors[0].stacktrace[0].file).toBe(__filename) + + expect(payload.events[0]._metadata.vue).toBeDefined() + expect(payload.events[0]._metadata.vue.component).toBe('MyComponent') + expect(payload.events[0]._metadata.vue.errorInfo).toBe('render function') + done() + }, + sendSession: () => {} + })) + + expect(typeof mockVueApp.config.errorHandler).toBe('function') + + const errorHandler = mockVueApp.config.errorHandler as unknown as Vue3ErrorHandler + + errorHandler('oops', { $options: { name: 'MyComponent' } }, 1) + }) + // // VUE 2 // @@ -150,6 +188,7 @@ describe('bugsnag vue', () => { sendEvent: (payload) => { expect(payload.events[0].errors[0].errorClass).toBe('Error') expect(payload.events[0].errors[0].errorMessage).toBe('oops') + expect(payload.events[0].errors[0].stacktrace[0].file).toBe(__filename) expect(payload.events[0]._metadata.vue).toBeDefined() done() }, @@ -179,6 +218,33 @@ describe('bugsnag vue', () => { errorHandler(new Error('oops'), { $root: true, $options: {} }, 'callback for watcher "fooBarBaz"') }) + it('supports string "errors"', done => { + const mockVue = { config: { errorHandler: undefined } } + const client = new Client({ apiKey: 'API_KEYYY', plugins: [new BugsnagVuePlugin(mockVue)] }) + + client._setDelivery(client => ({ + sendEvent: (payload) => { + expect(payload.events[0].errors[0].errorClass).toBe('Error') + expect(payload.events[0].errors[0].errorMessage).toBe('oops') + + // ensure the top-most stack frame has an accurate file; it should be + // this test file as that is where the error string was created + expect(payload.events[0].errors[0].stacktrace[0].file).toBe(__filename) + + expect(payload.events[0]._metadata.vue).toBeDefined() + done() + }, + sendSession: () => {} + })) + + expect(typeof mockVue.config.errorHandler).toBe('function') + + const errorHandler = mockVue.config.errorHandler as unknown as Vue2ErrorHandler + + // @ts-ignore + errorHandler('oops', { $root: true, $options: {} }, 'callback for watcher "fooBarBaz"') + }) + describe('global Vue', () => { // Workaround typescript getting upset at messing around with global // by taking a reference as 'any' and modifying that instead From 864e7ef389044b22b862ea6a2c0e7cccac9fb72d Mon Sep 17 00:00:00 2001 From: Karl Stenerud Date: Thu, 11 Mar 2021 11:41:54 +0100 Subject: [PATCH 60/65] Update bugsnag-cocoa to v6.7.1 --- CHANGELOG.md | 11 ++++++ .../react-native/ios/.bugsnag-cocoa-version | 2 +- .../vendor/bugsnag-cocoa/Bugsnag.podspec.json | 4 +- .../Bugsnag/BugsnagSessionTracker.m | 2 +- .../Bugsnag/Client/BugsnagClient.m | 6 ++- .../KSCrash/Recording/BSG_KSSystemInfo.m | 37 ++++++++++++------- .../KSCrash/Recording/Tools/BSG_KSMach.c | 4 +- .../Bugsnag/Payload/BugsnagDevice+Private.h | 2 +- .../Bugsnag/Payload/BugsnagDevice.m | 2 +- .../Payload/BugsnagDeviceWithState+Private.h | 2 +- .../Bugsnag/Payload/BugsnagDeviceWithState.m | 4 +- .../Bugsnag/Payload/BugsnagEvent.m | 2 +- .../Bugsnag/Payload/BugsnagNotifier.m | 2 +- .../ios/vendor/bugsnag-cocoa/CHANGELOG.md | 13 +++++++ .../vendor/bugsnag-cocoa/Framework/Info.plist | 2 +- .../ios/vendor/bugsnag-cocoa/VERSION | 2 +- 16 files changed, 67 insertions(+), 30 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4a56c1c83..972fc5198 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,16 @@ # Changelog +## TBD + +### Changed + +- (react-native): Update bugsnag-cocoa to v6.7.1 + - Fix `os_proc_available_memory` runtime link error on Mac Catalyst. [bugsnag-cocoa#1025](https://github.com/bugsnag/bugsnag-cocoa/pull/1025) + - Fix missing `osName` and `osVersion` for errors reported from app extensions that do not link against UIKit. [bugsnag-cocoa#1022](https://github.com/bugsnag/bugsnag-cocoa/pull/1022) + - Fix incorrect `freeMemory` for errors reported via `notify()` [bugsnag-cocoa#1021](https://github.com/bugsnag/bugsnag-cocoa/pull/1021) + + + ## v7.8.2 (2021-03-04) ### Changed diff --git a/packages/react-native/ios/.bugsnag-cocoa-version b/packages/react-native/ios/.bugsnag-cocoa-version index aac19293e..8b13fc764 100644 --- a/packages/react-native/ios/.bugsnag-cocoa-version +++ b/packages/react-native/ios/.bugsnag-cocoa-version @@ -1 +1 @@ -dc551c901478f23ca81684188e2ae62d98afc26c +bb114d83e676db6a40fbd7911b78a1490b0d57b1 diff --git a/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag.podspec.json b/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag.podspec.json index e08bdab70..d0bff7d95 100644 --- a/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag.podspec.json +++ b/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag.podspec.json @@ -1,6 +1,6 @@ { "name": "Bugsnag", - "version": "6.7.0", + "version": "6.7.1", "summary": "The Bugsnag crash reporting framework for Apple platforms.", "homepage": "https://bugsnag.com", "license": "MIT", @@ -9,7 +9,7 @@ }, "source": { "git": "https://github.com/bugsnag/bugsnag-cocoa.git", - "tag": "v6.7.0" + "tag": "v6.7.1" }, "frameworks": [ "Foundation", diff --git a/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag/BugsnagSessionTracker.m b/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag/BugsnagSessionTracker.m index 6f69caa33..dc4dc6d80 100644 --- a/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag/BugsnagSessionTracker.m +++ b/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag/BugsnagSessionTracker.m @@ -123,7 +123,7 @@ - (void)startNewSessionWithAutoCaptureValue:(BOOL)isAutoCaptured { BugsnagApp *app = [BugsnagApp appWithDictionary:@{@"system": systemInfo} config:self.config codeBundleId:self.codeBundleId]; - BugsnagDevice *device = [BugsnagDevice deviceWithDictionary:@{@"system": systemInfo}]; + BugsnagDevice *device = [BugsnagDevice deviceWithKSCrashReport:@{@"system": systemInfo}]; [device appendRuntimeInfo:self.extraRuntimeInfo]; BugsnagSession *newSession = [[BugsnagSession alloc] initWithId:[[NSUUID UUID] UUIDString] diff --git a/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag/Client/BugsnagClient.m b/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag/Client/BugsnagClient.m index ba78c190a..5f49ffb27 100644 --- a/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag/Client/BugsnagClient.m +++ b/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag/Client/BugsnagClient.m @@ -1031,6 +1031,10 @@ - (void)metadataChanged:(BugsnagMetadata *)metadata { */ #if BSG_PLATFORM_IOS - (void)batteryChanged:(NSNotification *)notification { + if (![UIDEVICE currentDevice]) { + return; + } + NSNumber *batteryLevel = @([UIDEVICE currentDevice].batteryLevel); BOOL charging = [UIDEVICE currentDevice].batteryState == UIDeviceBatteryStateCharging || [UIDEVICE currentDevice].batteryState == UIDeviceBatteryStateFull; @@ -1163,7 +1167,7 @@ - (BugsnagAppWithState *)generateAppWithState:(NSDictionary *)systemInfo { } - (BugsnagDeviceWithState *)generateDeviceWithState:(NSDictionary *)systemInfo { - BugsnagDeviceWithState *device = [BugsnagDeviceWithState deviceWithDictionary:@{@"system": systemInfo}]; + BugsnagDeviceWithState *device = [BugsnagDeviceWithState deviceWithKSCrashReport:@{@"system": systemInfo}]; device.time = [NSDate date]; // default to current time for handled errors [device appendRuntimeInfo:self.extraRuntimeInfo]; device.orientation = _lastOrientation; diff --git a/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSSystemInfo.m b/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSSystemInfo.m index 1b747e6b2..35afa7b50 100644 --- a/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSSystemInfo.m +++ b/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSSystemInfo.m @@ -371,25 +371,34 @@ + (NSDictionary *)systemInfo { #ifdef __clang_version__ sysInfo[@BSG_KSSystemField_ClangVersion] = @__clang_version__; #endif -#if BSG_HAS_UIDEVICE - sysInfo[@BSG_KSSystemField_SystemName] = [UIDEVICE currentDevice].systemName; - sysInfo[@BSG_KSSystemField_SystemVersion] = [UIDEVICE currentDevice].systemVersion; -#else +#if TARGET_OS_IOS + // Note: This does not match UIDevice.currentDevice.systemName for versions + // prior to (and some versions of) iOS 9 where the systemName was reported + // as "iPhone OS". UIDevice gets its data from MobileGestalt which is a + // private API. /System/Library/CoreServices/SystemVersion.plist contains + // the information we need but will contain the macOS information when + // running on the Simulator. + sysInfo[@BSG_KSSystemField_SystemName] = @"iOS"; +#elif TARGET_OS_OSX sysInfo[@BSG_KSSystemField_SystemName] = @"Mac OS"; +#elif TARGET_OS_TV + sysInfo[@BSG_KSSystemField_SystemName] = @"tvOS"; +#endif NSOperatingSystemVersion version = [NSProcessInfo processInfo].operatingSystemVersion; NSString *systemVersion; if (version.patchVersion == 0) { systemVersion = - [NSString stringWithFormat:@"%ld.%ld", version.majorVersion, - version.minorVersion]; + [NSString stringWithFormat:@"%ld.%ld", + (long)version.majorVersion, (long)version.minorVersion]; } else { - systemVersion = [NSString - stringWithFormat:@"%ld.%ld.%ld", version.majorVersion, - version.minorVersion, version.patchVersion]; + systemVersion = + [NSString stringWithFormat:@"%ld.%ld.%ld", + (long)version.majorVersion, (long)version.minorVersion, + (long)version.patchVersion]; } sysInfo[@BSG_KSSystemField_SystemVersion] = systemVersion; -#endif + if ([self isSimulatorBuild]) { NSString *model = [NSProcessInfo processInfo] .environment[BSGKeySimulatorModelId]; @@ -429,11 +438,11 @@ + (NSDictionary *)systemInfo { sysInfo[@BSG_KSSystemField_DeviceAppHash] = [self deviceAndAppHash]; sysInfo[@BSG_KSSystemField_BuildType] = [BSG_KSSystemInfo buildType]; - NSDictionary *memory = @{ - @BSG_KSSystemField_Size: [self int64Sysctl:@"hw.memsize"], - @BSG_KSCrashField_Usable: @(bsg_ksmachusableMemory()) + sysInfo[@(BSG_KSSystemField_Memory)] = @{ + @(BSG_KSCrashField_Free): @(bsg_ksmachfreeMemory()), + @(BSG_KSCrashField_Usable): @(bsg_ksmachusableMemory()), + @(BSG_KSSystemField_Size): [self int64Sysctl:@"hw.memsize"] }; - sysInfo[@BSG_KSSystemField_Memory] = memory; NSDictionary *statsInfo = [[BSG_KSCrash sharedInstance] captureAppStats]; sysInfo[@BSG_KSCrashField_AppStats] = statsInfo; diff --git a/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag/KSCrash/Source/KSCrash/Recording/Tools/BSG_KSMach.c b/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag/KSCrash/Source/KSCrash/Recording/Tools/BSG_KSMach.c index fe4aff40e..d9c3903e9 100644 --- a/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag/KSCrash/Source/KSCrash/Recording/Tools/BSG_KSMach.c +++ b/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag/KSCrash/Source/KSCrash/Recording/Tools/BSG_KSMach.c @@ -38,7 +38,7 @@ #include #include -#if __has_include() && TARGET_OS_IPHONE +#if __has_include() && TARGET_OS_IPHONE && !TARGET_OS_MACCATALYST #include #endif @@ -71,7 +71,7 @@ static pthread_t bsg_g_topThread; static size_t (* get_available_memory)(void); static void bsg_ksmachfreeMemory_init(void) { -#if __has_include() && TARGET_OS_IPHONE +#if __has_include() && TARGET_OS_IPHONE && !TARGET_OS_MACCATALYST if (__builtin_available(iOS 13.0, tvOS 13.0, watchOS 6.0, *)) { // Only use `os_proc_available_memory` if it appears to be working. // 0 is returned if the calling process is not an app or is running diff --git a/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag/Payload/BugsnagDevice+Private.h b/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag/Payload/BugsnagDevice+Private.h index 95b892673..dafd27da8 100644 --- a/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag/Payload/BugsnagDevice+Private.h +++ b/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag/Payload/BugsnagDevice+Private.h @@ -12,7 +12,7 @@ NS_ASSUME_NONNULL_BEGIN @interface BugsnagDevice () -+ (instancetype)deviceWithDictionary:(NSDictionary *)event; ++ (instancetype)deviceWithKSCrashReport:(NSDictionary *)event; + (instancetype)deserializeFromJson:(NSDictionary *)json; diff --git a/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag/Payload/BugsnagDevice.m b/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag/Payload/BugsnagDevice.m index 8a99d1c73..d1c124299 100644 --- a/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag/Payload/BugsnagDevice.m +++ b/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag/Payload/BugsnagDevice.m @@ -29,7 +29,7 @@ + (BugsnagDevice *)deserializeFromJson:(NSDictionary *)json { return device; } -+ (BugsnagDevice *)deviceWithDictionary:(NSDictionary *)event { ++ (BugsnagDevice *)deviceWithKSCrashReport:(NSDictionary *)event { BugsnagDevice *device = [BugsnagDevice new]; [self populateFields:device dictionary:event]; return device; diff --git a/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag/Payload/BugsnagDeviceWithState+Private.h b/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag/Payload/BugsnagDeviceWithState+Private.h index 107234986..9ed790a2c 100644 --- a/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag/Payload/BugsnagDeviceWithState+Private.h +++ b/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag/Payload/BugsnagDeviceWithState+Private.h @@ -16,7 +16,7 @@ NS_ASSUME_NONNULL_BEGIN + (instancetype)deviceFromJson:(NSDictionary *)json; -+ (instancetype)deviceWithDictionary:(NSDictionary *)event; ++ (instancetype)deviceWithKSCrashReport:(NSDictionary *)event; + (instancetype)deviceWithOomData:(NSDictionary *)data; diff --git a/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag/Payload/BugsnagDeviceWithState.m b/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag/Payload/BugsnagDeviceWithState.m index c9d94da0f..8482697e6 100644 --- a/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag/Payload/BugsnagDeviceWithState.m +++ b/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag/Payload/BugsnagDeviceWithState.m @@ -97,11 +97,11 @@ + (BugsnagDeviceWithState *)deviceWithOomData:(NSDictionary *)data { return device; } -+ (BugsnagDeviceWithState *)deviceWithDictionary:(NSDictionary *)event { ++ (BugsnagDeviceWithState *)deviceWithKSCrashReport:(NSDictionary *)event { BugsnagDeviceWithState *device = [BugsnagDeviceWithState new]; [self populateFields:device dictionary:event]; device.orientation = [event valueForKeyPath:@"user.state.deviceState.orientation"]; - device.freeMemory = [event valueForKeyPath:@"system.memory.free"] ?: [event valueForKeyPath:@"system.memory.usable"]; + device.freeMemory = [event valueForKeyPath:@"system.memory.free"]; device.freeDisk = BSGDeviceFreeSpace(NSCachesDirectory); NSString *val = [event valueForKeyPath:@"report.timestamp"]; diff --git a/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag/Payload/BugsnagEvent.m b/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag/Payload/BugsnagEvent.m index d907b6580..9d4a709d6 100644 --- a/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag/Payload/BugsnagEvent.m +++ b/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag/Payload/BugsnagEvent.m @@ -360,7 +360,7 @@ - (instancetype)initWithKSCrashData:(NSDictionary *)event { } } NSString *deviceAppHash = [event valueForKeyPath:@"system.device_app_hash"]; - BugsnagDeviceWithState *device = [BugsnagDeviceWithState deviceWithDictionary:event]; + BugsnagDeviceWithState *device = [BugsnagDeviceWithState deviceWithKSCrashReport:event]; BugsnagUser *user = [self parseUser:event deviceAppHash:deviceAppHash deviceId:device.id]; BugsnagConfiguration *config = [[BugsnagConfiguration alloc] initWithDictionaryRepresentation:[event valueForKeyPath:@"user.config"]]; BugsnagAppWithState *app = [BugsnagAppWithState appWithDictionary:event config:config codeBundleId:self.codeBundleId]; diff --git a/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag/Payload/BugsnagNotifier.m b/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag/Payload/BugsnagNotifier.m index 48db4fd3f..d600ea3a2 100644 --- a/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag/Payload/BugsnagNotifier.m +++ b/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag/Payload/BugsnagNotifier.m @@ -23,7 +23,7 @@ - (instancetype)init { #else self.name = @"Bugsnag Objective-C"; #endif - self.version = @"6.7.0"; + self.version = @"6.7.1"; self.url = @"https://github.com/bugsnag/bugsnag-cocoa"; self.dependencies = [NSMutableArray new]; } diff --git a/packages/react-native/ios/vendor/bugsnag-cocoa/CHANGELOG.md b/packages/react-native/ios/vendor/bugsnag-cocoa/CHANGELOG.md index 8ad88ba7f..62ca9f20f 100644 --- a/packages/react-native/ios/vendor/bugsnag-cocoa/CHANGELOG.md +++ b/packages/react-native/ios/vendor/bugsnag-cocoa/CHANGELOG.md @@ -1,6 +1,19 @@ Changelog ========= +## 6.7.1 (2021-03-10) + +### Bug fixes + +* Fix `os_proc_available_memory` runtime link error on Mac Catalyst. + [#1025](https://github.com/bugsnag/bugsnag-cocoa/pull/1025) + +* Fix missing `osName` and `osVersion` for errors reported from app extensions that do not link against UIKit. + [#1022](https://github.com/bugsnag/bugsnag-cocoa/pull/1022) + +* Fix incorrect `freeMemory` for errors reported via `notify()` + [#1021](https://github.com/bugsnag/bugsnag-cocoa/pull/1021) + ## 6.7.0 (2021-03-03) ### Enhancements diff --git a/packages/react-native/ios/vendor/bugsnag-cocoa/Framework/Info.plist b/packages/react-native/ios/vendor/bugsnag-cocoa/Framework/Info.plist index 427ffc62a..e6e276efb 100644 --- a/packages/react-native/ios/vendor/bugsnag-cocoa/Framework/Info.plist +++ b/packages/react-native/ios/vendor/bugsnag-cocoa/Framework/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 6.7.0 + 6.7.1 CFBundleVersion 1 diff --git a/packages/react-native/ios/vendor/bugsnag-cocoa/VERSION b/packages/react-native/ios/vendor/bugsnag-cocoa/VERSION index f0e13c509..06a765991 100644 --- a/packages/react-native/ios/vendor/bugsnag-cocoa/VERSION +++ b/packages/react-native/ios/vendor/bugsnag-cocoa/VERSION @@ -1 +1 @@ -6.7.0 +6.7.1 From 789646fa5601f695f135fdff84f8a829f78edf4a Mon Sep 17 00:00:00 2001 From: Ben Gourley Date: Thu, 11 Mar 2021 15:07:38 +0000 Subject: [PATCH 61/65] v7.9.0-alpha.0 --- lerna.json | 2 +- packages/browser/package-lock.json | 2 +- packages/browser/package.json | 38 +++++++++---------- packages/core/package-lock.json | 2 +- packages/core/package.json | 2 +- packages/delivery-expo/package-lock.json | 2 +- packages/delivery-expo/package.json | 4 +- packages/delivery-node/package-lock.json | 2 +- packages/delivery-node/package.json | 4 +- .../delivery-react-native/package-lock.json | 2 +- packages/delivery-react-native/package.json | 4 +- .../package-lock.json | 2 +- .../delivery-x-domain-request/package.json | 4 +- .../package-lock.json | 2 +- .../delivery-xml-http-request/package.json | 4 +- packages/expo/package-lock.json | 2 +- packages/expo/package.json | 28 +++++++------- packages/in-flight/package-lock.json | 2 +- packages/in-flight/package.json | 4 +- packages/js/package-lock.json | 2 +- packages/js/package.json | 6 +-- packages/node/package-lock.json | 2 +- packages/node/package.json | 26 ++++++------- packages/plugin-angular/package-lock.json | 2 +- packages/plugin-angular/package.json | 4 +- packages/plugin-app-duration/package.json | 4 +- packages/plugin-aws-lambda/package.json | 8 ++-- .../plugin-browser-context/package-lock.json | 2 +- packages/plugin-browser-context/package.json | 4 +- .../plugin-browser-device/package-lock.json | 2 +- packages/plugin-browser-device/package.json | 4 +- .../plugin-browser-request/package-lock.json | 2 +- packages/plugin-browser-request/package.json | 4 +- .../plugin-browser-session/package-lock.json | 2 +- packages/plugin-browser-session/package.json | 4 +- packages/plugin-client-ip/package-lock.json | 2 +- packages/plugin-client-ip/package.json | 4 +- .../package-lock.json | 2 +- .../plugin-console-breadcrumbs/package.json | 4 +- .../plugin-contextualize/package-lock.json | 2 +- packages/plugin-contextualize/package.json | 4 +- packages/plugin-expo-app/package-lock.json | 2 +- packages/plugin-expo-app/package.json | 4 +- packages/plugin-expo-device/package-lock.json | 2 +- packages/plugin-expo-device/package.json | 4 +- packages/plugin-express/package-lock.json | 2 +- packages/plugin-express/package.json | 4 +- .../package-lock.json | 2 +- .../plugin-inline-script-content/package.json | 4 +- .../package-lock.json | 2 +- .../package.json | 4 +- packages/plugin-intercept/package-lock.json | 2 +- packages/plugin-intercept/package.json | 4 +- packages/plugin-koa/package-lock.json | 2 +- packages/plugin-koa/package.json | 4 +- .../package-lock.json | 2 +- .../package.json | 4 +- .../package-lock.json | 2 +- .../plugin-network-breadcrumbs/package.json | 4 +- packages/plugin-node-device/package-lock.json | 2 +- packages/plugin-node-device/package.json | 4 +- .../plugin-node-in-project/package-lock.json | 2 +- packages/plugin-node-in-project/package.json | 4 +- .../package-lock.json | 2 +- .../plugin-node-surrounding-code/package.json | 4 +- .../package-lock.json | 2 +- .../package.json | 4 +- .../package-lock.json | 2 +- .../package.json | 4 +- .../package-lock.json | 2 +- .../package.json | 4 +- .../package-lock.json | 2 +- .../package.json | 4 +- .../package-lock.json | 2 +- .../package.json | 4 +- .../package-lock.json | 2 +- .../package.json | 4 +- .../package-lock.json | 2 +- .../package.json | 4 +- .../plugin-react-native-hermes/package.json | 4 +- .../package-lock.json | 2 +- .../package.json | 4 +- .../package-lock.json | 2 +- .../package.json | 4 +- .../package-lock.json | 2 +- .../plugin-react-native-session/package.json | 4 +- .../package-lock.json | 2 +- .../package.json | 4 +- .../plugin-react-navigation/package-lock.json | 2 +- packages/plugin-react-navigation/package.json | 4 +- packages/plugin-react/package-lock.json | 2 +- packages/plugin-react/package.json | 4 +- packages/plugin-restify/package-lock.json | 2 +- packages/plugin-restify/package.json | 4 +- .../plugin-server-session/package-lock.json | 2 +- packages/plugin-server-session/package.json | 4 +- .../plugin-simple-throttle/package-lock.json | 2 +- packages/plugin-simple-throttle/package.json | 4 +- .../package-lock.json | 2 +- .../plugin-strip-project-root/package.json | 4 +- .../package-lock.json | 2 +- .../plugin-strip-query-string/package.json | 4 +- packages/plugin-vue/package-lock.json | 2 +- packages/plugin-vue/package.json | 4 +- .../plugin-window-onerror/package-lock.json | 2 +- packages/plugin-window-onerror/package.json | 4 +- .../package-lock.json | 2 +- .../package.json | 4 +- packages/react-native-cli/package-lock.json | 2 +- packages/react-native-cli/package.json | 2 +- packages/react-native/package-lock.json | 2 +- packages/react-native/package.json | 24 ++++++------ 112 files changed, 220 insertions(+), 220 deletions(-) diff --git a/lerna.json b/lerna.json index 418141f3c..a22f53601 100644 --- a/lerna.json +++ b/lerna.json @@ -3,5 +3,5 @@ "packages": [ "packages/*" ], - "version": "7.8.2" + "version": "7.9.0-alpha.0" } diff --git a/packages/browser/package-lock.json b/packages/browser/package-lock.json index c2dab0f0e..70ff18526 100644 --- a/packages/browser/package-lock.json +++ b/packages/browser/package-lock.json @@ -1,6 +1,6 @@ { "name": "@bugsnag/browser", - "version": "7.7.0", + "version": "7.9.0-alpha.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/packages/browser/package.json b/packages/browser/package.json index 40387cf84..998031e6c 100644 --- a/packages/browser/package.json +++ b/packages/browser/package.json @@ -1,6 +1,6 @@ { "name": "@bugsnag/browser", - "version": "7.7.0", + "version": "7.9.0-alpha.0", "main": "dist/bugsnag.js", "types": "types/bugsnag.d.ts", "description": "Bugsnag error reporter for browser JavaScript", @@ -32,29 +32,29 @@ "license": "MIT", "devDependencies": { "@bugsnag/core": "^7.0.1", - "@bugsnag/delivery-x-domain-request": "^7.7.0", - "@bugsnag/delivery-xml-http-request": "^7.7.0", - "@bugsnag/plugin-app-duration": "^7.7.0", - "@bugsnag/plugin-browser-context": "^7.7.0", - "@bugsnag/plugin-browser-device": "^7.7.0", - "@bugsnag/plugin-browser-request": "^7.7.0", - "@bugsnag/plugin-browser-session": "^7.7.0", - "@bugsnag/plugin-client-ip": "^7.7.0", - "@bugsnag/plugin-console-breadcrumbs": "^7.7.0", - "@bugsnag/plugin-inline-script-content": "^7.7.0", - "@bugsnag/plugin-interaction-breadcrumbs": "^7.7.0", - "@bugsnag/plugin-navigation-breadcrumbs": "^7.7.0", - "@bugsnag/plugin-network-breadcrumbs": "^7.7.0", - "@bugsnag/plugin-simple-throttle": "^7.7.0", - "@bugsnag/plugin-strip-query-string": "^7.7.0", - "@bugsnag/plugin-window-onerror": "^7.7.0", - "@bugsnag/plugin-window-unhandled-rejection": "^7.7.0", + "@bugsnag/delivery-x-domain-request": "^7.9.0-alpha.0", + "@bugsnag/delivery-xml-http-request": "^7.9.0-alpha.0", + "@bugsnag/plugin-app-duration": "^7.9.0-alpha.0", + "@bugsnag/plugin-browser-context": "^7.9.0-alpha.0", + "@bugsnag/plugin-browser-device": "^7.9.0-alpha.0", + "@bugsnag/plugin-browser-request": "^7.9.0-alpha.0", + "@bugsnag/plugin-browser-session": "^7.9.0-alpha.0", + "@bugsnag/plugin-client-ip": "^7.9.0-alpha.0", + "@bugsnag/plugin-console-breadcrumbs": "^7.9.0-alpha.0", + "@bugsnag/plugin-inline-script-content": "^7.9.0-alpha.0", + "@bugsnag/plugin-interaction-breadcrumbs": "^7.9.0-alpha.0", + "@bugsnag/plugin-navigation-breadcrumbs": "^7.9.0-alpha.0", + "@bugsnag/plugin-network-breadcrumbs": "^7.9.0-alpha.0", + "@bugsnag/plugin-simple-throttle": "^7.9.0-alpha.0", + "@bugsnag/plugin-strip-query-string": "^7.9.0-alpha.0", + "@bugsnag/plugin-window-onerror": "^7.9.0-alpha.0", + "@bugsnag/plugin-window-unhandled-rejection": "^7.9.0-alpha.0", "cloudfront": "^0.4.1", "knox": "^0.9.2", "mime": "1.4.1", "semver": "^5.5.1" }, "dependencies": { - "@bugsnag/core": "^7.7.0" + "@bugsnag/core": "^7.9.0-alpha.0" } } diff --git a/packages/core/package-lock.json b/packages/core/package-lock.json index ab146cc11..8b06f3543 100644 --- a/packages/core/package-lock.json +++ b/packages/core/package-lock.json @@ -1,6 +1,6 @@ { "name": "@bugsnag/core", - "version": "7.7.0", + "version": "7.9.0-alpha.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/packages/core/package.json b/packages/core/package.json index 15493de74..e9bcb5e39 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,7 +1,7 @@ { "name": "@bugsnag/core", "main": "index.js", - "version": "7.7.0", + "version": "7.9.0-alpha.0", "types": "types/index.d.ts", "description": "Core classes and utilities for Bugsnag notifiers", "homepage": "https://www.bugsnag.com/", diff --git a/packages/delivery-expo/package-lock.json b/packages/delivery-expo/package-lock.json index e0ff54d5e..57f13dead 100644 --- a/packages/delivery-expo/package-lock.json +++ b/packages/delivery-expo/package-lock.json @@ -1,6 +1,6 @@ { "name": "@bugsnag/delivery-expo", - "version": "7.7.0", + "version": "7.9.0-alpha.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/packages/delivery-expo/package.json b/packages/delivery-expo/package.json index ab7e50e94..b4ac38987 100644 --- a/packages/delivery-expo/package.json +++ b/packages/delivery-expo/package.json @@ -1,6 +1,6 @@ { "name": "@bugsnag/delivery-expo", - "version": "7.7.0", + "version": "7.9.0-alpha.0", "main": "delivery.js", "description": "@bugsnag/js delivery mechanism to send events and sessions from Expo, using the FileSystem API to cache and retry sending failed payloads", "homepage": "https://www.bugsnag.com/", @@ -22,7 +22,7 @@ "expo-file-system": "~9.3.0" }, "devDependencies": { - "@bugsnag/core": "^7.7.0" + "@bugsnag/core": "^7.9.0-alpha.0" }, "peerDependencies": { "@bugsnag/core": "^7.0.0" diff --git a/packages/delivery-node/package-lock.json b/packages/delivery-node/package-lock.json index 4ff22c8c2..5990aa7ad 100644 --- a/packages/delivery-node/package-lock.json +++ b/packages/delivery-node/package-lock.json @@ -1,6 +1,6 @@ { "name": "@bugsnag/delivery-node", - "version": "7.7.0", + "version": "7.9.0-alpha.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/packages/delivery-node/package.json b/packages/delivery-node/package.json index 8fb41fe40..4e80ee3a5 100644 --- a/packages/delivery-node/package.json +++ b/packages/delivery-node/package.json @@ -1,6 +1,6 @@ { "name": "@bugsnag/delivery-node", - "version": "7.7.0", + "version": "7.9.0-alpha.0", "main": "delivery.js", "description": "@bugsnag/node delivery mechanism", "homepage": "https://www.bugsnag.com/", @@ -17,7 +17,7 @@ "author": "Bugsnag", "license": "MIT", "devDependencies": { - "@bugsnag/core": "^7.7.0" + "@bugsnag/core": "^7.9.0-alpha.0" }, "peerDependencies": { "@bugsnag/core": "^7.0.0" diff --git a/packages/delivery-react-native/package-lock.json b/packages/delivery-react-native/package-lock.json index 13ff3c3ea..0acb089e5 100644 --- a/packages/delivery-react-native/package-lock.json +++ b/packages/delivery-react-native/package-lock.json @@ -1,6 +1,6 @@ { "name": "@bugsnag/delivery-react-native", - "version": "7.8.0", + "version": "7.9.0-alpha.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/packages/delivery-react-native/package.json b/packages/delivery-react-native/package.json index 4d6442472..02a93f62b 100644 --- a/packages/delivery-react-native/package.json +++ b/packages/delivery-react-native/package.json @@ -1,6 +1,6 @@ { "name": "@bugsnag/delivery-react-native", - "version": "7.8.0", + "version": "7.9.0-alpha.0", "main": "delivery.js", "description": "@bugsnag/js delivery mechanism for React Native", "homepage": "https://www.bugsnag.com/", @@ -17,7 +17,7 @@ "author": "Bugsnag", "license": "MIT", "devDependencies": { - "@bugsnag/core": "^7.7.0" + "@bugsnag/core": "^7.9.0-alpha.0" }, "peerDependencies": { "@bugsnag/core": "^7.0.0" diff --git a/packages/delivery-x-domain-request/package-lock.json b/packages/delivery-x-domain-request/package-lock.json index 2d21c981f..54a28c871 100644 --- a/packages/delivery-x-domain-request/package-lock.json +++ b/packages/delivery-x-domain-request/package-lock.json @@ -1,6 +1,6 @@ { "name": "@bugsnag/delivery-x-domain-request", - "version": "7.7.0", + "version": "7.9.0-alpha.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/packages/delivery-x-domain-request/package.json b/packages/delivery-x-domain-request/package.json index 706779856..1f584b0b8 100644 --- a/packages/delivery-x-domain-request/package.json +++ b/packages/delivery-x-domain-request/package.json @@ -1,6 +1,6 @@ { "name": "@bugsnag/delivery-x-domain-request", - "version": "7.7.0", + "version": "7.9.0-alpha.0", "main": "delivery.js", "description": "@bugsnag/js delivery mechanism for IE 8, 9 and 10", "homepage": "https://www.bugsnag.com/", @@ -17,7 +17,7 @@ "author": "Bugsnag", "license": "MIT", "devDependencies": { - "@bugsnag/core": "^7.7.0" + "@bugsnag/core": "^7.9.0-alpha.0" }, "peerDependencies": { "@bugsnag/core": "^7.0.0" diff --git a/packages/delivery-xml-http-request/package-lock.json b/packages/delivery-xml-http-request/package-lock.json index c585b81b0..886b8439a 100644 --- a/packages/delivery-xml-http-request/package-lock.json +++ b/packages/delivery-xml-http-request/package-lock.json @@ -1,6 +1,6 @@ { "name": "@bugsnag/delivery-xml-http-request", - "version": "7.7.0", + "version": "7.9.0-alpha.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/packages/delivery-xml-http-request/package.json b/packages/delivery-xml-http-request/package.json index 18b5797ea..cbc66c282 100644 --- a/packages/delivery-xml-http-request/package.json +++ b/packages/delivery-xml-http-request/package.json @@ -1,6 +1,6 @@ { "name": "@bugsnag/delivery-xml-http-request", - "version": "7.7.0", + "version": "7.9.0-alpha.0", "main": "delivery.js", "description": "@bugsnag/js delivery mechanism for most browsers", "homepage": "https://www.bugsnag.com/", @@ -17,7 +17,7 @@ "author": "Bugsnag", "license": "MIT", "devDependencies": { - "@bugsnag/core": "^7.7.0" + "@bugsnag/core": "^7.9.0-alpha.0" }, "peerDependencies": { "@bugsnag/core": "^7.0.0" diff --git a/packages/expo/package-lock.json b/packages/expo/package-lock.json index c0399efe9..67caca971 100644 --- a/packages/expo/package-lock.json +++ b/packages/expo/package-lock.json @@ -1,6 +1,6 @@ { "name": "@bugsnag/expo", - "version": "7.8.2", + "version": "7.9.0-alpha.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/packages/expo/package.json b/packages/expo/package.json index b6906fb10..de1f296e2 100644 --- a/packages/expo/package.json +++ b/packages/expo/package.json @@ -1,6 +1,6 @@ { "name": "@bugsnag/expo", - "version": "7.8.2", + "version": "7.9.0-alpha.0", "main": "src/notifier.js", "types": "types/bugsnag.d.ts", "description": "Bugsnag error reporter for Expo applications", @@ -32,19 +32,19 @@ "author": "Bugsnag", "license": "MIT", "dependencies": { - "@bugsnag/core": "^7.7.0", - "@bugsnag/delivery-expo": "^7.7.0", - "@bugsnag/plugin-browser-session": "^7.7.0", - "@bugsnag/plugin-console-breadcrumbs": "^7.7.0", - "@bugsnag/plugin-expo-app": "^7.7.0", - "@bugsnag/plugin-expo-device": "^7.7.0", - "@bugsnag/plugin-network-breadcrumbs": "^7.7.0", - "@bugsnag/plugin-react": "^7.7.0", - "@bugsnag/plugin-react-native-app-state-breadcrumbs": "^7.7.0", - "@bugsnag/plugin-react-native-connectivity-breadcrumbs": "^7.8.2", - "@bugsnag/plugin-react-native-global-error-handler": "^7.7.0", - "@bugsnag/plugin-react-native-orientation-breadcrumbs": "^7.7.0", - "@bugsnag/plugin-react-native-unhandled-rejection": "^7.7.0", + "@bugsnag/core": "^7.9.0-alpha.0", + "@bugsnag/delivery-expo": "^7.9.0-alpha.0", + "@bugsnag/plugin-browser-session": "^7.9.0-alpha.0", + "@bugsnag/plugin-console-breadcrumbs": "^7.9.0-alpha.0", + "@bugsnag/plugin-expo-app": "^7.9.0-alpha.0", + "@bugsnag/plugin-expo-device": "^7.9.0-alpha.0", + "@bugsnag/plugin-network-breadcrumbs": "^7.9.0-alpha.0", + "@bugsnag/plugin-react": "^7.9.0-alpha.0", + "@bugsnag/plugin-react-native-app-state-breadcrumbs": "^7.9.0-alpha.0", + "@bugsnag/plugin-react-native-connectivity-breadcrumbs": "^7.9.0-alpha.0", + "@bugsnag/plugin-react-native-global-error-handler": "^7.9.0-alpha.0", + "@bugsnag/plugin-react-native-orientation-breadcrumbs": "^7.9.0-alpha.0", + "@bugsnag/plugin-react-native-unhandled-rejection": "^7.9.0-alpha.0", "@bugsnag/source-maps": "^1.0.1", "bugsnag-build-reporter": "^1.0.1", "expo-constants": "~9.3.3" diff --git a/packages/in-flight/package-lock.json b/packages/in-flight/package-lock.json index d0881cfb5..97d26ee77 100644 --- a/packages/in-flight/package-lock.json +++ b/packages/in-flight/package-lock.json @@ -1,6 +1,6 @@ { "name": "@bugsnag/in-flight", - "version": "7.7.0", + "version": "7.9.0-alpha.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/packages/in-flight/package.json b/packages/in-flight/package.json index 4e08fd75f..4e82979ec 100644 --- a/packages/in-flight/package.json +++ b/packages/in-flight/package.json @@ -1,6 +1,6 @@ { "name": "@bugsnag/in-flight", - "version": "7.7.0", + "version": "7.9.0-alpha.0", "main": "src/in-flight.js", "types": "types/bugsnag-in-flight.d.ts", "description": "Internal package to keep track of in-flight requests to Bugsnag", @@ -22,7 +22,7 @@ "@bugsnag/cuid": "^3.0.0" }, "devDependencies": { - "@bugsnag/core": "^7.7.0" + "@bugsnag/core": "^7.9.0-alpha.0" }, "peerDependencies": { "@bugsnag/core": "^7.0.0" diff --git a/packages/js/package-lock.json b/packages/js/package-lock.json index 5be2a0eed..b084d221f 100644 --- a/packages/js/package-lock.json +++ b/packages/js/package-lock.json @@ -1,6 +1,6 @@ { "name": "@bugsnag/js", - "version": "7.8.0", + "version": "7.9.0-alpha.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/packages/js/package.json b/packages/js/package.json index 6dfd2b803..cc5e1b69f 100644 --- a/packages/js/package.json +++ b/packages/js/package.json @@ -1,6 +1,6 @@ { "name": "@bugsnag/js", - "version": "7.8.0", + "version": "7.9.0-alpha.0", "main": "node/notifier.js", "browser": "browser/notifier.js", "types": "types.d.ts", @@ -33,8 +33,8 @@ "author": "Bugsnag", "license": "MIT", "dependencies": { - "@bugsnag/browser": "^7.7.0", - "@bugsnag/node": "^7.8.0" + "@bugsnag/browser": "^7.9.0-alpha.0", + "@bugsnag/node": "^7.9.0-alpha.0" }, "devDependencies": { "@babel/cli": "^7.0.0" diff --git a/packages/node/package-lock.json b/packages/node/package-lock.json index cd1c08a0f..aa2a22e94 100644 --- a/packages/node/package-lock.json +++ b/packages/node/package-lock.json @@ -1,6 +1,6 @@ { "name": "@bugsnag/node", - "version": "7.8.0", + "version": "7.9.0-alpha.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/packages/node/package.json b/packages/node/package.json index 47563decb..19bda5eca 100644 --- a/packages/node/package.json +++ b/packages/node/package.json @@ -1,6 +1,6 @@ { "name": "@bugsnag/node", - "version": "7.8.0", + "version": "7.9.0-alpha.0", "main": "dist/bugsnag.js", "types": "types/bugsnag.d.ts", "description": "Bugsnag error reporter for Node.js", @@ -25,20 +25,20 @@ "author": "Bugsnag", "license": "MIT", "devDependencies": { - "@bugsnag/delivery-node": "^7.7.0", - "@bugsnag/plugin-app-duration": "^7.7.0", - "@bugsnag/plugin-contextualize": "^7.7.0", - "@bugsnag/plugin-intercept": "^7.7.0", - "@bugsnag/plugin-node-device": "^7.7.0", - "@bugsnag/plugin-node-in-project": "^7.7.0", - "@bugsnag/plugin-node-surrounding-code": "^7.7.0", - "@bugsnag/plugin-node-uncaught-exception": "^7.7.0", - "@bugsnag/plugin-node-unhandled-rejection": "^7.7.0", - "@bugsnag/plugin-server-session": "^7.7.0", - "@bugsnag/plugin-strip-project-root": "^7.7.0" + "@bugsnag/delivery-node": "^7.9.0-alpha.0", + "@bugsnag/plugin-app-duration": "^7.9.0-alpha.0", + "@bugsnag/plugin-contextualize": "^7.9.0-alpha.0", + "@bugsnag/plugin-intercept": "^7.9.0-alpha.0", + "@bugsnag/plugin-node-device": "^7.9.0-alpha.0", + "@bugsnag/plugin-node-in-project": "^7.9.0-alpha.0", + "@bugsnag/plugin-node-surrounding-code": "^7.9.0-alpha.0", + "@bugsnag/plugin-node-uncaught-exception": "^7.9.0-alpha.0", + "@bugsnag/plugin-node-unhandled-rejection": "^7.9.0-alpha.0", + "@bugsnag/plugin-server-session": "^7.9.0-alpha.0", + "@bugsnag/plugin-strip-project-root": "^7.9.0-alpha.0" }, "dependencies": { - "@bugsnag/core": "^7.7.0", + "@bugsnag/core": "^7.9.0-alpha.0", "byline": "^5.0.0", "error-stack-parser": "^2.0.2", "iserror": "^0.0.2", diff --git a/packages/plugin-angular/package-lock.json b/packages/plugin-angular/package-lock.json index d4f83e1f1..84ec28b7e 100644 --- a/packages/plugin-angular/package-lock.json +++ b/packages/plugin-angular/package-lock.json @@ -1,6 +1,6 @@ { "name": "@bugsnag/plugin-angular", - "version": "7.8.0", + "version": "7.9.0-alpha.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/packages/plugin-angular/package.json b/packages/plugin-angular/package.json index f73d18563..22d81491a 100644 --- a/packages/plugin-angular/package.json +++ b/packages/plugin-angular/package.json @@ -1,6 +1,6 @@ { "name": "@bugsnag/plugin-angular", - "version": "7.8.0", + "version": "7.9.0-alpha.0", "description": "Angular integration for bugsnag-js", "main": "dist/esm5/index.js", "browser": "dist/esm5/index.js", @@ -35,7 +35,7 @@ "@angular/compiler": "^7.2.15", "@angular/compiler-cli": "^7.2.15", "@angular/core": "^7.2.15", - "@bugsnag/js": "^7.8.0", + "@bugsnag/js": "^7.9.0-alpha.0", "rxjs": "^5.5.8", "typescript": "^3.2.4", "zone.js": "^0.8.26" diff --git a/packages/plugin-app-duration/package.json b/packages/plugin-app-duration/package.json index 14c09896c..47ad15912 100644 --- a/packages/plugin-app-duration/package.json +++ b/packages/plugin-app-duration/package.json @@ -1,6 +1,6 @@ { "name": "@bugsnag/plugin-app-duration", - "version": "7.7.0", + "version": "7.9.0-alpha.0", "main": "app.js", "description": "@bugsnag/js plugin to set app duration in browsers and node", "homepage": "https://www.bugsnag.com/", @@ -17,7 +17,7 @@ "author": "Bugsnag", "license": "MIT", "devDependencies": { - "@bugsnag/core": "^7.7.0" + "@bugsnag/core": "^7.9.0-alpha.0" }, "peerDependencies": { "@bugsnag/core": "^7.0.0" diff --git a/packages/plugin-aws-lambda/package.json b/packages/plugin-aws-lambda/package.json index e289aafe5..3d9db1a63 100644 --- a/packages/plugin-aws-lambda/package.json +++ b/packages/plugin-aws-lambda/package.json @@ -1,6 +1,6 @@ { "name": "@bugsnag/plugin-aws-lambda", - "version": "7.7.0", + "version": "7.9.0-alpha.0", "main": "dist/bugsnag-aws-lambda.js", "types": "types/bugsnag-plugin-aws-lambda.d.ts", "description": "AWS Lambda support for @bugsnag/node", @@ -24,11 +24,11 @@ "author": "Bugsnag", "license": "MIT", "dependencies": { - "@bugsnag/in-flight": "^7.7.0", - "@bugsnag/plugin-browser-session": "^7.7.0" + "@bugsnag/in-flight": "^7.9.0-alpha.0", + "@bugsnag/plugin-browser-session": "^7.9.0-alpha.0" }, "devDependencies": { - "@bugsnag/core": "^7.7.0" + "@bugsnag/core": "^7.9.0-alpha.0" }, "peerDependencies": { "@bugsnag/core": "^7.0.0" diff --git a/packages/plugin-browser-context/package-lock.json b/packages/plugin-browser-context/package-lock.json index 7539db5c8..16849b23c 100644 --- a/packages/plugin-browser-context/package-lock.json +++ b/packages/plugin-browser-context/package-lock.json @@ -1,6 +1,6 @@ { "name": "@bugsnag/plugin-browser-context", - "version": "7.7.0", + "version": "7.9.0-alpha.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/packages/plugin-browser-context/package.json b/packages/plugin-browser-context/package.json index e20289040..aad6a72d9 100644 --- a/packages/plugin-browser-context/package.json +++ b/packages/plugin-browser-context/package.json @@ -1,6 +1,6 @@ { "name": "@bugsnag/plugin-browser-context", - "version": "7.7.0", + "version": "7.9.0-alpha.0", "main": "context.js", "description": "@bugsnag/js plugin to set event context in browsers", "homepage": "https://www.bugsnag.com/", @@ -17,7 +17,7 @@ "author": "Bugsnag", "license": "MIT", "devDependencies": { - "@bugsnag/core": "^7.7.0" + "@bugsnag/core": "^7.9.0-alpha.0" }, "peerDependencies": { "@bugsnag/core": "^7.0.0" diff --git a/packages/plugin-browser-device/package-lock.json b/packages/plugin-browser-device/package-lock.json index 0c088eace..7ad37cda2 100644 --- a/packages/plugin-browser-device/package-lock.json +++ b/packages/plugin-browser-device/package-lock.json @@ -1,6 +1,6 @@ { "name": "@bugsnag/plugin-browser-device", - "version": "7.7.0", + "version": "7.9.0-alpha.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/packages/plugin-browser-device/package.json b/packages/plugin-browser-device/package.json index b9e72d057..b8e1481dd 100644 --- a/packages/plugin-browser-device/package.json +++ b/packages/plugin-browser-device/package.json @@ -1,6 +1,6 @@ { "name": "@bugsnag/plugin-browser-device", - "version": "7.7.0", + "version": "7.9.0-alpha.0", "main": "device.js", "description": "@bugsnag/js plugin to set device info in browsers", "homepage": "https://www.bugsnag.com/", @@ -20,7 +20,7 @@ "@bugsnag/cuid": "^3.0.0" }, "devDependencies": { - "@bugsnag/core": "^7.7.0" + "@bugsnag/core": "^7.9.0-alpha.0" }, "peerDependencies": { "@bugsnag/core": "^7.0.0" diff --git a/packages/plugin-browser-request/package-lock.json b/packages/plugin-browser-request/package-lock.json index 5cf4f8fdb..ef1334a92 100644 --- a/packages/plugin-browser-request/package-lock.json +++ b/packages/plugin-browser-request/package-lock.json @@ -1,6 +1,6 @@ { "name": "@bugsnag/plugin-browser-request", - "version": "7.7.0", + "version": "7.9.0-alpha.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/packages/plugin-browser-request/package.json b/packages/plugin-browser-request/package.json index b1e2f847a..e4bcd8425 100644 --- a/packages/plugin-browser-request/package.json +++ b/packages/plugin-browser-request/package.json @@ -1,6 +1,6 @@ { "name": "@bugsnag/plugin-browser-request", - "version": "7.7.0", + "version": "7.9.0-alpha.0", "main": "request.js", "description": "@bugsnag/js plugin to set request info in browsers", "homepage": "https://www.bugsnag.com/", @@ -18,7 +18,7 @@ "author": "Bugsnag", "license": "MIT", "devDependencies": { - "@bugsnag/core": "^7.7.0" + "@bugsnag/core": "^7.9.0-alpha.0" }, "peerDependencies": { "@bugsnag/core": "^7.0.0" diff --git a/packages/plugin-browser-session/package-lock.json b/packages/plugin-browser-session/package-lock.json index 996ab94c5..dab8cf003 100644 --- a/packages/plugin-browser-session/package-lock.json +++ b/packages/plugin-browser-session/package-lock.json @@ -1,6 +1,6 @@ { "name": "@bugsnag/plugin-browser-session", - "version": "7.7.0", + "version": "7.9.0-alpha.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/packages/plugin-browser-session/package.json b/packages/plugin-browser-session/package.json index 314698deb..e1742a078 100644 --- a/packages/plugin-browser-session/package.json +++ b/packages/plugin-browser-session/package.json @@ -1,6 +1,6 @@ { "name": "@bugsnag/plugin-browser-session", - "version": "7.7.0", + "version": "7.9.0-alpha.0", "main": "session.js", "description": "@bugsnag/js plugin to enable session tracking in browsers", "homepage": "https://www.bugsnag.com/", @@ -17,7 +17,7 @@ "author": "Bugsnag", "license": "MIT", "devDependencies": { - "@bugsnag/core": "^7.7.0" + "@bugsnag/core": "^7.9.0-alpha.0" }, "peerDependencies": { "@bugsnag/core": "^7.0.0" diff --git a/packages/plugin-client-ip/package-lock.json b/packages/plugin-client-ip/package-lock.json index 6d6d36cef..e91138714 100644 --- a/packages/plugin-client-ip/package-lock.json +++ b/packages/plugin-client-ip/package-lock.json @@ -1,6 +1,6 @@ { "name": "@bugsnag/plugin-client-ip", - "version": "7.7.0", + "version": "7.9.0-alpha.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/packages/plugin-client-ip/package.json b/packages/plugin-client-ip/package.json index 222f22699..972eb80bf 100644 --- a/packages/plugin-client-ip/package.json +++ b/packages/plugin-client-ip/package.json @@ -1,6 +1,6 @@ { "name": "@bugsnag/plugin-client-ip", - "version": "7.7.0", + "version": "7.9.0-alpha.0", "main": "client-ip.js", "description": "@bugsnag/js plugin to disable client IP from error reports", "homepage": "https://www.bugsnag.com/", @@ -17,7 +17,7 @@ "author": "Bugsnag", "license": "MIT", "devDependencies": { - "@bugsnag/core": "^7.7.0" + "@bugsnag/core": "^7.9.0-alpha.0" }, "peerDependencies": { "@bugsnag/core": "^7.0.0" diff --git a/packages/plugin-console-breadcrumbs/package-lock.json b/packages/plugin-console-breadcrumbs/package-lock.json index ad6d2ade2..cf02dba3a 100644 --- a/packages/plugin-console-breadcrumbs/package-lock.json +++ b/packages/plugin-console-breadcrumbs/package-lock.json @@ -1,6 +1,6 @@ { "name": "@bugsnag/plugin-console-breadcrumbs", - "version": "7.7.0", + "version": "7.9.0-alpha.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/packages/plugin-console-breadcrumbs/package.json b/packages/plugin-console-breadcrumbs/package.json index b776e58a2..1a07d9dfa 100644 --- a/packages/plugin-console-breadcrumbs/package.json +++ b/packages/plugin-console-breadcrumbs/package.json @@ -1,6 +1,6 @@ { "name": "@bugsnag/plugin-console-breadcrumbs", - "version": "7.7.0", + "version": "7.9.0-alpha.0", "main": "console-breadcrumbs.js", "description": "@bugsnag/js plugin to record console log method calls as breadcrumbs", "homepage": "https://www.bugsnag.com/", @@ -17,7 +17,7 @@ "author": "Bugsnag", "license": "MIT", "devDependencies": { - "@bugsnag/core": "^7.7.0" + "@bugsnag/core": "^7.9.0-alpha.0" }, "peerDependencies": { "@bugsnag/core": "^7.0.0" diff --git a/packages/plugin-contextualize/package-lock.json b/packages/plugin-contextualize/package-lock.json index 4f42c352e..5c1ed5609 100644 --- a/packages/plugin-contextualize/package-lock.json +++ b/packages/plugin-contextualize/package-lock.json @@ -1,6 +1,6 @@ { "name": "@bugsnag/plugin-contextualize", - "version": "7.7.0", + "version": "7.9.0-alpha.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/packages/plugin-contextualize/package.json b/packages/plugin-contextualize/package.json index fc3b09146..437d6173e 100644 --- a/packages/plugin-contextualize/package.json +++ b/packages/plugin-contextualize/package.json @@ -1,6 +1,6 @@ { "name": "@bugsnag/plugin-contextualize", - "version": "7.7.0", + "version": "7.9.0-alpha.0", "main": "contextualize.js", "description": "@bugsnag/js plugin to add context to unhandled events", "homepage": "https://www.bugsnag.com/", @@ -17,7 +17,7 @@ "author": "Bugsnag", "license": "MIT", "devDependencies": { - "@bugsnag/core": "^7.7.0" + "@bugsnag/core": "^7.9.0-alpha.0" }, "peerDependencies": { "@bugsnag/core": "^7.0.0" diff --git a/packages/plugin-expo-app/package-lock.json b/packages/plugin-expo-app/package-lock.json index 2da41aa54..290de4203 100644 --- a/packages/plugin-expo-app/package-lock.json +++ b/packages/plugin-expo-app/package-lock.json @@ -1,6 +1,6 @@ { "name": "@bugsnag/plugin-expo-app", - "version": "7.7.0", + "version": "7.9.0-alpha.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/packages/plugin-expo-app/package.json b/packages/plugin-expo-app/package.json index 25e0296d6..b961bfeeb 100644 --- a/packages/plugin-expo-app/package.json +++ b/packages/plugin-expo-app/package.json @@ -1,6 +1,6 @@ { "name": "@bugsnag/plugin-expo-app", - "version": "7.7.0", + "version": "7.9.0-alpha.0", "main": "app.js", "description": "@bugsnag/js plugin to provide information about an Expo app", "homepage": "https://www.bugsnag.com/", @@ -17,7 +17,7 @@ "author": "Bugsnag", "license": "MIT", "devDependencies": { - "@bugsnag/core": "^7.7.0" + "@bugsnag/core": "^7.9.0-alpha.0" }, "dependencies": { "expo-constants": "~9.3.3" diff --git a/packages/plugin-expo-device/package-lock.json b/packages/plugin-expo-device/package-lock.json index 612e592a3..47d275754 100644 --- a/packages/plugin-expo-device/package-lock.json +++ b/packages/plugin-expo-device/package-lock.json @@ -1,6 +1,6 @@ { "name": "@bugsnag/plugin-expo-device", - "version": "7.7.0", + "version": "7.9.0-alpha.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/packages/plugin-expo-device/package.json b/packages/plugin-expo-device/package.json index 03f2e2512..78100b3a1 100644 --- a/packages/plugin-expo-device/package.json +++ b/packages/plugin-expo-device/package.json @@ -1,6 +1,6 @@ { "name": "@bugsnag/plugin-expo-device", - "version": "7.7.0", + "version": "7.9.0-alpha.0", "main": "device.js", "description": "@bugsnag/js plugin to attach Expo-specific device info to events", "homepage": "https://www.bugsnag.com/", @@ -17,7 +17,7 @@ "author": "Bugsnag", "license": "MIT", "devDependencies": { - "@bugsnag/core": "^7.7.0" + "@bugsnag/core": "^7.9.0-alpha.0" }, "dependencies": { "expo-constants": "~9.3.3", diff --git a/packages/plugin-express/package-lock.json b/packages/plugin-express/package-lock.json index 298c87a62..75ea03e78 100644 --- a/packages/plugin-express/package-lock.json +++ b/packages/plugin-express/package-lock.json @@ -1,6 +1,6 @@ { "name": "@bugsnag/plugin-express", - "version": "7.7.0", + "version": "7.9.0-alpha.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/packages/plugin-express/package.json b/packages/plugin-express/package.json index 3d7a2cdb0..0eba31ca3 100644 --- a/packages/plugin-express/package.json +++ b/packages/plugin-express/package.json @@ -1,6 +1,6 @@ { "name": "@bugsnag/plugin-express", - "version": "7.7.0", + "version": "7.9.0-alpha.0", "main": "dist/bugsnag-express.js", "types": "types/bugsnag-express.d.ts", "description": "@bugsnag/js error handling middleware for Express (and Connect) web servers", @@ -28,7 +28,7 @@ "@bugsnag/core": "^7.0.0" }, "devDependencies": { - "@bugsnag/core": "^7.7.0", + "@bugsnag/core": "^7.9.0-alpha.0", "@types/express": "^4.17.6" }, "dependencies": { diff --git a/packages/plugin-inline-script-content/package-lock.json b/packages/plugin-inline-script-content/package-lock.json index af9fa5444..7b5bbf234 100644 --- a/packages/plugin-inline-script-content/package-lock.json +++ b/packages/plugin-inline-script-content/package-lock.json @@ -1,6 +1,6 @@ { "name": "@bugsnag/plugin-inline-script-content", - "version": "7.7.0", + "version": "7.9.0-alpha.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/packages/plugin-inline-script-content/package.json b/packages/plugin-inline-script-content/package.json index 30b0bb6ac..8cb077ae1 100644 --- a/packages/plugin-inline-script-content/package.json +++ b/packages/plugin-inline-script-content/package.json @@ -1,6 +1,6 @@ { "name": "@bugsnag/plugin-inline-script-content", - "version": "7.7.0", + "version": "7.9.0-alpha.0", "main": "inline-script-content.js", "description": "@bugsnag/js plugin to attach inline script content to error events", "homepage": "https://www.bugsnag.com/", @@ -18,7 +18,7 @@ "author": "Bugsnag", "license": "MIT", "devDependencies": { - "@bugsnag/core": "^7.7.0" + "@bugsnag/core": "^7.9.0-alpha.0" }, "peerDependencies": { "@bugsnag/core": "^7.0.0" diff --git a/packages/plugin-interaction-breadcrumbs/package-lock.json b/packages/plugin-interaction-breadcrumbs/package-lock.json index 36a79c59f..599ac7cc3 100644 --- a/packages/plugin-interaction-breadcrumbs/package-lock.json +++ b/packages/plugin-interaction-breadcrumbs/package-lock.json @@ -1,6 +1,6 @@ { "name": "@bugsnag/plugin-interaction-breadcrumbs", - "version": "7.7.0", + "version": "7.9.0-alpha.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/packages/plugin-interaction-breadcrumbs/package.json b/packages/plugin-interaction-breadcrumbs/package.json index e9ad63b8e..8dc9f881d 100644 --- a/packages/plugin-interaction-breadcrumbs/package.json +++ b/packages/plugin-interaction-breadcrumbs/package.json @@ -1,6 +1,6 @@ { "name": "@bugsnag/plugin-interaction-breadcrumbs", - "version": "7.7.0", + "version": "7.9.0-alpha.0", "main": "interaction-breadcrumbs.js", "description": "@bugsnag/js plugin to record UI click events as breadcrumbs", "homepage": "https://www.bugsnag.com/", @@ -18,7 +18,7 @@ "author": "Bugsnag", "license": "MIT", "devDependencies": { - "@bugsnag/core": "^7.7.0" + "@bugsnag/core": "^7.9.0-alpha.0" }, "peerDependencies": { "@bugsnag/core": "^7.0.0" diff --git a/packages/plugin-intercept/package-lock.json b/packages/plugin-intercept/package-lock.json index a0f0218d3..1d32d2078 100644 --- a/packages/plugin-intercept/package-lock.json +++ b/packages/plugin-intercept/package-lock.json @@ -1,6 +1,6 @@ { "name": "@bugsnag/plugin-intercept", - "version": "7.7.0", + "version": "7.9.0-alpha.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/packages/plugin-intercept/package.json b/packages/plugin-intercept/package.json index f5f6269c1..dd43c2958 100644 --- a/packages/plugin-intercept/package.json +++ b/packages/plugin-intercept/package.json @@ -1,6 +1,6 @@ { "name": "@bugsnag/plugin-intercept", - "version": "7.7.0", + "version": "7.9.0-alpha.0", "main": "intercept.js", "description": "@bugsnag/js plugin providing convenience functions for intercepting asynchronous errors", "homepage": "https://www.bugsnag.com/", @@ -18,7 +18,7 @@ "author": "Bugsnag", "license": "MIT", "devDependencies": { - "@bugsnag/core": "^7.7.0" + "@bugsnag/core": "^7.9.0-alpha.0" }, "peerDependencies": { "@bugsnag/core": "^7.0.0" diff --git a/packages/plugin-koa/package-lock.json b/packages/plugin-koa/package-lock.json index 2b9f7cc24..288e5460e 100644 --- a/packages/plugin-koa/package-lock.json +++ b/packages/plugin-koa/package-lock.json @@ -1,6 +1,6 @@ { "name": "@bugsnag/plugin-koa", - "version": "7.7.0", + "version": "7.9.0-alpha.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/packages/plugin-koa/package.json b/packages/plugin-koa/package.json index ce51fa615..9ac786de0 100644 --- a/packages/plugin-koa/package.json +++ b/packages/plugin-koa/package.json @@ -1,6 +1,6 @@ { "name": "@bugsnag/plugin-koa", - "version": "7.7.0", + "version": "7.9.0-alpha.0", "main": "dist/bugsnag-koa.js", "types": "types/bugsnag-koa.d.ts", "description": "@bugsnag/js error handling middleware for Koa web servers", @@ -28,7 +28,7 @@ "@bugsnag/core": "^7.0.0" }, "devDependencies": { - "@bugsnag/core": "^7.7.0", + "@bugsnag/core": "^7.9.0-alpha.0", "@types/koa": "^2.11.3" }, "dependencies": { diff --git a/packages/plugin-navigation-breadcrumbs/package-lock.json b/packages/plugin-navigation-breadcrumbs/package-lock.json index c76c11e32..4e3c41fe1 100644 --- a/packages/plugin-navigation-breadcrumbs/package-lock.json +++ b/packages/plugin-navigation-breadcrumbs/package-lock.json @@ -1,6 +1,6 @@ { "name": "@bugsnag/plugin-navigation-breadcrumbs", - "version": "7.7.0", + "version": "7.9.0-alpha.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/packages/plugin-navigation-breadcrumbs/package.json b/packages/plugin-navigation-breadcrumbs/package.json index 6cc3a821c..29b88804d 100644 --- a/packages/plugin-navigation-breadcrumbs/package.json +++ b/packages/plugin-navigation-breadcrumbs/package.json @@ -1,6 +1,6 @@ { "name": "@bugsnag/plugin-navigation-breadcrumbs", - "version": "7.7.0", + "version": "7.9.0-alpha.0", "main": "navigation-breadcrumbs.js", "description": "@bugsnag/js plugin to record browser navigation as breadcrumbs", "homepage": "https://www.bugsnag.com/", @@ -17,7 +17,7 @@ "author": "Bugsnag", "license": "MIT", "devDependencies": { - "@bugsnag/core": "^7.7.0" + "@bugsnag/core": "^7.9.0-alpha.0" }, "peerDependencies": { "@bugsnag/core": "^7.0.0" diff --git a/packages/plugin-network-breadcrumbs/package-lock.json b/packages/plugin-network-breadcrumbs/package-lock.json index 07dec4577..94c211264 100644 --- a/packages/plugin-network-breadcrumbs/package-lock.json +++ b/packages/plugin-network-breadcrumbs/package-lock.json @@ -1,6 +1,6 @@ { "name": "@bugsnag/plugin-network-breadcrumbs", - "version": "7.7.0", + "version": "7.9.0-alpha.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/packages/plugin-network-breadcrumbs/package.json b/packages/plugin-network-breadcrumbs/package.json index ebacd5970..faf63af9e 100644 --- a/packages/plugin-network-breadcrumbs/package.json +++ b/packages/plugin-network-breadcrumbs/package.json @@ -1,6 +1,6 @@ { "name": "@bugsnag/plugin-network-breadcrumbs", - "version": "7.7.0", + "version": "7.9.0-alpha.0", "main": "network-breadcrumbs.js", "description": "@bugsnag/js plugin to record browser requests as breadcrumbs", "homepage": "https://www.bugsnag.com/", @@ -17,7 +17,7 @@ "author": "Bugsnag", "license": "MIT", "devDependencies": { - "@bugsnag/core": "^7.7.0" + "@bugsnag/core": "^7.9.0-alpha.0" }, "peerDependencies": { "@bugsnag/core": "^7.0.0" diff --git a/packages/plugin-node-device/package-lock.json b/packages/plugin-node-device/package-lock.json index 2723bcf12..cf3b77806 100644 --- a/packages/plugin-node-device/package-lock.json +++ b/packages/plugin-node-device/package-lock.json @@ -1,6 +1,6 @@ { "name": "@bugsnag/plugin-node-device", - "version": "7.7.0", + "version": "7.9.0-alpha.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/packages/plugin-node-device/package.json b/packages/plugin-node-device/package.json index 2fe437b5f..3cf1f798e 100644 --- a/packages/plugin-node-device/package.json +++ b/packages/plugin-node-device/package.json @@ -1,6 +1,6 @@ { "name": "@bugsnag/plugin-node-device", - "version": "7.7.0", + "version": "7.9.0-alpha.0", "main": "device.js", "description": "@bugsnag/js plugin to set device info in node", "homepage": "https://www.bugsnag.com/", @@ -18,7 +18,7 @@ "author": "Bugsnag", "license": "MIT", "devDependencies": { - "@bugsnag/core": "^7.7.0" + "@bugsnag/core": "^7.9.0-alpha.0" }, "peerDependencies": { "@bugsnag/core": "^7.0.0" diff --git a/packages/plugin-node-in-project/package-lock.json b/packages/plugin-node-in-project/package-lock.json index ee4b722aa..7b7fce092 100644 --- a/packages/plugin-node-in-project/package-lock.json +++ b/packages/plugin-node-in-project/package-lock.json @@ -1,6 +1,6 @@ { "name": "@bugsnag/plugin-node-in-project", - "version": "7.7.0", + "version": "7.9.0-alpha.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/packages/plugin-node-in-project/package.json b/packages/plugin-node-in-project/package.json index 71ab00b5a..e99ecdbd1 100644 --- a/packages/plugin-node-in-project/package.json +++ b/packages/plugin-node-in-project/package.json @@ -1,6 +1,6 @@ { "name": "@bugsnag/plugin-node-in-project", - "version": "7.7.0", + "version": "7.9.0-alpha.0", "main": "in-project.js", "description": "@bugsnag/js plugin to mark wether stackframes are 'in-project'", "homepage": "https://www.bugsnag.com/", @@ -18,7 +18,7 @@ "author": "Bugsnag", "license": "MIT", "devDependencies": { - "@bugsnag/core": "^7.7.0" + "@bugsnag/core": "^7.9.0-alpha.0" }, "peerDependencies": { "@bugsnag/core": "^7.0.0" diff --git a/packages/plugin-node-surrounding-code/package-lock.json b/packages/plugin-node-surrounding-code/package-lock.json index d48bba498..21f969b6d 100644 --- a/packages/plugin-node-surrounding-code/package-lock.json +++ b/packages/plugin-node-surrounding-code/package-lock.json @@ -1,6 +1,6 @@ { "name": "@bugsnag/plugin-node-surrounding-code", - "version": "7.7.0", + "version": "7.9.0-alpha.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/packages/plugin-node-surrounding-code/package.json b/packages/plugin-node-surrounding-code/package.json index 6b1c3cce4..7d7a482ba 100644 --- a/packages/plugin-node-surrounding-code/package.json +++ b/packages/plugin-node-surrounding-code/package.json @@ -1,6 +1,6 @@ { "name": "@bugsnag/plugin-node-surrounding-code", - "version": "7.7.0", + "version": "7.9.0-alpha.0", "main": "surrounding-code.js", "description": "@bugsnag/js plugin to load surrounding code in Node stacktraces", "homepage": "https://www.bugsnag.com/", @@ -22,7 +22,7 @@ "pump": "^3.0.0" }, "devDependencies": { - "@bugsnag/core": "^7.7.0" + "@bugsnag/core": "^7.9.0-alpha.0" }, "peerDependencies": { "@bugsnag/core": "^7.0.0" diff --git a/packages/plugin-node-uncaught-exception/package-lock.json b/packages/plugin-node-uncaught-exception/package-lock.json index c089247d3..9f77c3fd2 100644 --- a/packages/plugin-node-uncaught-exception/package-lock.json +++ b/packages/plugin-node-uncaught-exception/package-lock.json @@ -1,6 +1,6 @@ { "name": "@bugsnag/plugin-node-uncaught-exception", - "version": "7.7.0", + "version": "7.9.0-alpha.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/packages/plugin-node-uncaught-exception/package.json b/packages/plugin-node-uncaught-exception/package.json index 7dd690506..3acbc3fbb 100644 --- a/packages/plugin-node-uncaught-exception/package.json +++ b/packages/plugin-node-uncaught-exception/package.json @@ -1,6 +1,6 @@ { "name": "@bugsnag/plugin-node-uncaught-exception", - "version": "7.7.0", + "version": "7.9.0-alpha.0", "main": "uncaught-exception.js", "description": "@bugsnag/js plugin to capture and report uncaught exceptions", "homepage": "https://www.bugsnag.com/", @@ -18,7 +18,7 @@ "author": "Bugsnag", "license": "MIT", "devDependencies": { - "@bugsnag/core": "^7.7.0" + "@bugsnag/core": "^7.9.0-alpha.0" }, "peerDependencies": { "@bugsnag/core": "^7.0.0" diff --git a/packages/plugin-node-unhandled-rejection/package-lock.json b/packages/plugin-node-unhandled-rejection/package-lock.json index 0f60f73e2..91380e55b 100644 --- a/packages/plugin-node-unhandled-rejection/package-lock.json +++ b/packages/plugin-node-unhandled-rejection/package-lock.json @@ -1,6 +1,6 @@ { "name": "@bugsnag/plugin-node-unhandled-rejection", - "version": "7.7.0", + "version": "7.9.0-alpha.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/packages/plugin-node-unhandled-rejection/package.json b/packages/plugin-node-unhandled-rejection/package.json index 51ad2d25d..ac8c5a85f 100644 --- a/packages/plugin-node-unhandled-rejection/package.json +++ b/packages/plugin-node-unhandled-rejection/package.json @@ -1,6 +1,6 @@ { "name": "@bugsnag/plugin-node-unhandled-rejection", - "version": "7.7.0", + "version": "7.9.0-alpha.0", "main": "unhandled-rejection.js", "description": "@bugsnag/js plugin to capture and report unhandled rejections", "homepage": "https://www.bugsnag.com/", @@ -18,7 +18,7 @@ "author": "Bugsnag", "license": "MIT", "devDependencies": { - "@bugsnag/core": "^7.7.0" + "@bugsnag/core": "^7.9.0-alpha.0" }, "peerDependencies": { "@bugsnag/core": "^7.0.0" diff --git a/packages/plugin-react-native-app-state-breadcrumbs/package-lock.json b/packages/plugin-react-native-app-state-breadcrumbs/package-lock.json index bf0cbf79f..e26e4ed35 100644 --- a/packages/plugin-react-native-app-state-breadcrumbs/package-lock.json +++ b/packages/plugin-react-native-app-state-breadcrumbs/package-lock.json @@ -2133,5 +2133,5 @@ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" } }, - "version": "7.7.0" + "version": "7.9.0-alpha.0" } diff --git a/packages/plugin-react-native-app-state-breadcrumbs/package.json b/packages/plugin-react-native-app-state-breadcrumbs/package.json index 427896f4b..3f29f0fed 100644 --- a/packages/plugin-react-native-app-state-breadcrumbs/package.json +++ b/packages/plugin-react-native-app-state-breadcrumbs/package.json @@ -1,6 +1,6 @@ { "name": "@bugsnag/plugin-react-native-app-state-breadcrumbs", - "version": "7.7.0", + "version": "7.9.0-alpha.0", "main": "app-state.js", "description": "@bugsnag/js plugin to create breadcrumbs when a React Native app enters the foreground/background", "homepage": "https://www.bugsnag.com/", @@ -17,7 +17,7 @@ "author": "Bugsnag", "license": "MIT", "devDependencies": { - "@bugsnag/core": "^7.7.0" + "@bugsnag/core": "^7.9.0-alpha.0" }, "peerDependencies": { "@bugsnag/core": "^7.0.0" diff --git a/packages/plugin-react-native-client-sync/package-lock.json b/packages/plugin-react-native-client-sync/package-lock.json index 3b9668434..1b473e005 100644 --- a/packages/plugin-react-native-client-sync/package-lock.json +++ b/packages/plugin-react-native-client-sync/package-lock.json @@ -1,6 +1,6 @@ { "name": "@bugsnag/plugin-react-native-client-sync", - "version": "7.7.0", + "version": "7.9.0-alpha.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/packages/plugin-react-native-client-sync/package.json b/packages/plugin-react-native-client-sync/package.json index 7a549c599..d164116e3 100644 --- a/packages/plugin-react-native-client-sync/package.json +++ b/packages/plugin-react-native-client-sync/package.json @@ -1,6 +1,6 @@ { "name": "@bugsnag/plugin-react-native-client-sync", - "version": "7.7.0", + "version": "7.9.0-alpha.0", "main": "client-sync.js", "description": "@bugsnag/react-native plugin to sync information between JS and native layer", "homepage": "https://www.bugsnag.com/", @@ -17,7 +17,7 @@ "author": "Bugsnag", "license": "MIT", "devDependencies": { - "@bugsnag/core": "^7.7.0" + "@bugsnag/core": "^7.9.0-alpha.0" }, "peerDependencies": { "@bugsnag/core": "^7.0.0" diff --git a/packages/plugin-react-native-connectivity-breadcrumbs/package-lock.json b/packages/plugin-react-native-connectivity-breadcrumbs/package-lock.json index 1b76e85a6..27830ed19 100644 --- a/packages/plugin-react-native-connectivity-breadcrumbs/package-lock.json +++ b/packages/plugin-react-native-connectivity-breadcrumbs/package-lock.json @@ -1,6 +1,6 @@ { "name": "@bugsnag/plugin-react-native-connectivity-breadcrumbs", - "version": "7.8.2", + "version": "7.9.0-alpha.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/packages/plugin-react-native-connectivity-breadcrumbs/package.json b/packages/plugin-react-native-connectivity-breadcrumbs/package.json index 84d072e4c..1a2995dd3 100644 --- a/packages/plugin-react-native-connectivity-breadcrumbs/package.json +++ b/packages/plugin-react-native-connectivity-breadcrumbs/package.json @@ -1,6 +1,6 @@ { "name": "@bugsnag/plugin-react-native-connectivity-breadcrumbs", - "version": "7.8.2", + "version": "7.9.0-alpha.0", "main": "connectivity.js", "description": "@bugsnag/js plugin to create breadcrumbs when the network status changes in a React Native app", "homepage": "https://www.bugsnag.com/", @@ -20,7 +20,7 @@ "@react-native-community/netinfo": "5.9.7" }, "devDependencies": { - "@bugsnag/core": "^7.7.0" + "@bugsnag/core": "^7.9.0-alpha.0" }, "peerDependencies": { "@bugsnag/core": "^7.0.0" diff --git a/packages/plugin-react-native-event-sync/package-lock.json b/packages/plugin-react-native-event-sync/package-lock.json index a6ba4cc2c..f0349e849 100644 --- a/packages/plugin-react-native-event-sync/package-lock.json +++ b/packages/plugin-react-native-event-sync/package-lock.json @@ -1,6 +1,6 @@ { "name": "@bugsnag/plugin-react-native-event-sync", - "version": "7.7.0", + "version": "7.9.0-alpha.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/packages/plugin-react-native-event-sync/package.json b/packages/plugin-react-native-event-sync/package.json index ebf9f48d3..c5fffd016 100644 --- a/packages/plugin-react-native-event-sync/package.json +++ b/packages/plugin-react-native-event-sync/package.json @@ -1,6 +1,6 @@ { "name": "@bugsnag/plugin-react-native-event-sync", - "version": "7.7.0", + "version": "7.9.0-alpha.0", "main": "event-sync.js", "description": "@bugsnag/react-native plugin to sync native event information in an onError callbacks", "homepage": "https://www.bugsnag.com/", @@ -17,7 +17,7 @@ "author": "Bugsnag", "license": "MIT", "devDependencies": { - "@bugsnag/core": "^7.7.0" + "@bugsnag/core": "^7.9.0-alpha.0" }, "peerDependencies": { "@bugsnag/core": "^7.0.0" diff --git a/packages/plugin-react-native-global-error-handler/package-lock.json b/packages/plugin-react-native-global-error-handler/package-lock.json index 3cf7bafd3..164d8ae50 100644 --- a/packages/plugin-react-native-global-error-handler/package-lock.json +++ b/packages/plugin-react-native-global-error-handler/package-lock.json @@ -1,6 +1,6 @@ { "name": "@bugsnag/plugin-react-native-global-error-handler", - "version": "7.7.0", + "version": "7.9.0-alpha.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/packages/plugin-react-native-global-error-handler/package.json b/packages/plugin-react-native-global-error-handler/package.json index c6d0edb16..4f205aa1c 100644 --- a/packages/plugin-react-native-global-error-handler/package.json +++ b/packages/plugin-react-native-global-error-handler/package.json @@ -1,6 +1,6 @@ { "name": "@bugsnag/plugin-react-native-global-error-handler", - "version": "7.7.0", + "version": "7.9.0-alpha.0", "main": "error-handler.js", "description": "@bugsnag/js plugin to report unhandled exceptions in React Native", "homepage": "https://www.bugsnag.com/", @@ -17,7 +17,7 @@ "author": "Bugsnag", "license": "MIT", "devDependencies": { - "@bugsnag/core": "^7.7.0" + "@bugsnag/core": "^7.9.0-alpha.0" }, "peerDependencies": { "@bugsnag/core": "^7.0.0" diff --git a/packages/plugin-react-native-hermes/package.json b/packages/plugin-react-native-hermes/package.json index 82e1c0412..3255f4987 100644 --- a/packages/plugin-react-native-hermes/package.json +++ b/packages/plugin-react-native-hermes/package.json @@ -1,6 +1,6 @@ { "name": "@bugsnag/plugin-react-native-hermes", - "version": "7.7.0", + "version": "7.9.0-alpha.0", "main": "hermes.js", "description": "@bugsnag/react-native plugin to support Hermes", "homepage": "https://www.bugsnag.com/", @@ -18,7 +18,7 @@ "author": "Bugsnag", "license": "MIT", "devDependencies": { - "@bugsnag/core": "^7.7.0" + "@bugsnag/core": "^7.9.0-alpha.0" }, "peerDependencies": { "@bugsnag/core": "^7.0.0" diff --git a/packages/plugin-react-native-navigation/package-lock.json b/packages/plugin-react-native-navigation/package-lock.json index b77352aa6..294edbaaa 100644 --- a/packages/plugin-react-native-navigation/package-lock.json +++ b/packages/plugin-react-native-navigation/package-lock.json @@ -1,6 +1,6 @@ { "name": "@bugsnag/plugin-react-native-navigation", - "version": "7.7.0", + "version": "7.9.0-alpha.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/packages/plugin-react-native-navigation/package.json b/packages/plugin-react-native-navigation/package.json index fa241942c..087b6b285 100644 --- a/packages/plugin-react-native-navigation/package.json +++ b/packages/plugin-react-native-navigation/package.json @@ -1,6 +1,6 @@ { "name": "@bugsnag/plugin-react-native-navigation", - "version": "7.7.0", + "version": "7.9.0-alpha.0", "main": "react-native-navigation.js", "types": "types/react-native-navigation.d.ts", "description": "@bugsnag/react-native plugin for integration with react-native-navigation", @@ -19,7 +19,7 @@ "author": "Bugsnag", "license": "MIT", "devDependencies": { - "@bugsnag/core": "^7.7.0", + "@bugsnag/core": "^7.9.0-alpha.0", "@types/react-native": "^0.63.20", "react-native-navigation": "^7.0.0" }, diff --git a/packages/plugin-react-native-orientation-breadcrumbs/package-lock.json b/packages/plugin-react-native-orientation-breadcrumbs/package-lock.json index bbf9e8b3c..2b59fb808 100644 --- a/packages/plugin-react-native-orientation-breadcrumbs/package-lock.json +++ b/packages/plugin-react-native-orientation-breadcrumbs/package-lock.json @@ -1,6 +1,6 @@ { "name": "@bugsnag/plugin-react-native-orientation-breadcrumbs", - "version": "7.7.0", + "version": "7.9.0-alpha.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/packages/plugin-react-native-orientation-breadcrumbs/package.json b/packages/plugin-react-native-orientation-breadcrumbs/package.json index cc29b5660..3e55928ad 100644 --- a/packages/plugin-react-native-orientation-breadcrumbs/package.json +++ b/packages/plugin-react-native-orientation-breadcrumbs/package.json @@ -1,6 +1,6 @@ { "name": "@bugsnag/plugin-react-native-orientation-breadcrumbs", - "version": "7.7.0", + "version": "7.9.0-alpha.0", "main": "orientation.js", "description": "@bugsnag/js plugin to create breadcrumbs when the device orientation changes in a React Native app", "homepage": "https://www.bugsnag.com/", @@ -17,7 +17,7 @@ "author": "Bugsnag", "license": "MIT", "devDependencies": { - "@bugsnag/core": "^7.7.0" + "@bugsnag/core": "^7.9.0-alpha.0" }, "peerDependencies": { "@bugsnag/core": "^7.0.0" diff --git a/packages/plugin-react-native-session/package-lock.json b/packages/plugin-react-native-session/package-lock.json index 33bd9582f..6d349fc68 100644 --- a/packages/plugin-react-native-session/package-lock.json +++ b/packages/plugin-react-native-session/package-lock.json @@ -1,6 +1,6 @@ { "name": "@bugsnag/plugin-react-native-session", - "version": "7.7.0", + "version": "7.9.0-alpha.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/packages/plugin-react-native-session/package.json b/packages/plugin-react-native-session/package.json index 88ce16d13..26f3120e6 100644 --- a/packages/plugin-react-native-session/package.json +++ b/packages/plugin-react-native-session/package.json @@ -1,6 +1,6 @@ { "name": "@bugsnag/plugin-react-native-session", - "version": "7.7.0", + "version": "7.9.0-alpha.0", "main": "session.js", "description": "@bugsnag/react-native session implementation (which delegates all functionality to the native client)", "homepage": "https://www.bugsnag.com/", @@ -17,7 +17,7 @@ "author": "Bugsnag", "license": "MIT", "devDependencies": { - "@bugsnag/core": "^7.7.0" + "@bugsnag/core": "^7.9.0-alpha.0" }, "peerDependencies": { "@bugsnag/core": "^7.0.0" diff --git a/packages/plugin-react-native-unhandled-rejection/package-lock.json b/packages/plugin-react-native-unhandled-rejection/package-lock.json index 09d34e8c0..29c43d32c 100644 --- a/packages/plugin-react-native-unhandled-rejection/package-lock.json +++ b/packages/plugin-react-native-unhandled-rejection/package-lock.json @@ -1,6 +1,6 @@ { "name": "@bugsnag/plugin-react-native-unhandled-rejection", - "version": "7.7.0", + "version": "7.9.0-alpha.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/packages/plugin-react-native-unhandled-rejection/package.json b/packages/plugin-react-native-unhandled-rejection/package.json index a1d41510b..e0d9c38c3 100644 --- a/packages/plugin-react-native-unhandled-rejection/package.json +++ b/packages/plugin-react-native-unhandled-rejection/package.json @@ -1,6 +1,6 @@ { "name": "@bugsnag/plugin-react-native-unhandled-rejection", - "version": "7.7.0", + "version": "7.9.0-alpha.0", "main": "rejection-handler.js", "description": "@bugsnag/js plugin to report unhandled promise rejections in React Native", "homepage": "https://www.bugsnag.com/", @@ -17,7 +17,7 @@ "author": "Bugsnag", "license": "MIT", "devDependencies": { - "@bugsnag/core": "^7.7.0", + "@bugsnag/core": "^7.9.0-alpha.0", "promise": "^8.0.2" }, "peerDependencies": { diff --git a/packages/plugin-react-navigation/package-lock.json b/packages/plugin-react-navigation/package-lock.json index 0905d1b35..fa1c2712b 100644 --- a/packages/plugin-react-navigation/package-lock.json +++ b/packages/plugin-react-navigation/package-lock.json @@ -1,6 +1,6 @@ { "name": "@bugsnag/plugin-react-navigation", - "version": "7.7.0", + "version": "7.9.0-alpha.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/packages/plugin-react-navigation/package.json b/packages/plugin-react-navigation/package.json index 556693475..f7137fffd 100644 --- a/packages/plugin-react-navigation/package.json +++ b/packages/plugin-react-navigation/package.json @@ -1,6 +1,6 @@ { "name": "@bugsnag/plugin-react-navigation", - "version": "7.7.0", + "version": "7.9.0-alpha.0", "main": "react-navigation.js", "description": "@bugsnag/react-native plugin to update context and leave breadcrumb when the screen changes", "homepage": "https://www.bugsnag.com/", @@ -22,7 +22,7 @@ "author": "Bugsnag", "license": "MIT", "devDependencies": { - "@bugsnag/core": "^7.7.0", + "@bugsnag/core": "^7.9.0-alpha.0", "@react-navigation/native": "^5.7.3", "@types/react": "^16.9.49", "@types/react-native": "^0.63.20", diff --git a/packages/plugin-react/package-lock.json b/packages/plugin-react/package-lock.json index 5d5c1e922..ec9c8f7dd 100644 --- a/packages/plugin-react/package-lock.json +++ b/packages/plugin-react/package-lock.json @@ -1,6 +1,6 @@ { "name": "@bugsnag/plugin-react", - "version": "7.7.0", + "version": "7.9.0-alpha.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/packages/plugin-react/package.json b/packages/plugin-react/package.json index 1ee92c86a..e41e377ec 100644 --- a/packages/plugin-react/package.json +++ b/packages/plugin-react/package.json @@ -1,6 +1,6 @@ { "name": "@bugsnag/plugin-react", - "version": "7.7.0", + "version": "7.9.0-alpha.0", "main": "dist/bugsnag-react.js", "description": "React integration for @bugsnag/js", "browser": "dist/bugsnag-react.js", @@ -25,7 +25,7 @@ "author": "Bugsnag", "license": "MIT", "devDependencies": { - "@bugsnag/core": "^7.7.0" + "@bugsnag/core": "^7.9.0-alpha.0" }, "peerDependencies": { "@bugsnag/core": "^7.0.0" diff --git a/packages/plugin-restify/package-lock.json b/packages/plugin-restify/package-lock.json index 0c9c85857..4f6b7c5a2 100644 --- a/packages/plugin-restify/package-lock.json +++ b/packages/plugin-restify/package-lock.json @@ -1,6 +1,6 @@ { "name": "@bugsnag/plugin-restify", - "version": "7.7.0", + "version": "7.9.0-alpha.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/packages/plugin-restify/package.json b/packages/plugin-restify/package.json index c81669b90..3fb2f3eee 100644 --- a/packages/plugin-restify/package.json +++ b/packages/plugin-restify/package.json @@ -1,6 +1,6 @@ { "name": "@bugsnag/plugin-restify", - "version": "7.7.0", + "version": "7.9.0-alpha.0", "main": "dist/bugsnag-restify.js", "types": "types/bugsnag-restify.d.ts", "description": "@bugsnag/js error handling middleware for Restify web servers", @@ -28,7 +28,7 @@ "@bugsnag/core": "^7.0.0" }, "devDependencies": { - "@bugsnag/core": "^7.7.0", + "@bugsnag/core": "^7.9.0-alpha.0", "@types/restify": "^8.4.2" }, "dependencies": { diff --git a/packages/plugin-server-session/package-lock.json b/packages/plugin-server-session/package-lock.json index 629152a49..afeb9bd77 100644 --- a/packages/plugin-server-session/package-lock.json +++ b/packages/plugin-server-session/package-lock.json @@ -1,6 +1,6 @@ { "name": "@bugsnag/plugin-server-session", - "version": "7.7.0", + "version": "7.9.0-alpha.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/packages/plugin-server-session/package.json b/packages/plugin-server-session/package.json index ed5bcbbd2..35316fefa 100644 --- a/packages/plugin-server-session/package.json +++ b/packages/plugin-server-session/package.json @@ -1,6 +1,6 @@ { "name": "@bugsnag/plugin-server-session", - "version": "7.7.0", + "version": "7.9.0-alpha.0", "main": "session.js", "description": "@bugsnag/js plugin to enable session tracking in server applications", "homepage": "https://www.bugsnag.com/", @@ -20,7 +20,7 @@ "backo": "^1.1.0" }, "devDependencies": { - "@bugsnag/core": "^7.7.0" + "@bugsnag/core": "^7.9.0-alpha.0" }, "peerDependencies": { "@bugsnag/core": "^7.0.0" diff --git a/packages/plugin-simple-throttle/package-lock.json b/packages/plugin-simple-throttle/package-lock.json index b40b74277..2f391bfeb 100644 --- a/packages/plugin-simple-throttle/package-lock.json +++ b/packages/plugin-simple-throttle/package-lock.json @@ -1,6 +1,6 @@ { "name": "@bugsnag/plugin-simple-throttle", - "version": "7.7.0", + "version": "7.9.0-alpha.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/packages/plugin-simple-throttle/package.json b/packages/plugin-simple-throttle/package.json index 6d7db5a8d..227150e1d 100644 --- a/packages/plugin-simple-throttle/package.json +++ b/packages/plugin-simple-throttle/package.json @@ -1,6 +1,6 @@ { "name": "@bugsnag/plugin-simple-throttle", - "version": "7.7.0", + "version": "7.9.0-alpha.0", "main": "throttle.js", "description": "@bugsnag/js plugin to prevent too many events from being sent", "homepage": "https://www.bugsnag.com/", @@ -18,7 +18,7 @@ "author": "Bugsnag", "license": "MIT", "devDependencies": { - "@bugsnag/core": "^7.7.0" + "@bugsnag/core": "^7.9.0-alpha.0" }, "peerDependencies": { "@bugsnag/core": "^7.0.0" diff --git a/packages/plugin-strip-project-root/package-lock.json b/packages/plugin-strip-project-root/package-lock.json index 54838fd76..9046bb442 100644 --- a/packages/plugin-strip-project-root/package-lock.json +++ b/packages/plugin-strip-project-root/package-lock.json @@ -1,6 +1,6 @@ { "name": "@bugsnag/plugin-strip-project-root", - "version": "7.7.0", + "version": "7.9.0-alpha.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/packages/plugin-strip-project-root/package.json b/packages/plugin-strip-project-root/package.json index 1e1a802d5..17a0fcc99 100644 --- a/packages/plugin-strip-project-root/package.json +++ b/packages/plugin-strip-project-root/package.json @@ -1,6 +1,6 @@ { "name": "@bugsnag/plugin-strip-project-root", - "version": "7.7.0", + "version": "7.9.0-alpha.0", "main": "strip-project-root.js", "description": "@bugsnag/js plugin to remove common project root paths from stacktraces", "homepage": "https://www.bugsnag.com/", @@ -17,7 +17,7 @@ "author": "Bugsnag", "license": "MIT", "devDependencies": { - "@bugsnag/core": "^7.7.0" + "@bugsnag/core": "^7.9.0-alpha.0" }, "peerDependencies": { "@bugsnag/core": "^7.0.0" diff --git a/packages/plugin-strip-query-string/package-lock.json b/packages/plugin-strip-query-string/package-lock.json index 67e9de51e..b7ddf98ad 100644 --- a/packages/plugin-strip-query-string/package-lock.json +++ b/packages/plugin-strip-query-string/package-lock.json @@ -1,6 +1,6 @@ { "name": "@bugsnag/plugin-strip-query-string", - "version": "7.7.0", + "version": "7.9.0-alpha.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/packages/plugin-strip-query-string/package.json b/packages/plugin-strip-query-string/package.json index 9195b8f1b..b97edefdf 100644 --- a/packages/plugin-strip-query-string/package.json +++ b/packages/plugin-strip-query-string/package.json @@ -1,6 +1,6 @@ { "name": "@bugsnag/plugin-strip-query-string", - "version": "7.7.0", + "version": "7.9.0-alpha.0", "main": "strip-query-string.js", "description": "@bugsnag/js plugin to strip query string and document fragment from stackframe filenames", "homepage": "https://www.bugsnag.com/", @@ -17,7 +17,7 @@ "author": "Bugsnag", "license": "MIT", "devDependencies": { - "@bugsnag/core": "^7.7.0" + "@bugsnag/core": "^7.9.0-alpha.0" }, "peerDependencies": { "@bugsnag/core": "^7.0.0" diff --git a/packages/plugin-vue/package-lock.json b/packages/plugin-vue/package-lock.json index 762792690..517f28452 100644 --- a/packages/plugin-vue/package-lock.json +++ b/packages/plugin-vue/package-lock.json @@ -1,6 +1,6 @@ { "name": "@bugsnag/plugin-vue", - "version": "7.7.0", + "version": "7.9.0-alpha.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/packages/plugin-vue/package.json b/packages/plugin-vue/package.json index 2bd9381f6..5310df5b5 100644 --- a/packages/plugin-vue/package.json +++ b/packages/plugin-vue/package.json @@ -1,6 +1,6 @@ { "name": "@bugsnag/plugin-vue", - "version": "7.7.0", + "version": "7.9.0-alpha.0", "description": "Vue.js integration for bugsnag-js", "main": "dist/bugsnag-vue.js", "browser": "dist/bugsnag-vue.js", @@ -25,7 +25,7 @@ "author": "Bugsnag", "license": "MIT", "devDependencies": { - "@bugsnag/core": "^7.7.0" + "@bugsnag/core": "^7.9.0-alpha.0" }, "peerDependencies": { "@bugsnag/core": "^7.0.0" diff --git a/packages/plugin-window-onerror/package-lock.json b/packages/plugin-window-onerror/package-lock.json index c2375e37f..588b3a02b 100644 --- a/packages/plugin-window-onerror/package-lock.json +++ b/packages/plugin-window-onerror/package-lock.json @@ -1,6 +1,6 @@ { "name": "@bugsnag/plugin-window-onerror", - "version": "7.7.0", + "version": "7.9.0-alpha.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/packages/plugin-window-onerror/package.json b/packages/plugin-window-onerror/package.json index 526b06fad..345d56ece 100644 --- a/packages/plugin-window-onerror/package.json +++ b/packages/plugin-window-onerror/package.json @@ -1,6 +1,6 @@ { "name": "@bugsnag/plugin-window-onerror", - "version": "7.7.0", + "version": "7.9.0-alpha.0", "main": "onerror.js", "description": "@bugsnag/js plugin to report unhandled exceptions in browsers", "homepage": "https://www.bugsnag.com/", @@ -17,7 +17,7 @@ "author": "Bugsnag", "license": "MIT", "devDependencies": { - "@bugsnag/core": "^7.7.0" + "@bugsnag/core": "^7.9.0-alpha.0" }, "peerDependencies": { "@bugsnag/core": "^7.0.0" diff --git a/packages/plugin-window-unhandled-rejection/package-lock.json b/packages/plugin-window-unhandled-rejection/package-lock.json index c35004613..2e965440b 100644 --- a/packages/plugin-window-unhandled-rejection/package-lock.json +++ b/packages/plugin-window-unhandled-rejection/package-lock.json @@ -1,6 +1,6 @@ { "name": "@bugsnag/plugin-window-unhandled-rejection", - "version": "7.7.0", + "version": "7.9.0-alpha.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/packages/plugin-window-unhandled-rejection/package.json b/packages/plugin-window-unhandled-rejection/package.json index 9e5c499db..082f8a69a 100644 --- a/packages/plugin-window-unhandled-rejection/package.json +++ b/packages/plugin-window-unhandled-rejection/package.json @@ -1,6 +1,6 @@ { "name": "@bugsnag/plugin-window-unhandled-rejection", - "version": "7.7.0", + "version": "7.9.0-alpha.0", "main": "unhandled-rejection.js", "description": "@bugsnag/js plugin to report unhandled promise rejections in browsers", "homepage": "https://www.bugsnag.com/", @@ -17,7 +17,7 @@ "author": "Bugsnag", "license": "MIT", "devDependencies": { - "@bugsnag/core": "^7.7.0" + "@bugsnag/core": "^7.9.0-alpha.0" }, "peerDependencies": { "@bugsnag/core": "^7.0.0" diff --git a/packages/react-native-cli/package-lock.json b/packages/react-native-cli/package-lock.json index ae477670b..5fd7149b0 100644 --- a/packages/react-native-cli/package-lock.json +++ b/packages/react-native-cli/package-lock.json @@ -1,6 +1,6 @@ { "name": "@bugsnag/react-native-cli", - "version": "7.8.0", + "version": "7.9.0-alpha.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/packages/react-native-cli/package.json b/packages/react-native-cli/package.json index 91eedb996..04621fe2e 100644 --- a/packages/react-native-cli/package.json +++ b/packages/react-native-cli/package.json @@ -1,6 +1,6 @@ { "name": "@bugsnag/react-native-cli", - "version": "7.8.0", + "version": "7.9.0-alpha.0", "description": "A tool to help integrate Bugsnag with a React Native app", "bin": { "bugsnag-react-native-cli": "bin/cli" diff --git a/packages/react-native/package-lock.json b/packages/react-native/package-lock.json index 4a2646f0f..5929ee713 100644 --- a/packages/react-native/package-lock.json +++ b/packages/react-native/package-lock.json @@ -1,6 +1,6 @@ { "name": "@bugsnag/react-native", - "version": "7.8.2", + "version": "7.9.0-alpha.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/packages/react-native/package.json b/packages/react-native/package.json index a442f5aeb..dbf13287a 100644 --- a/packages/react-native/package.json +++ b/packages/react-native/package.json @@ -1,6 +1,6 @@ { "name": "@bugsnag/react-native", - "version": "7.8.2", + "version": "7.9.0-alpha.0", "main": "src/notifier.js", "types": "types/bugsnag.d.ts", "description": "Bugsnag error reporter for React Native applications", @@ -49,17 +49,17 @@ "typescript": "^3.3.3" }, "dependencies": { - "@bugsnag/core": "^7.7.0", - "@bugsnag/delivery-react-native": "^7.8.0", - "@bugsnag/plugin-console-breadcrumbs": "^7.7.0", - "@bugsnag/plugin-network-breadcrumbs": "^7.7.0", - "@bugsnag/plugin-react": "^7.7.0", - "@bugsnag/plugin-react-native-client-sync": "^7.7.0", - "@bugsnag/plugin-react-native-event-sync": "^7.7.0", - "@bugsnag/plugin-react-native-global-error-handler": "^7.7.0", - "@bugsnag/plugin-react-native-hermes": "^7.7.0", - "@bugsnag/plugin-react-native-session": "^7.7.0", - "@bugsnag/plugin-react-native-unhandled-rejection": "^7.7.0", + "@bugsnag/core": "^7.9.0-alpha.0", + "@bugsnag/delivery-react-native": "^7.9.0-alpha.0", + "@bugsnag/plugin-console-breadcrumbs": "^7.9.0-alpha.0", + "@bugsnag/plugin-network-breadcrumbs": "^7.9.0-alpha.0", + "@bugsnag/plugin-react": "^7.9.0-alpha.0", + "@bugsnag/plugin-react-native-client-sync": "^7.9.0-alpha.0", + "@bugsnag/plugin-react-native-event-sync": "^7.9.0-alpha.0", + "@bugsnag/plugin-react-native-global-error-handler": "^7.9.0-alpha.0", + "@bugsnag/plugin-react-native-hermes": "^7.9.0-alpha.0", + "@bugsnag/plugin-react-native-session": "^7.9.0-alpha.0", + "@bugsnag/plugin-react-native-unhandled-rejection": "^7.9.0-alpha.0", "iserror": "^0.0.2" } } From 97abac736857931769a31c8823ad8d2cac52ba49 Mon Sep 17 00:00:00 2001 From: Joe Haines Date: Thu, 11 Mar 2021 15:44:58 +0000 Subject: [PATCH 62/65] Allow regex to match pre-release version numbers This will now strip the version from 'bugsnag-core-7.9.0-alpha.0.tgz' for example --- test/aws-lambda/features/scripts/build-fixtures | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/aws-lambda/features/scripts/build-fixtures b/test/aws-lambda/features/scripts/build-fixtures index 9bb8cf0b8..f048f62e8 100755 --- a/test/aws-lambda/features/scripts/build-fixtures +++ b/test/aws-lambda/features/scripts/build-fixtures @@ -66,7 +66,7 @@ def pack # Strip the version suffix from the packages Dir["#{FileUtils.pwd}/bugsnag-*.tgz"].each do |package| - package_with_no_version = package.gsub(/-\d+\.\d+\.\d+/, '') + package_with_no_version = package.gsub(/-\d+\.\d+\.\d+.*(?=\.tgz)/, '') File.rename(package, package_with_no_version) end end From cffc31da417d915e43e80687222dd0aa0a5f4bd0 Mon Sep 17 00:00:00 2001 From: Dan Skinner Date: Mon, 15 Mar 2021 11:21:40 +0000 Subject: [PATCH 63/65] test(plugin-koa): update koa.feature for mazerunner 4 --- test/node/features/koa.feature | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/node/features/koa.feature b/test/node/features/koa.feature index 0b2f38e3e..1725aad97 100644 --- a/test/node/features/koa.feature +++ b/test/node/features/koa.feature @@ -93,8 +93,8 @@ Scenario: A handled error with ctx.bugsnag.notify() Scenario: adding body to request metadata When I POST the data "data=in_request_body" to the URL "http://koa/bodytest" - And I wait to receive a request - Then the request is valid for the error reporting API version "4" for the "Bugsnag Node" notifier + And I wait to receive an error + Then the error is valid for the error reporting API version "4" for the "Bugsnag Node" notifier And the event "unhandled" is true And the event "severity" equals "error" And the exception "errorClass" equals "Error" From 43477592109d0be78a4ee5fac45ff67a81de8274 Mon Sep 17 00:00:00 2001 From: Ben Gourley Date: Mon, 15 Mar 2021 15:50:57 +0000 Subject: [PATCH 64/65] chore: Update changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 972fc5198..8a8f3e79a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## TBD +### Added + +- (plugin-aws-lambda): Add support for Node.js on AWS Lambda. See the [docs](https://docs.bugsnag.com/platforms/javascript/aws-lambda/) for usage. [#1334](https://github.com/bugsnag/bugsnag-js/pull/1334) + ### Changed - (react-native): Update bugsnag-cocoa to v6.7.1 From ab9bb71d3e1fd12220206433117f8c762bd59112 Mon Sep 17 00:00:00 2001 From: Dan Skinner Date: Tue, 16 Mar 2021 11:41:01 +0000 Subject: [PATCH 65/65] chore: update changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4b04b64d3..3d89d6dbd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## TBD +## v7.9.0 (2021-03-16) ### Added