Skip to content

Commit

Permalink
feat: add run_events_pubsub (#1809)
Browse files Browse the repository at this point in the history
* feat: add run_events_pubsub

Signed-off-by: Grant Timmerman <timmerman+devrel@google.com>

* fix: address PR issues

Signed-off-by: Grant Timmerman <timmerman+devrel@google.com>

* fix: formalize CE function response

Signed-off-by: Grant Timmerman <timmerman+devrel@google.com>

* fix: add base64 data

Signed-off-by: Grant Timmerman <timmerman+devrel@google.com>

* docs: remove bodyparser

Signed-off-by: Grant Timmerman <timmerman+devrel@google.com>

* docs: address comments about run events pubsub

Signed-off-by: Grant Timmerman <timmerman+devrel@google.com>
  • Loading branch information
grant authored May 20, 2020
1 parent 17b8c6a commit 1fd4f1d
Show file tree
Hide file tree
Showing 8 changed files with 232 additions and 0 deletions.
2 changes: 2 additions & 0 deletions run/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
|[Hello World][helloworld]&nbsp;&#10149; | Quickstart | [<img src="https://storage.googleapis.com/cloudrun/button.svg" alt="Run on Google Cloud" height="30"/>][run_button_helloworld] |
|[System Packages][system_package] | Use system-installed binaries in your service. | [<img src="https://storage.googleapis.com/cloudrun/button.svg" alt="Run on Google Cloud" height="30">][run_button_system_package] |
|[Pub/Sub][pubsub] | Event-driven service with a Pub/Sub push subscription | [<img src="https://storage.googleapis.com/cloudrun/button.svg" alt="Run on Google Cloud" height="30"/>][run_button_pubsub] |
|[Events – Pub/Sub][events_pubsub] | Use Pub/Sub with Cloud Run | - |
|[Image Processing][image_processing] | Event-driven image analysis & transformation | [<img src="https://storage.googleapis.com/cloudrun/button.svg" alt="Run on Google Cloud" height="30"/>][run_button_image_processing] |
|[Manual Logging][manual_logging] | Structured logging without client library | [<img src="https://storage.googleapis.com/cloudrun/button.svg" alt="Run on Google Cloud" height="30"/>][run_button_manual_logging] |
|[Cloud SQL (MySQL)][mysql] | Use MySQL with Cloud Run | - |
Expand Down Expand Up @@ -127,3 +128,4 @@ for more information.
[run_button_image_processing]: https://deploy.cloud.run/?git_repo=https://github.com/GoogleCloudPlatform/nodejs-docs-samples&dir=run/image-processing
[run_button_manual_logging]: https://deploy.cloud.run/?git_repo=https://github.com/GoogleCloudPlatform/nodejs-docs-samples&dir=run/logging-manual
[run_button_hello_broken]: https://deploy.cloud.run/?git_repo=https://github.com/GoogleCloudPlatform/nodejs-docs-samples&dir=run/hello-broken
[events_pubsub]: https://deploy.cloud.run/?git_repo=https://github.com/GoogleCloudPlatform/nodejs-docs-samples&dir=run/events-pubsub
4 changes: 4 additions & 0 deletions run/events-pubsub/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Dockerfile
.dockerignore
node_modules
npm-debug.log
30 changes: 30 additions & 0 deletions run/events-pubsub/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Copyright 2020 Google LLC. All rights reserved.
# Use of this source code is governed by the Apache 2.0
# license that can be found in the LICENSE file.

# [START run_events_pubsub_dockerfile]

# Use the official lightweight Node.js 10 image.
# https://hub.docker.com/_/node
FROM node:10-slim

# Create and change to the app directory.
WORKDIR /usr/src/app

# Copy application dependency manifests to the container image.
# A wildcard is used to ensure both package.json AND package-lock.json are copied.
# Copying this separately prevents re-running npm install on every code change.
COPY package*.json ./

# Install dependencies.
# If you add a package-lock.json speed your build by switching to 'npm ci'.
# RUN npm ci --only=production
RUN npm install --production

# Copy local code to the container image.
COPY . .

# Run the web service on container startup.
CMD [ "npm", "start" ]

# [END run_events_pubsub_dockerfile]
47 changes: 47 additions & 0 deletions run/events-pubsub/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Events for Cloud Run – Pub/Sub tutorial

This sample shows how to create a service that processes Pub/Sub messages.

For more details on how to work with this sample read the [Google Cloud Run Node.js Samples README](https://github.com/GoogleCloudPlatform/nodejs-docs-samples/tree/master/run).

## Dependencies

* **express**: Web server framework.
* **mocha**: [development] Test running framework.
* **supertest**: [development] HTTP assertion test client.

## Quickstart

Create a Cloud Pub/Sub topic:

```sh
gcloud pubsub topics create my-topic
```

Create a Cloud Pub/Sub trigger:

```sh
gcloud alpha events triggers create pubsub-trigger \
--target-service cloudrun-events-pubsub \
--type com.google.cloud.pubsub.topic.publish \
--parameters topic=my-topic
```

Deploy your Cloud Run service:

```sh
gcloud builds submit \
--tag gcr.io/$(gcloud config get-value project)/cloudrun-events-pubsub
gcloud run deploy cloudrun-events-pubsub \
--image gcr.io/$(gcloud config get-value project)/cloudrun-events-pubsub
```

## Test

Test your Cloud Run service by publishing a message to the topic:

```sh
gcloud pubsub topics publish my-topic --message="Hello there"
```

You may observe the Run service receiving an event in Cloud Logging.
33 changes: 33 additions & 0 deletions run/events-pubsub/app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Copyright 2020 Google LLC. All rights reserved.
// Use of this source code is governed by the Apache 2.0
// license that can be found in the LICENSE file.

// [START run_events_pubsub_server_setup]
const express = require('express');
const app = express();
app.use(express.json());

// [END run_events_pubsub_server_setup]

// [START run_events_pubsub_handler]
app.post('/', (req, res) => {
if (!req.body) {
const msg = 'no Pub/Sub message received';
res.status(400).send(`Bad Request: ${msg}`);
return;
}
if (!req.body.message) {
const msg = 'invalid Pub/Sub message format';
res.status(400).send(`Bad Request: ${msg}`);
return;
}
const pubSubMessage = req.body.message;
const name = pubSubMessage.data
? Buffer.from(pubSubMessage.data, 'base64').toString().trim()
: 'World';

res.send(`Hello, ${name}! ID: ${req.get('ce-id') || ''}`);
});

module.exports = app;
// [END run_events_pubsub_handler]
12 changes: 12 additions & 0 deletions run/events-pubsub/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// Copyright 2020 Google LLC. All rights reserved.
// Use of this source code is governed by the Apache 2.0
// license that can be found in the LICENSE file.

// [START run_pubsub_server]
const app = require('./app.js');
const PORT = process.env.PORT || 8080;

app.listen(PORT, () =>
console.log(`nodejs-run-events-pubsub listening on port ${PORT}`)
);
// [END run_pubsub_server]
29 changes: 29 additions & 0 deletions run/events-pubsub/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"name": "cloud-run-events-pubsub",
"version": "1.0.0",
"private": true,
"description": "Simple Events for Cloud Run – Pub/Sub sample",
"main": "index.js",
"author": "Google LLC",
"license": "Apache-2.0",
"repository": {
"type": "git",
"url": "https://github.com/GoogleCloudPlatform/nodejs-docs-samples.git"
},
"engines": {
"node": ">= 8.0.0"
},
"scripts": {
"start": "node index.js",
"test": "mocha test/*.test.js --check-leaks"
},
"dependencies": {
"express": "^4.16.4"
},
"devDependencies": {
"mocha": "^7.0.0",
"sinon": "^9.0.0",
"supertest": "^4.0.2",
"uuid": "^8.0.0"
}
}
75 changes: 75 additions & 0 deletions run/events-pubsub/test/app.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
// Copyright 2019, 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.

// NOTE:
// This app can only be fully tested when deployed, because
// Pub/Sub requires a live endpoint URL to hit. Nevertheless,
// these tests mock it and partially test it locally.

'use strict';

const assert = require('assert');
const path = require('path');
const supertest = require('supertest');

let request;

describe('Unit Tests', () => {
before(() => {
const app = require(path.join(__dirname, '..', 'app'));
request = supertest(app);
});

describe('should fail', () => {
it(`on a Bad Request with an empty payload`, async () => {
await request.post('/').type('json').send('').expect(400);
});

it(`on a Bad Request with an invalid payload`, async () => {
await request
.post('/')
.type('json')
.send({nomessage: 'invalid'})
.expect(400);
});

it(`on a Bad Request with an invalid mimetype`, async () => {
await request.post('/').type('text').send('{message: true}').expect(400);
});
});

describe('should succeed', () => {
const data = Buffer.from('World').toString(`base64`);

it(`with a minimally valid Pub/Sub Message`, async () => {
await request
.post('/')
.type('json')
.send({message: {data}})
.expect((res) => {
assert.equal(res.text, 'Hello, World! ID: ');
});
});

it(`with CloudEvent HTTP headers`, async () => {
await request
.post('/')
.type('json')
.set('ce-id', 1234)
.send({message: {data}})
.expect((res) => {
assert.equal(res.text, 'Hello, World! ID: 1234');
});
});
});
});

0 comments on commit 1fd4f1d

Please sign in to comment.