diff --git a/.github/workflows/error-reporting.yaml b/.github/workflows/error-reporting.yaml new file mode 100644 index 0000000000..ab072c8e21 --- /dev/null +++ b/.github/workflows/error-reporting.yaml @@ -0,0 +1,66 @@ +name: error-reporting +on: + push: + branches: + - main + paths: + - "error-reporting/**" + pull_request: + paths: + - "error-reporting/**" + pull_request_target: + types: [labeled] + schedule: + - cron: "0 0 * * 0" +jobs: + test: + if: ${{ github.event.action != 'labeled' || github.event.label.name == 'actions:force-run' }} + runs-on: ubuntu-latest + timeout-minutes: 60 + permissions: + contents: "write" + pull-requests: "write" + id-token: "write" + steps: + - uses: actions/checkout@v3.1.0 + with: + ref: ${{github.event.pull_request.head.sha}} + - uses: "google-github-actions/auth@v0.8.3" + with: + workload_identity_provider: "projects/1046198160504/locations/global/workloadIdentityPools/github-actions-pool/providers/github-actions-provider" + service_account: "kokoro-system-test@long-door-651.iam.gserviceaccount.com" + create_credentials_file: "true" + access_token_lifetime: 600s + - uses: actions/setup-node@v3.5.1 + with: + node-version: 16 + - run: npm install + working-directory: error-reporting + - run: npm test + working-directory: error-reporting + env: + MOCHA_REPORTER_SUITENAME: error_reporting + MOCHA_REPORTER_OUTPUT: error_reporting_sponge_log.xml + MOCHA_REPORTER: xunit + - if: ${{ github.event.action == 'labeled' && github.event.label.name == 'actions:force-run' }} + uses: actions/github-script@v6 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + try { + await github.rest.issues.removeLabel({ + name: 'actions:force-run', + owner: 'GoogleCloudPlatform', + repo: 'nodejs-docs-samples', + issue_number: context.payload.pull_request.number + }); + } catch (e) { + if (!e.message.includes('Label does not exist')) { + throw e; + } + } + - if: ${{ github.event_name == 'schedule'}} + run: | + curl https://github.com/googleapis/repo-automation-bots/releases/download/flakybot-1.1.0/flakybot -o flakybot -s -L + chmod +x ./flakybot + ./flakybot --repo GoogleCloudPlatform/nodejs-docs-samples --commit_hash ${{github.sha}} --build_url https://github.com/${{github.repository}}/actions/runs/${{github.run_id}} diff --git a/.github/workflows/workflows.json b/.github/workflows/workflows.json index 36ee104fd7..9b8a6675b4 100644 --- a/.github/workflows/workflows.json +++ b/.github/workflows/workflows.json @@ -30,6 +30,7 @@ "datacatalog/quickstart", "endpoints/getting-started", "endpoints/getting-started-grpc", + "error-reporting", "functions/firebase", "functions/helloworld", "functions/http", diff --git a/CODEOWNERS b/CODEOWNERS index 85d85eba5b..aea26aa813 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -39,3 +39,6 @@ monitoring/opencensus @GoogleCloudPlatform/nodejs-samples-reviewers # Data & AI contact-center-insights @GoogleCloudPlatform/dee-data-ai @GoogleCloudPlatform/nodejs-samples-reviewers talent @GoogleCloudPlatform/dee-data-ai @GoogleCloudPlatform/nodejs-samples-reviewers + +# DEE-PO +error-reporting @GoogleCloudPlatform/dee-platform-ops @GoogleCloudPlatform/nodejs-samples-reviewers \ No newline at end of file diff --git a/error-reporting/.eslintrc.yml b/error-reporting/.eslintrc.yml new file mode 100644 index 0000000000..282535f55f --- /dev/null +++ b/error-reporting/.eslintrc.yml @@ -0,0 +1,3 @@ +--- +rules: + no-console: off diff --git a/error-reporting/explicitSetup.js b/error-reporting/explicitSetup.js new file mode 100644 index 0000000000..afb0830d26 --- /dev/null +++ b/error-reporting/explicitSetup.js @@ -0,0 +1,38 @@ +// Copyright 2017 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// sample-metadata: +// title: Explicit setup +// description: Reports a simple error using explicit credentials. +// usage: node explicitSetup.js + +'use strict'; + +function explicitSetup() { + // [START error_reporting_setup_nodejs_explicit] + // Imports the Google Cloud client library + const {ErrorReporting} = require('@google-cloud/error-reporting'); + + // Instantiates a client + const errors = new ErrorReporting({ + projectId: 'your-project-id', + keyFilename: '/path/to/key.json', + }); + + // Reports a simple error + errors.report('Something broke!'); + // [END error_reporting_setup_nodejs_explicit] +} + +explicitSetup(); diff --git a/error-reporting/express.js b/error-reporting/express.js new file mode 100644 index 0000000000..685c7d5f04 --- /dev/null +++ b/error-reporting/express.js @@ -0,0 +1,54 @@ +// Copyright 2017 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// sample-metadata: +// title: Express integration +// description: Starts and Express service with integrated error reporting. +// usage: node express.js + +'use strict'; + +function express() { + // [START error_reporting_setup_nodejs_express] + const express = require('express'); + + // Imports the Google Cloud client library + const {ErrorReporting} = require('@google-cloud/error-reporting'); + + // Instantiates a client + const errors = new ErrorReporting(); + + const app = express(); + + app.get('/error', (req, res, next) => { + res.send('Something broke!'); + next(new Error('Custom error message')); + }); + + app.get('/exception', () => { + JSON.parse('{"malformedJson": true'); + }); + + // Note that express error handling middleware should be attached after all + // the other routes and use() calls. See the Express.js docs. + app.use(errors.express); + + const PORT = process.env.PORT || 8080; + app.listen(PORT, () => { + console.log(`App listening on port ${PORT}`); + console.log('Press Ctrl+C to quit.'); + }); + // [END error_reporting_setup_nodejs_express] +} +express(); diff --git a/error-reporting/implicitSetup.js b/error-reporting/implicitSetup.js new file mode 100644 index 0000000000..f8d7eb80c8 --- /dev/null +++ b/error-reporting/implicitSetup.js @@ -0,0 +1,35 @@ +// Copyright 2017 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// sample-metadata: +// title: Implicit setup +// description: Reports a simple error using implicit credentials. +// usage: node implicitSetup.js + +'use strict'; + +function setupImplicit() { + // [START error_reporting_setup_nodejs_implicit] + // Imports the Google Cloud client library + const {ErrorReporting} = require('@google-cloud/error-reporting'); + + // Instantiates a client + const errors = new ErrorReporting(); + + // Reports a simple error + errors.report('Something broke!'); + // [END error_reporting_setup_nodejs_implicit] +} + +setupImplicit(); diff --git a/error-reporting/manual.js b/error-reporting/manual.js new file mode 100644 index 0000000000..229d881c52 --- /dev/null +++ b/error-reporting/manual.js @@ -0,0 +1,53 @@ +// Copyright 2017 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// sample-metadata: +// title: Manual reporting +// description: Manually reports errors. +// usage: node manual.js + +'use strict'; + +function manual() { + // [START error_reporting_setup_nodejs_manual] + // Imports the Google Cloud client library + const {ErrorReporting} = require('@google-cloud/error-reporting'); + + // Instantiates a client + const errors = new ErrorReporting(); + + // Use the error message builder to customize all fields ... + const errorEvent = errors.event(); + + // Add error information + errorEvent.setMessage('My error message'); + errorEvent.setUser('root@nexus'); + + // Report the error event + errors.report(errorEvent, () => { + console.log('Done reporting error event!'); + }); + + // Report an Error object + errors.report(new Error('My error message'), () => { + console.log('Done reporting Error object!'); + }); + + // Report an error by provided just a string + errors.report('My error message', () => { + console.log('Done reporting error string!'); + }); + // [END error_reporting_setup_nodejs_manual] +} +manual(); diff --git a/error-reporting/package.json b/error-reporting/package.json new file mode 100644 index 0000000000..1fc957e27c --- /dev/null +++ b/error-reporting/package.json @@ -0,0 +1,26 @@ +{ + "name": "nodejs-docs-samples-error-reporting", + "private": true, + "license": "Apache-2.0", + "author": "Google Inc.", + "repository": "googleapis/nodejs-error-reporting", + "files": [ + "*.js", + "!test/" + ], + "engines": { + "node": ">=12.0.0" + }, + "scripts": { + "test": "mocha --timeout=600000" + }, + "dependencies": { + "@google-cloud/error-reporting": "^3.0.4", + "express": "^4.16.3" + }, + "devDependencies": { + "chai": "^4.2.0", + "gaxios": "^5.0.0", + "mocha": "^8.0.0" + } +} \ No newline at end of file diff --git a/error-reporting/quickstart.js b/error-reporting/quickstart.js new file mode 100644 index 0000000000..4cffc3795d --- /dev/null +++ b/error-reporting/quickstart.js @@ -0,0 +1,29 @@ +// Copyright 2017 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +'use strict'; + +function quickstart() { + // [START error_reporting_quickstart] + // Imports the Google Cloud client library + const {ErrorReporting} = require('@google-cloud/error-reporting'); + + // Instantiates a client + const errors = new ErrorReporting(); + + // Reports a simple error + errors.report('Something broke!'); + // [END error_reporting_quickstart] +} +quickstart(); diff --git a/error-reporting/test/snippets.test.js b/error-reporting/test/snippets.test.js new file mode 100644 index 0000000000..d5d217f43c --- /dev/null +++ b/error-reporting/test/snippets.test.js @@ -0,0 +1,80 @@ +// Copyright 2017 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +'use strict'; + +const {assert} = require('chai'); +const {describe, it} = require('mocha'); +const {execSync, spawn} = require('child_process'); +const {request} = require('gaxios'); + +const exec = cmd => execSync(cmd, {encoding: 'utf8'}); + +/** + * Start the express server, and wait for it to start serving content before + * resolving the promise. + */ +async function startExpress() { + return new Promise((resolve, reject) => { + const p = spawn('node', ['express']); + p.on('error', reject); + p.stdout.on('data', data => { + if (data.includes('Press Ctrl+C to quit.')) { + resolve(p); + } + }); + }); +} + +describe(__filename, () => { + it('should setup using implicit credentials', () => { + // There's no output, the command should just succeed + exec('node implicitSetup'); + }); + + it('should report errors manually', () => { + const output = exec('node manual'); + assert.include(output, 'Done reporting error event!'); + assert.include(output, 'Done reporting Error object!'); + assert.include(output, 'Done reporting error string!'); + }); + + it('should report errors with the express test', async () => { + let pid; + try { + pid = await startExpress(); + const res = await request({ + url: 'http://localhost:8080/error', + }); + assert.include(res.data, 'Something broke!'); + } finally { + if (pid) pid.kill(); + } + }); + + it('should report exceptions from the express test', async () => { + let pid; + try { + pid = await startExpress(); + const res = await request({ + url: 'http://localhost:8080/exception', + validateStatus: () => true, + }); + assert.strictEqual(res.status, 500); + assert.include(res.data, 'SyntaxError'); + } finally { + if (pid) pid.kill(); + } + }); +});