From c4e7c1971f9b5778ea1f212e94f253f01c113d7b Mon Sep 17 00:00:00 2001 From: Kenneth Rosario Acevedo Date: Mon, 13 Mar 2023 16:48:41 +0000 Subject: [PATCH] chore(functions/v2/tips/retry): create new cloud event sample from background sample --- functions/v2/tips/retry/index.js | 66 ++++++++++++++++ functions/v2/tips/retry/package.json | 25 ++++++ .../v2/tips/retry/test/integration.test.js | 65 ++++++++++++++++ functions/v2/tips/retry/test/unit.test.js | 76 +++++++++++++++++++ 4 files changed, 232 insertions(+) create mode 100644 functions/v2/tips/retry/index.js create mode 100644 functions/v2/tips/retry/package.json create mode 100644 functions/v2/tips/retry/test/integration.test.js create mode 100644 functions/v2/tips/retry/test/unit.test.js diff --git a/functions/v2/tips/retry/index.js b/functions/v2/tips/retry/index.js new file mode 100644 index 00000000000..b4aea79e489 --- /dev/null +++ b/functions/v2/tips/retry/index.js @@ -0,0 +1,66 @@ +// Copyright 2023 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. + +// [START functions_cloudevent_tips_retry] +const functions = require('@google-cloud/functions-framework'); + +/** + * Register a Cloud Event Function that demonstrates + * how to toggle retries using a promise + * + * @param {object} event The Cloud Event for the function trigger. + */ +functions.cloudEvent('retryPromise', cloudEvent => { + // The Pub/Sub event payload is passed as the CloudEvent's data payload. + // See the documentation for more details: + // https://cloud.google.com/eventarc/docs/cloudevents#pubsub + const base64PubsubMessage = cloudEvent.data.message.data; + const jsonString = Buffer.from(base64PubsubMessage, 'base64').toString(); + + tryAgain = JSON.parse(jsonString).retry; + + if (tryAgain) { + throw new Error('Retrying...'); + } else { + console.error('Not retrying...'); + return Promise.resolve(); + } +}); + +/** + * Cloud Event Function that demonstrates + * how to toggle retries using a callback + * + * @param {object} event The Cloud Event for the function trigger. + * @param {function} callback The callback function. + */ +functions.cloudEvent('retryCallback', (cloudEvent, callback) => { + // The Pub/Sub event payload is passed as the CloudEvent's data payload. + // See the documentation for more details: + // https://cloud.google.com/eventarc/docs/cloudevents#pubsub + const base64PubsubMessage = cloudEvent.data.message.data; + const jsonString = Buffer.from(base64PubsubMessage, 'base64').toString(); + + tryAgain = JSON.parse(jsonString).retry; + const err = new Error('Error!'); + + if (tryAgain) { + console.error('Retrying:', err); + callback(err); + } else { + console.error('Not retrying:', err); + callback(); + } +}); +// [END functions_cloudevent_tips_retry] diff --git a/functions/v2/tips/retry/package.json b/functions/v2/tips/retry/package.json new file mode 100644 index 00000000000..f3d35c1638e --- /dev/null +++ b/functions/v2/tips/retry/package.json @@ -0,0 +1,25 @@ +{ + "name": "nodejs-docs-samples-functions-tips-retry", + "version": "0.0.1", + "private": true, + "license": "Apache-2.0", + "author": "Google Inc.", + "repository": { + "type": "git", + "url": "https://github.com/GoogleCloudPlatform/nodejs-docs-samples.git" + }, + "engines": { + "node": ">=12.0.0" + }, + "scripts": { + "test": "mocha test/*.test.js --timeout=60000 --exit" + }, + "devDependencies": { + "mocha": "^9.0.0", + "sinon": "^15.0.0", + "supertest": "^6.3.3" + }, + "dependencies": { + "@google-cloud/functions-framework": "^3.1.3" + } +} diff --git a/functions/v2/tips/retry/test/integration.test.js b/functions/v2/tips/retry/test/integration.test.js new file mode 100644 index 00000000000..f0a34ea8014 --- /dev/null +++ b/functions/v2/tips/retry/test/integration.test.js @@ -0,0 +1,65 @@ +// Copyright 2023 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. + +const supertest = require('supertest'); +const functionsFramework = require('@google-cloud/functions-framework/testing'); + +require('..'); + +const jsonRetryObject = JSON.stringify({retry: true}); +const encodedRetryMessage = Buffer.from(jsonRetryObject).toString('base64'); + +const jsonDontRetryObject = JSON.stringify({retry: false}); +const encodedDontRetryMessage = + Buffer.from(jsonDontRetryObject).toString('base64'); + +describe('functions_cloudevent_tips_retry', () => { + it('should raise error to retry', async () => { + const cloudEventData = {data: {message: {}}}; + cloudEventData.data.message.data = encodedRetryMessage; + + const promiseServer = functionsFramework.getTestServer('retryPromise'); + await supertest(promiseServer) + .post('/') + .send(cloudEventData) + .set('Content-Type', 'application/json') + .expect(500); + + const callBackServer = functionsFramework.getTestServer('retryCallback'); + await supertest(callBackServer) + .post('/') + .send(cloudEventData) + .set('Content-Type', 'application/json') + .expect(500); + }); + + it('should not raise error to not retry', async () => { + const cloudEventData = {data: {message: {}}}; + cloudEventData.data.message.data = encodedDontRetryMessage; + + const promiseServer = functionsFramework.getTestServer('retryPromise'); + await supertest(promiseServer) + .post('/') + .send(cloudEventData) + .set('Content-Type', 'application/json') + .expect(204); + + const callBackServer = functionsFramework.getTestServer('retryCallback'); + await supertest(callBackServer) + .post('/') + .send(cloudEventData) + .set('Content-Type', 'application/json') + .expect(204); + }); +}); diff --git a/functions/v2/tips/retry/test/unit.test.js b/functions/v2/tips/retry/test/unit.test.js new file mode 100644 index 00000000000..05c9959b079 --- /dev/null +++ b/functions/v2/tips/retry/test/unit.test.js @@ -0,0 +1,76 @@ +// Copyright 2023 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 {getFunction} = require('@google-cloud/functions-framework/testing'); +const sinon = require('sinon'); +const assert = require('assert'); +require('..'); + +const jsonRetryObject = JSON.stringify({retry: true}); +const encodedRetryMessage = Buffer.from(jsonRetryObject).toString('base64'); + +const jsonDontRetryObject = JSON.stringify({retry: false}); +const encodedDontRetryMessage = + Buffer.from(jsonDontRetryObject).toString('base64'); + +describe('functions_cloudevent_tips_retry', () => { + it('should demonstrate retry behavior for a promise', async () => { + const retryPromise = getFunction('retryPromise'); + + // Retry by throwing an error + assert.throws(() => { + retryPromise({ + data: { + message: { + data: encodedRetryMessage, + }, + }, + }); + }, new Error('Retrying...')); + + // Terminate by returning a rejected promise + try { + await retryPromise({data: {message: {data: encodedDontRetryMessage}}}); + } catch (err) { + assert.strictEqual(err.message, 'Not retrying...'); + return Promise.resolve(); + } + }); + + it('should demonstrate retry behavior for a callback', done => { + const retryCallback = getFunction('retryCallback'); + const cb = sinon.stub(); + const err = new Error('Error!'); + + // Retry by passing an error to the callback + retryCallback( + { + data: { + message: { + data: encodedRetryMessage, + }, + }, + }, + cb + ); + assert.deepStrictEqual(cb.firstCall.args, [err]); + + // Terminate by passing nothing to the callback + retryCallback({data: {message: {data: encodedDontRetryMessage}}}, cb); + assert.deepStrictEqual(cb.secondCall.args, []); + done(); + }); +});