diff --git a/package-lock.json b/package-lock.json index a965d890f..111e4109e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -106,9 +106,9 @@ } }, "@google-cloud/nodejs-repo-tools": { - "version": "2.2.6", - "resolved": "https://registry.npmjs.org/@google-cloud/nodejs-repo-tools/-/nodejs-repo-tools-2.2.6.tgz", - "integrity": "sha512-bDEgNBAJ8kfYWrpifg+D7rXs8NUBUUeu2I6NWkcL9IOXg86rblu7xnhFHRxBMcvytJ+7WY4pvkCLxO1v3QBsXg==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@google-cloud/nodejs-repo-tools/-/nodejs-repo-tools-2.3.0.tgz", + "integrity": "sha512-c8dIGESnNkmM88duFxGHvMQP5QKPgp/sfJq0QhC6+gOcJC7/PKjqd0PkmgPPeIgVl6SXy5Zf/KLbxnJUVgNT1Q==", "dev": true, "requires": { "ava": "0.25.0", @@ -119,6 +119,7 @@ "lodash": "4.17.5", "nyc": "11.4.1", "proxyquire": "1.8.0", + "semver": "5.5.0", "sinon": "4.3.0", "string": "3.3.3", "supertest": "3.0.0", @@ -2007,7 +2008,7 @@ "integrity": "sha512-jOdnI/3qTpHABjM5cx1Hc0sKsPoYCp+DP/GJRGtDlPd7fiV9oXGGIcjW/ZOxLIvjGz8MA+uMZI9metHlgqbgwQ==", "dev": true, "requires": { - "@types/node": "9.6.2" + "@types/node": "9.6.4" } }, "@types/body-parser": { @@ -2017,7 +2018,7 @@ "dev": true, "requires": { "@types/express": "4.11.1", - "@types/node": "9.6.2" + "@types/node": "9.6.4" } }, "@types/boom": { @@ -2033,7 +2034,7 @@ "dev": true, "requires": { "@types/events": "1.2.0", - "@types/node": "9.6.2" + "@types/node": "9.6.4" } }, "@types/caseless": { @@ -2057,7 +2058,7 @@ "integrity": "sha512-OPSxsP6XqA3984KWDUXq/u05Hu8VWa/2rUVlw/aDUOx87BptIep6xb3NdCxCpKLfLdjZcCE5jR+gouTul3gjdA==", "dev": true, "requires": { - "@types/node": "9.6.2" + "@types/node": "9.6.4" } }, "@types/cookies": { @@ -2069,7 +2070,7 @@ "@types/connect": "3.4.31", "@types/express": "4.11.1", "@types/keygrip": "1.0.1", - "@types/node": "9.6.2" + "@types/node": "9.6.4" } }, "@types/events": { @@ -2096,7 +2097,7 @@ "dev": true, "requires": { "@types/events": "1.2.0", - "@types/node": "9.6.2" + "@types/node": "9.6.4" } }, "@types/extend": { @@ -2111,7 +2112,7 @@ "integrity": "sha512-JAMFhOaHIciYVh8fb5/83nmuO/AHwmto+Hq7a9y8FzLDcC1KCU344XDOMEmahnrTFlHjgh4L0WJFczNIX2GxnQ==", "dev": true, "requires": { - "@types/node": "9.6.2" + "@types/node": "9.6.4" } }, "@types/glob": { @@ -2122,7 +2123,7 @@ "requires": { "@types/events": "1.2.0", "@types/minimatch": "3.0.3", - "@types/node": "9.6.2" + "@types/node": "9.6.4" } }, "@types/hapi": { @@ -2136,7 +2137,7 @@ "@types/events": "1.2.0", "@types/joi": "13.0.7", "@types/mimos": "3.0.1", - "@types/node": "9.6.2", + "@types/node": "9.6.4", "@types/podium": "1.0.0", "@types/shot": "3.4.0" } @@ -2166,9 +2167,9 @@ "dev": true }, "@types/koa": { - "version": "2.0.44", - "resolved": "https://registry.npmjs.org/@types/koa/-/koa-2.0.44.tgz", - "integrity": "sha512-xOg6XLJdKmYriExAF0pV+HYhzftNJbpxplJgjyCwEM2LNLQPMgW4uSHXjgsqSPKsaAgM8CiR31vIeocRibFSjw==", + "version": "2.0.45", + "resolved": "https://registry.npmjs.org/@types/koa/-/koa-2.0.45.tgz", + "integrity": "sha512-emzrfiyQFMtx/dPMVhEMM1GU/p7QbRYWgubp1VGsR+bIC/53ehDf5IATM2fEZhoyHPeLuYyQVAjSPc/YqoWMSQ==", "dev": true, "requires": { "@types/accepts": "1.3.5", @@ -2177,7 +2178,7 @@ "@types/http-assert": "1.2.2", "@types/keygrip": "1.0.1", "@types/koa-compose": "3.2.2", - "@types/node": "9.6.2" + "@types/node": "9.6.4" } }, "@types/koa-compose": { @@ -2330,22 +2331,22 @@ "integrity": "sha512-TeiJ7uvv/92ugSqZ0v9l0eNXzutlki0aK+R1K5bfA5SYUil46ITlxLW4iNTCf55P4L5weCmaOdtxGeGWvudwPg==", "dev": true, "requires": { - "@types/node": "9.6.2" + "@types/node": "9.6.4" } }, "@types/nock": { - "version": "9.1.2", - "resolved": "https://registry.npmjs.org/@types/nock/-/nock-9.1.2.tgz", - "integrity": "sha512-Vdd1dRTUT5S1ONTcAMmQ2PCzIQccKMOpgu9T+knvJeGRCt29j3tcz9oRC1AM6OXD81+8U4mVuWzHklTlQW7W+w==", + "version": "9.1.3", + "resolved": "https://registry.npmjs.org/@types/nock/-/nock-9.1.3.tgz", + "integrity": "sha512-S8rJ+SaW82ICX87pZP62UcMifrMfjEdqNzSp+llx4YcvKw6bO650Ye6HwTqER1Dar3S40GIZECQisOrAICDCjA==", "dev": true, "requires": { - "@types/node": "9.6.2" + "@types/node": "9.6.4" } }, "@types/node": { - "version": "9.6.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-9.6.2.tgz", - "integrity": "sha512-UWkRY9X7RQHp5OhhRIIka58/gVVycL1zHZu0OTsT5LI86ABaMOSbUjAl+b0FeDhQcxclrkyft3kW5QWdMRs8wQ==", + "version": "9.6.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-9.6.4.tgz", + "integrity": "sha512-Awg4BcUYiZtNKoveGOu654JVPt11V/KIC77iBz8NweyoOAZpz5rUJfPDwwD+ajfTs2HndbTCEB8IuLfX9m/mmw==", "dev": true }, "@types/once": { @@ -2380,7 +2381,7 @@ "requires": { "@types/caseless": "0.12.1", "@types/form-data": "2.2.1", - "@types/node": "9.6.2", + "@types/node": "9.6.4", "@types/tough-cookie": "2.3.2" } }, @@ -2391,7 +2392,7 @@ "dev": true, "requires": { "@types/bunyan": "1.8.4", - "@types/node": "9.6.2", + "@types/node": "9.6.4", "@types/spdy": "3.4.4" } }, @@ -2402,7 +2403,7 @@ "dev": true, "requires": { "@types/glob": "5.0.35", - "@types/node": "9.6.2" + "@types/node": "9.6.4" } }, "@types/serve-static": { @@ -2421,7 +2422,7 @@ "integrity": "sha1-RZR3xRh9Pr0wNmCrCZ5+ng87ZW8=", "dev": true, "requires": { - "@types/node": "9.6.2" + "@types/node": "9.6.4" } }, "@types/spdy": { @@ -2430,7 +2431,7 @@ "integrity": "sha512-N9LBlbVRRYq6HgYpPkqQc3a9HJ/iEtVZToW6xlTtJiMhmRJ7jJdV7TaZQJw/Ve/1ePUsQiCTDc4JMuzzag94GA==", "dev": true, "requires": { - "@types/node": "9.6.2" + "@types/node": "9.6.4" } }, "@types/tmp": { @@ -2785,7 +2786,7 @@ "clean-stack": "1.3.0", "clean-yaml-object": "0.1.0", "cli-cursor": "2.1.0", - "cli-spinners": "1.3.0", + "cli-spinners": "1.3.1", "cli-truncate": "1.1.0", "co-with-promise": "4.6.0", "code-excerpt": "2.1.1", @@ -2866,9 +2867,9 @@ "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" }, "aws4": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.6.0.tgz", - "integrity": "sha1-g+9cqGCysy5KDe7e6MdxudtXRx4=" + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.7.0.tgz", + "integrity": "sha512-32NDda82rhwD9/JBCCkB+MRYDp0oSvlo2IL6rQWA10PQi7tDUM3eqMSltXmY+Oyl/7N3P3qNtAlv7X0d9bI28w==" }, "axios": { "version": "0.18.0", @@ -3122,7 +3123,7 @@ "babel-generator": "6.26.1", "babylon": "6.18.0", "call-matcher": "1.0.1", - "core-js": "2.5.4", + "core-js": "2.5.5", "espower-location-detector": "1.0.0", "espurify": "1.7.0", "estraverse": "4.2.0" @@ -3269,7 +3270,7 @@ "requires": { "babel-core": "6.26.0", "babel-runtime": "6.26.0", - "core-js": "2.5.4", + "core-js": "2.5.5", "home-or-tmp": "2.0.0", "lodash": "4.17.5", "mkdirp": "0.5.1", @@ -3293,7 +3294,7 @@ "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "dev": true, "requires": { - "core-js": "2.5.4", + "core-js": "2.5.5", "regenerator-runtime": "0.11.1" } }, @@ -3643,7 +3644,7 @@ "integrity": "sha1-UTTQd5hPcSpU2tPL9i3ijc5BbKg=", "dev": true, "requires": { - "core-js": "2.5.4", + "core-js": "2.5.5", "deep-equal": "1.0.1", "espurify": "1.7.0", "estraverse": "4.2.0" @@ -3904,9 +3905,9 @@ } }, "cli-spinners": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-1.3.0.tgz", - "integrity": "sha512-ahr3q/EW26uLN3vqBaDQS4g1rUwKMbVSTRlyfyoY06VwwSJmMYRxhT3FTAiTz9Yam6OOb1e0ldwvbsnuThvuzA==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-1.3.1.tgz", + "integrity": "sha512-1QL4544moEsDVH9T/l6Cemov/37iv1RtoKf7NJ04A60+4MREXNfx/QvavbH6QoGdsD4N4Mwy49cmaINR/o2mdg==", "dev": true }, "cli-table": { @@ -4137,7 +4138,7 @@ "dev": true, "requires": { "aws-sign2": "0.6.0", - "aws4": "1.6.0", + "aws4": "1.7.0", "caseless": "0.12.0", "combined-stream": "1.0.6", "extend": "3.0.1", @@ -4392,9 +4393,9 @@ } }, "core-js": { - "version": "2.5.4", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.4.tgz", - "integrity": "sha1-8si/GB8qgLkvNgEhQpzmOi8K6uA=", + "version": "2.5.5", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.5.tgz", + "integrity": "sha1-sU3ek2xkDAV5prUMq8wTLdYSfjs=", "dev": true }, "core-util-is": { @@ -4958,7 +4959,7 @@ "integrity": "sha1-bw2nNEf07dg4/sXGAxOoi6XLhSs=", "dev": true, "requires": { - "core-js": "2.5.4", + "core-js": "2.5.5", "empower-core": "0.6.2" } }, @@ -4978,7 +4979,7 @@ "dev": true, "requires": { "call-signature": "0.0.2", - "core-js": "2.5.4" + "core-js": "2.5.5" } }, "encodeurl": { @@ -5259,14 +5260,14 @@ "requires": { "ignore": "3.3.7", "minimatch": "3.0.4", - "resolve": "1.6.0", + "resolve": "1.7.0", "semver": "5.5.0" }, "dependencies": { "resolve": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.6.0.tgz", - "integrity": "sha512-mw7JQNu5ExIkcw4LPih0owX/TZXjD/ZUF/ZQ/pDnkw3ZKhDcZZw5klmBlj6gVMwjQ3Pz5Jgu7F3d0jcDVuEWdw==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.7.0.tgz", + "integrity": "sha512-QdgZ5bjR1WAlpLaO5yHepFvC+o3rCr6wpfE2tpJNMkXdulf2jKomQBdNRQITF3ZKHNlT71syG98yQP03gasgnA==", "dev": true, "requires": { "path-parse": "1.0.5" @@ -5395,7 +5396,7 @@ "integrity": "sha1-HFz2y8zDLm9jk4C9T5kfq5up0iY=", "dev": true, "requires": { - "core-js": "2.5.4" + "core-js": "2.5.5" } }, "esquery": { @@ -6987,9 +6988,9 @@ } }, "google-auth-library": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-1.3.2.tgz", - "integrity": "sha512-aRz0om4Bs85uyR2Ousk3Gb8Nffx2Sr2RoKts1smg1MhRwrehE1aD1HC4RmprNt1HVJ88IDnQ8biJQ/aXjiIxlQ==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-1.4.0.tgz", + "integrity": "sha512-vWRx6pJulK7Y5V/Xyr7MPMlx2mWfmrUVbcffZ7hpq8ElFg5S8WY6PvjMovdcr6JfuAwwpAX4R0I1XOcyWuBcUw==", "requires": { "axios": "0.18.0", "gcp-metadata": "0.6.3", @@ -7007,7 +7008,7 @@ "requires": { "async": "2.6.0", "gcp-metadata": "0.6.3", - "google-auth-library": "1.3.2", + "google-auth-library": "1.4.0", "request": "2.85.0" } }, @@ -7992,9 +7993,9 @@ "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" }, "isemail": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/isemail/-/isemail-3.1.1.tgz", - "integrity": "sha512-mVjAjvdPkpwXW61agT2E9AkGoegZO7SdJGCezWwxnETL58f5KwJ4vSVAMBUL5idL6rTlYAIGkX3n4suiviMLNw==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/isemail/-/isemail-3.1.2.tgz", + "integrity": "sha512-zfRhJn9rFSGhzU5tGZqepRSAj3+g6oTOHxMGGriWNJZzyLPUK8H7VHpqKntegnW8KLyGA9zwuNaCoopl40LTpg==", "dev": true, "requires": { "punycode": "2.1.0" @@ -8051,7 +8052,7 @@ "dev": true, "requires": { "hoek": "5.0.3", - "isemail": "3.1.1", + "isemail": "3.1.2", "topo": "3.0.0" }, "dependencies": { @@ -8071,7 +8072,7 @@ "requires": { "argparse": "1.0.10", "axios": "0.18.0", - "npm-package-arg": "6.0.0", + "npm-package-arg": "6.1.0", "package-json": "4.0.1", "pify": "3.0.0", "spdx-correct": "3.0.0", @@ -9474,9 +9475,9 @@ } }, "nock": { - "version": "9.2.3", - "resolved": "https://registry.npmjs.org/nock/-/nock-9.2.3.tgz", - "integrity": "sha512-4XYNSJDJ/PvNoH+cCRWcGOOFsq3jtZdNTRIlPIBA7CopGWJO56m5OaPEjjJ3WddxNYfe5HL9sQQAtMt8oyR9AA==", + "version": "9.2.5", + "resolved": "https://registry.npmjs.org/nock/-/nock-9.2.5.tgz", + "integrity": "sha512-ciCpyEq72Ws6/yhdayDfd0mAb3eQ7/533xKmFlBQZ5CDwrL0/bddtSicfL7R07oyvPAuegQrR+9ctrlPEp0EjQ==", "dev": true, "requires": { "chai": "4.1.2", @@ -9571,9 +9572,9 @@ } }, "npm-package-arg": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-6.0.0.tgz", - "integrity": "sha512-hwC7g81KLgRmchv9ol6f3Fx4Yyc9ARX5X5niDHVILgpuvf08JRIgOZcEfpFXli3BgESoTrkauqorXm6UbvSgSg==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-6.1.0.tgz", + "integrity": "sha512-zYbhP2k9DbJhA0Z3HKUePUgdB1x7MfIfKssC+WLPFMKTBZKpZh5m13PgexJjCq6KW7j17r0jHWcCpxEqnnncSA==", "dev": true, "requires": { "hosted-git-info": "2.6.0", @@ -13259,7 +13260,7 @@ "integrity": "sha1-7bo1LT7YpgMRTWZyZazOYNaJzN8=", "dev": true, "requires": { - "core-js": "2.5.4", + "core-js": "2.5.5", "power-assert-context-traversal": "1.1.1" } }, @@ -13271,7 +13272,7 @@ "requires": { "acorn": "4.0.13", "acorn-es7-plugin": "1.1.7", - "core-js": "2.5.4", + "core-js": "2.5.5", "espurify": "1.7.0", "estraverse": "4.2.0" }, @@ -13290,7 +13291,7 @@ "integrity": "sha1-iMq8oNE7Y1nwfT0+ivppkmRXftk=", "dev": true, "requires": { - "core-js": "2.5.4", + "core-js": "2.5.5", "estraverse": "4.2.0" } }, @@ -13300,7 +13301,7 @@ "integrity": "sha1-XcEl7VCj37HdomwZNH879Y7CiEo=", "dev": true, "requires": { - "core-js": "2.5.4", + "core-js": "2.5.5", "power-assert-context-formatter": "1.1.1", "power-assert-context-reducer-ast": "1.1.2", "power-assert-renderer-assertion": "1.1.1", @@ -13331,7 +13332,7 @@ "integrity": "sha1-10Odl9hRVr5OMKAPL7WnJRTOPAg=", "dev": true, "requires": { - "core-js": "2.5.4", + "core-js": "2.5.5", "diff-match-patch": "1.0.0", "power-assert-renderer-base": "1.1.1", "stringifier": "1.3.0", @@ -13344,7 +13345,7 @@ "integrity": "sha1-ZV+PcRk1qbbVQbhjJ2VHF8Y3qYY=", "dev": true, "requires": { - "core-js": "2.5.4", + "core-js": "2.5.5", "power-assert-renderer-base": "1.1.1", "power-assert-util-string-width": "1.1.1", "stringifier": "1.3.0" @@ -13806,7 +13807,7 @@ "integrity": "sha512-8H7Ehijd4js+s6wuVPLjwORxD4zeuyjYugprdOXlPSqaApmL/QOy+EB/beICHVCHkGMKNh5rvihb5ov+IDw4mg==", "requires": { "aws-sign2": "0.7.0", - "aws4": "1.6.0", + "aws4": "1.7.0", "caseless": "0.12.0", "combined-stream": "1.0.6", "extend": "3.0.1", @@ -14560,7 +14561,7 @@ "integrity": "sha1-3vGDQvaTPbDy2/yaoCF1tEjBeVk=", "dev": true, "requires": { - "core-js": "2.5.4", + "core-js": "2.5.5", "traverse": "0.6.6", "type-name": "2.0.2" } @@ -14900,16 +14901,16 @@ "glob": "7.1.2", "js-yaml": "3.11.0", "minimatch": "3.0.4", - "resolve": "1.6.0", + "resolve": "1.7.0", "semver": "5.5.0", "tslib": "1.9.0", - "tsutils": "2.26.0" + "tsutils": "2.26.1" }, "dependencies": { "resolve": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.6.0.tgz", - "integrity": "sha512-mw7JQNu5ExIkcw4LPih0owX/TZXjD/ZUF/ZQ/pDnkw3ZKhDcZZw5klmBlj6gVMwjQ3Pz5Jgu7F3d0jcDVuEWdw==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.7.0.tgz", + "integrity": "sha512-QdgZ5bjR1WAlpLaO5yHepFvC+o3rCr6wpfE2tpJNMkXdulf2jKomQBdNRQITF3ZKHNlT71syG98yQP03gasgnA==", "dev": true, "requires": { "path-parse": "1.0.5" @@ -14918,9 +14919,9 @@ } }, "tsutils": { - "version": "2.26.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.26.0.tgz", - "integrity": "sha512-hXUttgxeaZ/uPP/dpeiWUHbP5h744mPrfN2YFFtcZzd7vBRPBP6Knr0Mt6Bd+5SntMn8/1r6IGFeYPDSBIIPpg==", + "version": "2.26.1", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.26.1.tgz", + "integrity": "sha512-bnm9bcjOqOr1UljleL94wVCDlpa6KjfGaTkefeLch4GRafgDkROxPizbB/FxTEdI++5JqhxczRy/Qub0syNqZA==", "dev": true, "requires": { "tslib": "1.9.0" diff --git a/src/index.ts b/src/index.ts index 0753791cf..95506e734 100644 --- a/src/index.ts +++ b/src/index.ts @@ -105,7 +105,10 @@ export class ErrorReporting { request?: manualRequestExtractor.Request, additionalMessage?: string|{}, callback?: manualInterface.Callback|{}|string) => ErrorMessage; event: () => ErrorMessage; - hapi: {register: (server: {}, options: {}, next: Function) => void}; + hapi: { + register: (server: {}, options: {}, next?: Function) => void; name: string; + version?: string; + }; express: (err: {}, req: {}, res: {}, next: Function) => void; restify: (server: {}) => RestifyRequestHandler | RestifyRequestHandler[]; // tslint:disable-next-line:no-any diff --git a/src/interfaces/hapi.ts b/src/interfaces/hapi.ts index da42a7fe2..10181a232 100644 --- a/src/interfaces/hapi.ts +++ b/src/interfaces/hapi.ts @@ -37,13 +37,13 @@ import * as hapi from 'hapi'; * @returns {ErrorMessage} - a partially or fully populated instance of * ErrorMessage */ -function hapiErrorHandler(req: hapi.Request, err: {}, config: Configuration) { +function hapiErrorHandler(err: {}, req?: hapi.Request, config?: Configuration) { let service = ''; let version: string|undefined = ''; if (isObject(config)) { - service = config.getServiceContext().service; - version = config.getServiceContext().version; + service = config!.getServiceContext().service; + version = config!.getServiceContext().version; } const em = @@ -78,40 +78,68 @@ export function makeHapiPlugin(client: RequestHandler, config: Configuration) { * plugin * @returns {Undefined} - returns the execution of the next callback */ - function hapiRegisterFunction(server: {}, options: {}, next: Function) { - if (isObject(server)) { - if (isFunction((server as hapi.Server).on)) { - (server as hapi.Server).on('request-error', (req, err) => { - client.sendError(hapiErrorHandler(req, err, config)); - }); - } + function hapiRegisterFunction( + server: any, // tslint:disable-line:no-any + options: {}, next?: Function) { + if (server) { + if (server.events && server.events.on) { + // Hapi 17 is being used + server.events.on( + 'log', (event: {error?: {}; channel: string;}, tags: {}) => { + if (event.error && event.channel === 'app') { + client.sendError(hapiErrorHandler(event.error)); + } + }); + + server.events.on( + 'request', + (request: hapi.Request, event: {error?: {}; channel: string;}, + tags: {}) => { + if (event.error && event.channel === 'error') { + client.sendError(hapiErrorHandler(event.error, request)); + } + }); + } else { + if (isFunction(server.on)) { + server.on('request-error', (req: hapi.Request, err: {}) => { + client.sendError(hapiErrorHandler(err, req, config)); + }); + } - if (isFunction((server as hapi.Server).ext)) { - (server as hapi.Server).ext('onPreResponse', (request, reply) => { - if (isObject(request) && isObject(request.response) && - // TODO: Handle the case when `request.response` is null - request.response!.isBoom) { - const em = hapiErrorHandler( - request, - // TODO: Handle the case when `request.response` is null - // TODO: Handle the type conflict that requires a cast to string - new Error(request.response!.message as {} as string), config); - client.sendError(em); - } + if (isFunction(server.ext)) { + server.ext( + 'onPreResponse', + (request: hapi.Request, reply: hapi.ReplyWithContinue) => { + if (isObject(request) && isObject(request.response) && + // TODO: Handle the case when `request.response` is null + request.response!.isBoom) { + const em = hapiErrorHandler( + // TODO: Handle the case when `request.response` is null + // TODO: Handle the type conflict that requires a cast to + // string + new Error(request.response!.message as {} as string), + request, config); + client.sendError(em); + } - if (reply && isFunction(reply.continue)) { - reply.continue(); - } - }); + if (reply && isFunction(reply.continue)) { + reply.continue(); + } + }); + } } } if (isFunction(next)) { - return next(); + return next!(); } } - const hapiPlugin = {register: hapiRegisterFunction}; + const hapiPlugin = { + register: hapiRegisterFunction, + name: packageJson.name, + version: packageJson.version + }; (hapiPlugin.register as {} as {attributes: {}}).attributes = { name: packageJson.name, diff --git a/src/request-extractors/hapi.ts b/src/request-extractors/hapi.ts index 9ab1f4b76..31be14f86 100644 --- a/src/request-extractors/hapi.ts +++ b/src/request-extractors/hapi.ts @@ -77,22 +77,22 @@ function extractRemoteAddressFromRequest(req: hapi.Request) { * @returns {RequestInformationContainer} - an object containing the request * information in a standardized format */ -export function hapiRequestInformationExtractor(req: hapi.Request) { +export function hapiRequestInformationExtractor(req?: hapi.Request) { const returnObject = new RequestInformationContainer(); - if (!isObject(req) || !isObject(req.headers) || isFunction(req) || + if (!isObject(req) || !isObject(req!.headers) || isFunction(req) || isArray(req)) { return returnObject; } returnObject - .setMethod(req.method) + .setMethod(req!.method) // TODO: Address the type conflict that requires a cast to string - .setUrl(req.url as {} as string) - .setUserAgent(req.headers['user-agent']) - .setReferrer(req.headers.referrer) - .setStatusCode(attemptToExtractStatusCode(req)) - .setRemoteAddress(extractRemoteAddressFromRequest(req)); + .setUrl(req!.url as {} as string) + .setUserAgent(req!.headers['user-agent']) + .setReferrer(req!.headers.referrer) + .setStatusCode(attemptToExtractStatusCode(req!)) + .setRemoteAddress(extractRemoteAddressFromRequest(req!)); return returnObject; } diff --git a/system-test/test-install.ts b/system-test/test-install.ts index 170f4f904..c38a05877 100644 --- a/system-test/test-install.ts +++ b/system-test/test-install.ts @@ -108,8 +108,37 @@ server.route({ server.register(errors.hapi); `, description: 'uses hapi16', - dependencies: ['hapi@16.6.3'], - devDependencies: ['@types/hapi@16.1.14'] + dependencies: ['hapi@16.x.x'], + devDependencies: ['@types/hapi@16.x.x'] + }, + { + code: `import * as hapi from 'hapi'; + +import {ErrorReporting} from '@google-cloud/error-reporting'; +const errors = new ErrorReporting(); + +async function start() { + const server = new hapi.Server({ + host: '0.0.0.0', + port: 3000 + }); + + server.route({ + method: 'GET', + path: '/error', + handler: async (request, h) => { + throw new Error(\`You requested an error at ${new Date()}\`); + } + }); + + await server.register(errors.hapi); +} + +start().catch(console.error); +`, + description: 'uses hapi17', + dependencies: ['hapi@17.x.x'], + devDependencies: ['@types/hapi@17.x.x'] }, { code: `import * as Koa from 'koa'; @@ -240,7 +269,36 @@ server.route({ server.register(errors.hapi); `, description: 'uses hapi16', - dependencies: ['hapi@16.6.3'], + dependencies: ['hapi@16.x.x'], + devDependencies: [] + }, + { + code: `const hapi = require('hapi'); + +const ErrorReporting = require('@google-cloud/error-reporting').ErrorReporting; +const errors = new ErrorReporting(); + +async function start() { + const server = new hapi.Server({ + host: '0.0.0.0', + port: 3000 + }); + + server.route({ + method: 'GET', + path: '/error', + handler: async (request, h) => { + throw new Error(\`You requested an error at ${new Date()}\`); + } + }); + + await server.register(errors.hapi); +} + +start().catch(console.error); +`, + description: 'uses hapi17', + dependencies: ['hapi@17.x.x'], devDependencies: [] }, { diff --git a/test/unit/interfaces/hapi.ts b/test/unit/interfaces/hapi.ts index 93760eab6..7b478184f 100644 --- a/test/unit/interfaces/hapi.ts +++ b/test/unit/interfaces/hapi.ts @@ -26,6 +26,10 @@ import {EventEmitter} from 'events'; import * as config from '../../../src/configuration'; import {RequestHandler} from '../../../src/google-apis/auth-client'; import {FakeConfiguration as Configuration} from '../../fixtures/configuration'; +import * as http from 'http'; +import * as hapi from 'hapi'; + +const packageJson = require('../../../../package.json'); type HapiPlugin = { register: @@ -174,4 +178,101 @@ describe('Hapi interface', () => { fakeServer.emit(EVENT, {response: {isBoom: true}}, {continue() {}}); }); }); + describe('Hapi17', () => { + const errorsSent: ErrorMessage[] = []; + // the only method in the client that should be used is `sendError` + const fakeClient = { + sendError: + (errorMessage: ErrorMessage, + userCb?: ( + err: Error|null, response: http.ServerResponse|null, body: {}) => + void) => { + errorsSent.push(errorMessage); + } + } as {} as RequestHandler; + + // the configuration should be not be needed to send errors correctly + const plugin = hapiInterface(fakeClient, {} as Configuration); + + afterEach(() => { + errorsSent.length = 0; + }); + + it('Plugin should have name and version properties', () => { + assert.strictEqual(plugin.name, packageJson.name); + assert.strictEqual(plugin.version, packageJson.version); + }); + + it(`Should record 'log' events correctly`, () => { + const fakeServer = {events: new EventEmitter()}; + + // emulate how the hapi server would register itself + plugin.register(fakeServer, {}); + + // emulate the hapi server emitting a log event + const testError = new Error('Error emitted through a log event'); + + // this event should not be recorded + fakeServer.events.emit('log', {error: testError, channel: 'internal'}); + + // this event should be recorded + fakeServer.events.emit('log', {error: testError, channel: 'app'}); + + assert.strictEqual(errorsSent.length, 1); + const errorMessage = errorsSent[0]; + + // note: the error's stack contains the error message + assert.strictEqual(errorMessage.message, testError.stack); + }); + + it(`Should record 'request' events correctly`, () => { + const fakeServer = {events: new EventEmitter()}; + + // emulate how the hapi server would register itself + plugin.register(fakeServer, {}); + + // emulate the hapi server emitting a request event + // a cast to hapi.Request is being done since only the listed + // properties are the properties that are being tested. In + // addition other properties of hapi.Request should be needed + // to properly send the error. + const fakeRequest = { + method: 'custom-method', + url: 'custom-url', + headers: { + 'user-agent': 'custom-user-agent', + referrer: 'custom-referrer', + 'x-forwarded-for': 'some-remote-address' + }, + response: {statusCode: 42} + } as {} as hapi.Request; + + const testError = new Error('Error emitted through a request event'); + + // this event should not be recorded + fakeServer.events.emit( + 'request', fakeRequest, {error: testError, channel: 'internal'}); + + // this event should be recorded + fakeServer.events.emit( + 'request', fakeRequest, {error: testError, channel: 'error'}); + + assert.strictEqual(errorsSent.length, 1); + const errorMessage = errorsSent[0]; + + // note: the error's stack contains the error message + assert.strictEqual(errorMessage.message, testError.stack); + assert.strictEqual( + errorMessage.context.httpRequest.method, 'custom-method'); + assert.strictEqual(errorMessage.context.httpRequest.url, 'custom-url'); + assert.strictEqual( + errorMessage.context.httpRequest.userAgent, 'custom-user-agent'); + assert.strictEqual( + errorMessage.context.httpRequest.referrer, 'custom-referrer'); + assert.strictEqual( + errorMessage.context.httpRequest.remoteIp, 'some-remote-address'); + assert.strictEqual( + errorMessage.context.httpRequest.responseStatusCode, 42); + }); + }); });