From 9d64ae134ebe1ec3ff236eb43c637189eb695abd Mon Sep 17 00:00:00 2001 From: Kelsey Krippaehne Date: Thu, 2 Apr 2020 21:15:04 -0700 Subject: [PATCH 01/96] Add service-to-service Node.js sample --- run/authentication/auth.js | 52 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 run/authentication/auth.js diff --git a/run/authentication/auth.js b/run/authentication/auth.js new file mode 100644 index 0000000000..c152408e4a --- /dev/null +++ b/run/authentication/auth.js @@ -0,0 +1,52 @@ +// 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 +// +// https://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 run_service_to_service_auth] +// Make sure to `npm install gcp-metadata` and `npm install got` or add the dependencies to your package.json +const gcpMetadata = require('gcp-metadata') +const got = require('got'); + +const requestServiceToken = async () => { + try { + + // Add the URL of your receiving service + const receivingServiceURL = ... + + // Set up the metadata server request options + // See https://cloud.google.com/compute/docs/instances/verifying-instance-identity#request_signature + const metadataServerTokenPath = 'service-accounts/default/identity?audience=' + receivingServiceURL; + const tokenRequestOptions = { + headers: { + 'Metadata-Flavor': 'Google' + } + }; + + // Fetch the token and then provide it in the request to the receiving service + const tokenResponse = await gcpMetadata.instance(metadataServerTokenPath, tokenRequestOptions); + const serviceRequestOptions = { + headers: { + 'Authorization': 'bearer ' + tokenResponse + } + }; + + const serviceResponse = await got(receivingServiceURL, serviceRequestOptions); + res.send(serviceResponse.body); + + } catch (error) { + console.log('Metadata server could not respond to query ', error); + res.send(error); + } +}; + +// [END run_service_to_service_auth] \ No newline at end of file From 64ab35fa4a18c1bbc2a219ab4e28f8cafba2eea1 Mon Sep 17 00:00:00 2001 From: kelsk Date: Thu, 2 Apr 2020 21:15:04 -0700 Subject: [PATCH 02/96] Add service-to-service Node.js sample --- run/authentication/auth.js | 52 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 run/authentication/auth.js diff --git a/run/authentication/auth.js b/run/authentication/auth.js new file mode 100644 index 0000000000..c152408e4a --- /dev/null +++ b/run/authentication/auth.js @@ -0,0 +1,52 @@ +// 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 +// +// https://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 run_service_to_service_auth] +// Make sure to `npm install gcp-metadata` and `npm install got` or add the dependencies to your package.json +const gcpMetadata = require('gcp-metadata') +const got = require('got'); + +const requestServiceToken = async () => { + try { + + // Add the URL of your receiving service + const receivingServiceURL = ... + + // Set up the metadata server request options + // See https://cloud.google.com/compute/docs/instances/verifying-instance-identity#request_signature + const metadataServerTokenPath = 'service-accounts/default/identity?audience=' + receivingServiceURL; + const tokenRequestOptions = { + headers: { + 'Metadata-Flavor': 'Google' + } + }; + + // Fetch the token and then provide it in the request to the receiving service + const tokenResponse = await gcpMetadata.instance(metadataServerTokenPath, tokenRequestOptions); + const serviceRequestOptions = { + headers: { + 'Authorization': 'bearer ' + tokenResponse + } + }; + + const serviceResponse = await got(receivingServiceURL, serviceRequestOptions); + res.send(serviceResponse.body); + + } catch (error) { + console.log('Metadata server could not respond to query ', error); + res.send(error); + } +}; + +// [END run_service_to_service_auth] \ No newline at end of file From 00451bfb9bd06168a8b962d02104a488431ffbd8 Mon Sep 17 00:00:00 2001 From: kelsk <38271546+kelsk@users.noreply.github.com> Date: Fri, 3 Apr 2020 14:12:51 -0700 Subject: [PATCH 03/96] Update run/authentication/auth.js Co-Authored-By: Averi Kitsch --- run/authentication/auth.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/run/authentication/auth.js b/run/authentication/auth.js index c152408e4a..6aa1a53407 100644 --- a/run/authentication/auth.js +++ b/run/authentication/auth.js @@ -1,4 +1,4 @@ -// Copyright 2019 Google LLC +// Copyright 2020 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -49,4 +49,4 @@ const requestServiceToken = async () => { } }; -// [END run_service_to_service_auth] \ No newline at end of file +// [END run_service_to_service_auth] From 86b0635f62c161c44a1bd19f404ab2b6b3a6acde Mon Sep 17 00:00:00 2001 From: kelsk Date: Fri, 3 Apr 2020 14:48:17 -0700 Subject: [PATCH 04/96] Update sample to address suggested changes --- run/authentication/auth.js | 27 ++++++++++++++++----------- run/authentication/package.json | 21 +++++++++++++++++++++ 2 files changed, 37 insertions(+), 11 deletions(-) create mode 100644 run/authentication/package.json diff --git a/run/authentication/auth.js b/run/authentication/auth.js index c152408e4a..6411bea0fb 100644 --- a/run/authentication/auth.js +++ b/run/authentication/auth.js @@ -1,4 +1,4 @@ -// Copyright 2019 Google LLC +// Copyright 2020 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -12,19 +12,20 @@ // See the License for the specific language governing permissions and // limitations under the License. +function main(receivingServiceURL = 'YOUR_RECEIVING_SERVICE_URL') { // [START run_service_to_service_auth] -// Make sure to `npm install gcp-metadata` and `npm install got` or add the dependencies to your package.json + +// Import the Metadata API const gcpMetadata = require('gcp-metadata') const got = require('got'); +// TODO(developer): Add the URL of your receiving service +// const receivingServiceURL = 'YOUR_RECEIVING_SERVICE_URL'' + const requestServiceToken = async () => { try { - // Add the URL of your receiving service - const receivingServiceURL = ... - // Set up the metadata server request options - // See https://cloud.google.com/compute/docs/instances/verifying-instance-identity#request_signature const metadataServerTokenPath = 'service-accounts/default/identity?audience=' + receivingServiceURL; const tokenRequestOptions = { headers: { @@ -33,20 +34,24 @@ const requestServiceToken = async () => { }; // Fetch the token and then provide it in the request to the receiving service - const tokenResponse = await gcpMetadata.instance(metadataServerTokenPath, tokenRequestOptions); + const token = await gcpMetadata.instance(metadataServerTokenPath, tokenRequestOptions); const serviceRequestOptions = { headers: { - 'Authorization': 'bearer ' + tokenResponse + 'Authorization': 'bearer ' + token } }; const serviceResponse = await got(receivingServiceURL, serviceRequestOptions); - res.send(serviceResponse.body); + return serviceResponse; } catch (error) { console.log('Metadata server could not respond to query ', error); - res.send(error); + return error; } }; -// [END run_service_to_service_auth] \ No newline at end of file +// [END run_service_to_service_auth] + +requestServiceToken(); +}; +main(); \ No newline at end of file diff --git a/run/authentication/package.json b/run/authentication/package.json new file mode 100644 index 0000000000..58ae47bb48 --- /dev/null +++ b/run/authentication/package.json @@ -0,0 +1,21 @@ +{ + "name": "nodejs-auth", + "version": "1.0.0", + "description": "Cloud Run service-to-service authentication", + "main": "index.js", + "private": true, + "scripts": { + "start": "node index.js", + "test": "test" + }, + "author": "krippaehne", + "license": "Apache-2.0", + "dependencies": { + "express": "^4.16.4", + "gcp-metadata": "^4.0.0", + "got": "^10.7.0" + }, + "devDependencies": { + "mocha": "^7.0.0" + } +} From 5975b571f1d086abafa635cc26fa421bf335a72b Mon Sep 17 00:00:00 2001 From: kelsk Date: Mon, 6 Apr 2020 13:47:04 -0700 Subject: [PATCH 05/96] Initial code added --- run/markdown-preview/.env | 2 + run/markdown-preview/README.md | 0 run/markdown-preview/editor/Dockerfile | 0 run/markdown-preview/editor/main.js | 28 +++++ run/markdown-preview/editor/render.js | 67 +++++++++++ run/markdown-preview/editor/service.js | 59 ++++++++++ .../editor/templates/index.html | 110 ++++++++++++++++++ .../editor/templates/markdown.md | 19 +++ run/markdown-preview/package.json | 18 +++ run/markdown-preview/renderer/Dockerfile | 0 run/markdown-preview/renderer/main.js | 41 +++++++ 11 files changed, 344 insertions(+) create mode 100644 run/markdown-preview/.env create mode 100644 run/markdown-preview/README.md create mode 100644 run/markdown-preview/editor/Dockerfile create mode 100644 run/markdown-preview/editor/main.js create mode 100644 run/markdown-preview/editor/render.js create mode 100644 run/markdown-preview/editor/service.js create mode 100644 run/markdown-preview/editor/templates/index.html create mode 100644 run/markdown-preview/editor/templates/markdown.md create mode 100644 run/markdown-preview/package.json create mode 100644 run/markdown-preview/renderer/Dockerfile create mode 100644 run/markdown-preview/renderer/main.js diff --git a/run/markdown-preview/.env b/run/markdown-preview/.env new file mode 100644 index 0000000000..45c2c8af25 --- /dev/null +++ b/run/markdown-preview/.env @@ -0,0 +1,2 @@ +EDITOR_UPSTREAM_RENDER_URL='' +EDITOR_UPSTREAM_AUTHENTICATED=false diff --git a/run/markdown-preview/README.md b/run/markdown-preview/README.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/run/markdown-preview/editor/Dockerfile b/run/markdown-preview/editor/Dockerfile new file mode 100644 index 0000000000..e69de29bb2 diff --git a/run/markdown-preview/editor/main.js b/run/markdown-preview/editor/main.js new file mode 100644 index 0000000000..08dfbaf283 --- /dev/null +++ b/run/markdown-preview/editor/main.js @@ -0,0 +1,28 @@ +// Copyright 2020 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 +// +// https://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 editor provides a frontend to a markdown rendering microservice. + + +const service = require('./service.js'); + +function main() { + + if (service.newServiceFromEnv) { + console.log("newServiceFromEnv: ", service.newServiceFromEnv()) + } + + +} +main(); \ No newline at end of file diff --git a/run/markdown-preview/editor/render.js b/run/markdown-preview/editor/render.js new file mode 100644 index 0000000000..83a6ea7467 --- /dev/null +++ b/run/markdown-preview/editor/render.js @@ -0,0 +1,67 @@ +// Copyright 2020 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 +// +// https://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 run_secure_request] + +// Import the Metadata API +const gcpMetadata = require('gcp-metadata') +const got = require('got'); +const { renderService } = require('./service.js'); + +// NewRequest creates a new HTTP request with IAM ID Token credential. +// This token is automatically handled by private Cloud Run (fully managed) and Cloud Functions. +const newRequest = async (service) => { + // Skip authentication if not using HTTPS, such as for local development. + if (!service.isAuthenticated) return null; + + try { + // Query the id_token with ?audience as the serviceURL + const metadataServerTokenPath = 'service-accounts/default/identity?audience=' + service.URL; + const tokenRequestOptions = { + headers: { + 'Metadata-Flavor': 'Google' + } + }; + // Fetch the token and then provide it in the request to the receiving service + const token = await gcpMetadata.instance(metadataServerTokenPath, tokenRequestOptions); + const serviceRequestOptions = { + headers: { + 'Authorization': 'bearer ' + token + } + }; + const serviceResponse = await got(receivingServiceURL, serviceRequestOptions); + return serviceResponse; + } catch (error) { + console.log('Metadata server could not respond to query ', error); + return error; + } +}; + +// [END run_secure_request] + +// [START run_secure_request_do] + +// Render converts the Markdown plaintext to HTML. +const render = async (service) => { + service.newRequest(renderService) + ////// TODO: this needs to convert the markdown into html + ////// go uses 'ioutil' +} +// [END run_secure_request_do] + +module.exports = { + requestServiceToken, + newRequest, + render, +} \ No newline at end of file diff --git a/run/markdown-preview/editor/service.js b/run/markdown-preview/editor/service.js new file mode 100644 index 0000000000..8f3aa89d5a --- /dev/null +++ b/run/markdown-preview/editor/service.js @@ -0,0 +1,59 @@ +// Copyright 2020 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 +// +// https://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 handlebars = require('handlebars'); +const { readFileSync } = require('fs'); + + +const newServiceFromEnv = () => { + + const url = process.env.EDITOR_UPSTREAM_RENDER_URL; + if (url === '') throw Error ("no configuration for upstream render service: add EDITOR_UPSTREAM_RENDER_URL environment variable"); + const auth = process.env.EDITOR_UPSTREAM_AUTHENTICATED; + if (!auth) console.log("editor: starting in unauthenticated upstream mode"); + + // The use case of this service is the UI driven by these files. + // Loading them as part of the server startup process keeps failures easily + // discoverable and minimizes latency for the first request. + + const parsedTemplate = handlebars.compile(readFileSync(__dirname + '/templates/index.html', 'utf8')); + const markdownDefault = readFileSync(__dirname + '/templates/markdown.md'); + + console.log(parsedTemplate({ default: markdownDefault})); + + const renderService = { + Renderer: { + URL: url, + isAuthenticated: auth, + }, + parsedTemplate: parsedTemplate, + markdownDefault: markdownDefault + } + + return renderService; + +}; +newServiceFromEnv(); +const registerHandlers = () => { +////// TODO: uses mux. might not need this method for node.js +}; + +const renderHandler = (req, res) => { + +}; + +module.exports = { + newServiceFromEnv, + registerHandlers, + renderHandler +} \ No newline at end of file diff --git a/run/markdown-preview/editor/templates/index.html b/run/markdown-preview/editor/templates/index.html new file mode 100644 index 0000000000..bb23e475e1 --- /dev/null +++ b/run/markdown-preview/editor/templates/index.html @@ -0,0 +1,110 @@ + + + + + + + Markdown Editor + + + + + + + +
+
+
+ Markdown Editor +
+ +
+
+ +
+
+ +
+
+ +
+
+
+

Markdown Text

+
+
+
+ +
+ +
+ +
+
+ +
+

Rendered HTML

+
+
Tap "Preview Rendered Markdown" below the text entry to see rendered content.
+
+
+ +
+ + + + + diff --git a/run/markdown-preview/editor/templates/markdown.md b/run/markdown-preview/editor/templates/markdown.md new file mode 100644 index 0000000000..c840566c22 --- /dev/null +++ b/run/markdown-preview/editor/templates/markdown.md @@ -0,0 +1,19 @@ +# Playing with Markdown + +This UI allows a user to write Markdown text and preview the rendered HTML. + +You may be familiar with this workflow from sites such as Github or Wikipedia. + +In practice, this web page does the following: + +* On click of the *"Preview Rendered Markdown"* button, browser JavaScript + lifts the markdown text and sends it to the editor UI's public backend. +* The editor backend sends the text to a private Renderer service which + converts it to HTML. +* The HTML is injected into the web page in the right-side **Rendered HTML** area. + +## Markdown Background + +Markdown is a text-to-HTML conversion tool that allows you to convert plain text to valid HTML. + +Read more about the [syntax on Wikipedia](https://en.wikipedia.org/wiki/Markdown). diff --git a/run/markdown-preview/package.json b/run/markdown-preview/package.json new file mode 100644 index 0000000000..f785d9c706 --- /dev/null +++ b/run/markdown-preview/package.json @@ -0,0 +1,18 @@ +{ + "name": "markdown-preview", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "start": "node editor/main.js", + "serve": "node renderer/main.js", + "test": "test" + }, + "author": "krippaehne", + "license": "ISC", + "dependencies": { + "express": "^4.17.1", + "handlebars": "^4.7.6", + "showdown": "^1.9.1" + } +} diff --git a/run/markdown-preview/renderer/Dockerfile b/run/markdown-preview/renderer/Dockerfile new file mode 100644 index 0000000000..e69de29bb2 diff --git a/run/markdown-preview/renderer/main.js b/run/markdown-preview/renderer/main.js new file mode 100644 index 0000000000..79ea24b0d2 --- /dev/null +++ b/run/markdown-preview/renderer/main.js @@ -0,0 +1,41 @@ +// Copyright 2020 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 +// +// https://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 editor provides a frontend to a markdown rendering microservice. + +const showdown = require('showdown'); +const express = require('express') + +const app = express(); + +const main = () => { + app.get('/', (req, res) => {markdownHandler(req,res)}) + + const port = process.env.PORT || 8080; + app.listen(port, err => { + if (err) console.log('Error: ', err); + console.log('Server is listening on port ', port) + }) +} + +const markdownHandler = (req, res) => { + console.log(req.headers); + + const testMarkdown = 'this is *markdown* text'; + const converter = new showdown.Converter(); + const html = converter.makeHtml(testMarkdown) + const fullString = testMarkdown + html; + res.send(fullString) +} +main(); \ No newline at end of file From 8e9ef37671991eba7b23017cb267d2a5d60ed655 Mon Sep 17 00:00:00 2001 From: kelsk Date: Mon, 13 Apr 2020 15:34:54 -0700 Subject: [PATCH 06/96] Updated package.json; resolved issues in auth.js from review --- run/authentication/auth.js | 23 ++++++----------------- run/authentication/package.json | 19 +++++++++++++------ 2 files changed, 19 insertions(+), 23 deletions(-) diff --git a/run/authentication/auth.js b/run/authentication/auth.js index 620d70c684..c080111d9a 100644 --- a/run/authentication/auth.js +++ b/run/authentication/auth.js @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -function main(receivingServiceURL = 'YOUR_RECEIVING_SERVICE_URL') { +const requestServiceToken = async (receivingServiceURL = 'https://SERVICE_NAME-HASH-run.app') => { // [START run_service_to_service_auth] // Import the Metadata API @@ -20,26 +20,18 @@ const gcpMetadata = require('gcp-metadata') const got = require('got'); // TODO(developer): Add the URL of your receiving service -// const receivingServiceURL = 'YOUR_RECEIVING_SERVICE_URL'' +// const receivingServiceURL = 'https://SERVICE_NAME-HASH-run.app' -const requestServiceToken = async () => { try { - // Set up the metadata server request options - - const metadataServerTokenPath = 'service-accounts/default/identity?audience=' + receivingServiceURL; - const tokenRequestOptions = { - headers: { - 'Metadata-Flavor': 'Google' - } - }; + // Set up the metadata server request URL + const metadataServerTokenPath = `service-accounts/default/identity?audience=${receivingServiceURL}`; // Fetch the token and then provide it in the request to the receiving service - const token = await gcpMetadata.instance(metadataServerTokenPath, tokenRequestOptions); + const token = await gcpMetadata.instance(metadataServerTokenPath); const serviceRequestOptions = { headers: { 'Authorization': 'bearer ' + token - } }; @@ -49,13 +41,10 @@ const requestServiceToken = async () => { } catch (error) { console.log('Metadata server could not respond to query ', error); return error; - } -}; // [END run_service_to_service_auth] -requestServiceToken(); }; -main(); +requestServiceToken(...process.argv.slice(2)); diff --git a/run/authentication/package.json b/run/authentication/package.json index 58ae47bb48..b01e1fd79a 100644 --- a/run/authentication/package.json +++ b/run/authentication/package.json @@ -1,15 +1,22 @@ { - "name": "nodejs-auth", - "version": "1.0.0", + "name": "nodejs-docs-samples-run-auth", "description": "Cloud Run service-to-service authentication", - "main": "index.js", + "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": ">= 10.0.0" + }, + "main": "index.js", "scripts": { "start": "node index.js", - "test": "test" + "test": "mocha test/*.test.js" }, - "author": "krippaehne", - "license": "Apache-2.0", "dependencies": { "express": "^4.16.4", "gcp-metadata": "^4.0.0", From 867eb65a431d74199e70903fa1ece85eae1fd9e7 Mon Sep 17 00:00:00 2001 From: kelsk Date: Mon, 13 Apr 2020 15:41:45 -0700 Subject: [PATCH 07/96] Removed auth from branch --- package.json | 4 +++ run/authentication/auth.js | 50 --------------------------------- run/authentication/package.json | 28 ------------------ 3 files changed, 4 insertions(+), 78 deletions(-) delete mode 100644 run/authentication/auth.js delete mode 100644 run/authentication/package.json diff --git a/package.json b/package.json index 2cb490bc93..d9fb36c015 100644 --- a/package.json +++ b/package.json @@ -25,5 +25,9 @@ "eslint-plugin-promise": "^4.1.1", "prettier": "^2.0.0", "requestretry": "^4.0.0" + }, + "dependencies": { + "express": "^4.17.1", + "showdown": "^1.9.1" } } diff --git a/run/authentication/auth.js b/run/authentication/auth.js deleted file mode 100644 index c080111d9a..0000000000 --- a/run/authentication/auth.js +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright 2020 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 -// -// https://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 requestServiceToken = async (receivingServiceURL = 'https://SERVICE_NAME-HASH-run.app') => { -// [START run_service_to_service_auth] - -// Import the Metadata API -const gcpMetadata = require('gcp-metadata') -const got = require('got'); - -// TODO(developer): Add the URL of your receiving service -// const receivingServiceURL = 'https://SERVICE_NAME-HASH-run.app' - - try { - - // Set up the metadata server request URL - const metadataServerTokenPath = `service-accounts/default/identity?audience=${receivingServiceURL}`; - - // Fetch the token and then provide it in the request to the receiving service - const token = await gcpMetadata.instance(metadataServerTokenPath); - const serviceRequestOptions = { - headers: { - 'Authorization': 'bearer ' + token - } - }; - - const serviceResponse = await got(receivingServiceURL, serviceRequestOptions); - return serviceResponse; - - } catch (error) { - console.log('Metadata server could not respond to query ', error); - return error; - } - -// [END run_service_to_service_auth] - -}; -requestServiceToken(...process.argv.slice(2)); - diff --git a/run/authentication/package.json b/run/authentication/package.json deleted file mode 100644 index b01e1fd79a..0000000000 --- a/run/authentication/package.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "name": "nodejs-docs-samples-run-auth", - "description": "Cloud Run service-to-service authentication", - "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": ">= 10.0.0" - }, - "main": "index.js", - "scripts": { - "start": "node index.js", - "test": "mocha test/*.test.js" - }, - "dependencies": { - "express": "^4.16.4", - "gcp-metadata": "^4.0.0", - "got": "^10.7.0" - }, - "devDependencies": { - "mocha": "^7.0.0" - } -} From 62fd173df73aa3b020a74c29e5a5234bdfe2d197 Mon Sep 17 00:00:00 2001 From: kelsk Date: Mon, 20 Apr 2020 10:08:57 -0700 Subject: [PATCH 08/96] Markdown renderer & editor added --- run/markdown-preview/.env | 2 - run/markdown-preview/editor/.env | 2 + run/markdown-preview/editor/Dockerfile | 20 +++++++ run/markdown-preview/editor/main.js | 34 +++++++++--- run/markdown-preview/editor/package.json | 20 +++++++ run/markdown-preview/editor/render.js | 53 +++++++++++-------- run/markdown-preview/editor/service.js | 34 +++++------- .../editor/templates/index.html | 1 + run/markdown-preview/package.json | 2 + run/markdown-preview/renderer/Dockerfile | 20 +++++++ run/markdown-preview/renderer/main.js | 46 ++++++++++------ run/markdown-preview/renderer/package.json | 18 +++++++ 12 files changed, 182 insertions(+), 70 deletions(-) delete mode 100644 run/markdown-preview/.env create mode 100644 run/markdown-preview/editor/.env create mode 100644 run/markdown-preview/editor/package.json create mode 100644 run/markdown-preview/renderer/package.json diff --git a/run/markdown-preview/.env b/run/markdown-preview/.env deleted file mode 100644 index 45c2c8af25..0000000000 --- a/run/markdown-preview/.env +++ /dev/null @@ -1,2 +0,0 @@ -EDITOR_UPSTREAM_RENDER_URL='' -EDITOR_UPSTREAM_AUTHENTICATED=false diff --git a/run/markdown-preview/editor/.env b/run/markdown-preview/editor/.env new file mode 100644 index 0000000000..8063ab8d61 --- /dev/null +++ b/run/markdown-preview/editor/.env @@ -0,0 +1,2 @@ +EDITOR_UPSTREAM_RENDER_URL='https://renderer-l5x3ygs5pa-uc.a.run.app' +EDITOR_UPSTREAM_AUTHENTICATED=true diff --git a/run/markdown-preview/editor/Dockerfile b/run/markdown-preview/editor/Dockerfile index e69de29bb2..e990c654e1 100644 --- a/run/markdown-preview/editor/Dockerfile +++ b/run/markdown-preview/editor/Dockerfile @@ -0,0 +1,20 @@ +# Use the official lightweight Node.js 12 image. +# https://hub.docker.com/_/node +FROM node:12-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 production dependencies. +RUN npm install --only=production + +# Copy local code to the container image. +COPY . ./ + +# Run the web service on container startup. +CMD [ "npm", "start" ] \ No newline at end of file diff --git a/run/markdown-preview/editor/main.js b/run/markdown-preview/editor/main.js index 08dfbaf283..f947579576 100644 --- a/run/markdown-preview/editor/main.js +++ b/run/markdown-preview/editor/main.js @@ -15,14 +15,34 @@ // Sample editor provides a frontend to a markdown rendering microservice. -const service = require('./service.js'); + const express = require('express'); + const {renderService} = require('./service.js'); + const {renderRequest} = require('./render.js'); -function main() { + const app = express() - if (service.newServiceFromEnv) { - console.log("newServiceFromEnv: ", service.newServiceFromEnv()) - } + app.use(express.json()); + app.use(express.urlencoded()); + + const service = renderService(); + app.get('/', async (req, res) => { + try { + const parsedTemplate = service.parsedTemplate; + res.send(parsedTemplate) + } catch (err) { + console.log(err) + res.send('error', err) + } + }) -} -main(); \ No newline at end of file + app.post('/render', async (req, res) => { + console.log('body in post: ', req.body); + const markdown = req.body.data; // markdown text from index.html + const render = await renderRequest(service, markdown) + res.send(render) + }) + + const port = process.env.PORT || 8080; + app.listen(port, () => console.log(`app listening on port ${port}`)) + diff --git a/run/markdown-preview/editor/package.json b/run/markdown-preview/editor/package.json new file mode 100644 index 0000000000..fb20393c9d --- /dev/null +++ b/run/markdown-preview/editor/package.json @@ -0,0 +1,20 @@ +{ + "name": "markdown-preview", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "start": "node main.js", + "serve": "node renderer/main.js", + "test": "test" + }, + "author": "krippaehne", + "license": "ISC", + "dependencies": { + "dotenv": "^8.2.0", + "express": "^4.17.1", + "gcp-metadata": "^4.0.0", + "got": "^10.7.0", + "handlebars": "^4.7.6" + } +} diff --git a/run/markdown-preview/editor/render.js b/run/markdown-preview/editor/render.js index 83a6ea7467..20d236b963 100644 --- a/run/markdown-preview/editor/render.js +++ b/run/markdown-preview/editor/render.js @@ -17,30 +17,34 @@ // Import the Metadata API const gcpMetadata = require('gcp-metadata') const got = require('got'); -const { renderService } = require('./service.js'); // NewRequest creates a new HTTP request with IAM ID Token credential. // This token is automatically handled by private Cloud Run (fully managed) and Cloud Functions. -const newRequest = async (service) => { +const newRequest = async (service, markdown) => { // Skip authentication if not using HTTPS, such as for local development. - if (!service.isAuthenticated) return null; - + if (!service.isAuthenticated) { + console.log("Service is not authenticated") + return null; + } try { - // Query the id_token with ?audience as the serviceURL - const metadataServerTokenPath = 'service-accounts/default/identity?audience=' + service.URL; - const tokenRequestOptions = { - headers: { - 'Metadata-Flavor': 'Google' - } - }; + console.log('markdown in render: ', markdown); + // Query the token with ?audience as the service URL + const metadataServerTokenPath = `service-accounts/default/identity?audience=${service.url}`; // Fetch the token and then provide it in the request to the receiving service - const token = await gcpMetadata.instance(metadataServerTokenPath, tokenRequestOptions); + const token = await gcpMetadata.instance(metadataServerTokenPath); + const serviceRequestOptions = { + method: 'POST', headers: { - 'Authorization': 'bearer ' + token - } + 'Authorization': 'bearer ' + token, + 'Content-Type': 'application/json' + }, + body: JSON.stringify({data: markdown}) }; - const serviceResponse = await got(receivingServiceURL, serviceRequestOptions); + + const serviceRequest = await got(service.url, serviceRequestOptions); + const serviceResponse = serviceRequest; + console.log('service response: ', serviceResponse); return serviceResponse; } catch (error) { console.log('Metadata server could not respond to query ', error); @@ -53,15 +57,20 @@ const newRequest = async (service) => { // [START run_secure_request_do] // Render converts the Markdown plaintext to HTML. -const render = async (service) => { - service.newRequest(renderService) - ////// TODO: this needs to convert the markdown into html - ////// go uses 'ioutil' + +const renderRequest = async (service, markdown) => { + const authedService = await newRequest(service, markdown); + if (authedService) { + return authedService; + } else { + return null; + } + // ////// TODO: this needs to convert the markdown into html +// ////// Go uses 'ioutil' } // [END run_secure_request_do] + module.exports = { - requestServiceToken, - newRequest, - render, + renderRequest, } \ No newline at end of file diff --git a/run/markdown-preview/editor/service.js b/run/markdown-preview/editor/service.js index 8f3aa89d5a..2ff63129d7 100644 --- a/run/markdown-preview/editor/service.js +++ b/run/markdown-preview/editor/service.js @@ -15,45 +15,35 @@ const handlebars = require('handlebars'); const { readFileSync } = require('fs'); -const newServiceFromEnv = () => { - +const renderService = () => { const url = process.env.EDITOR_UPSTREAM_RENDER_URL; - if (url === '') throw Error ("no configuration for upstream render service: add EDITOR_UPSTREAM_RENDER_URL environment variable"); + if (!url) throw Error ("no configuration for upstream render service: add EDITOR_UPSTREAM_RENDER_URL environment variable"); const auth = process.env.EDITOR_UPSTREAM_AUTHENTICATED; if (!auth) console.log("editor: starting in unauthenticated upstream mode"); + console.log('url: ', url); + console.log('auth: ', auth); // The use case of this service is the UI driven by these files. // Loading them as part of the server startup process keeps failures easily // discoverable and minimizes latency for the first request. - const parsedTemplate = handlebars.compile(readFileSync(__dirname + '/templates/index.html', 'utf8')); + const template = handlebars.compile(readFileSync(__dirname + '/templates/index.html', 'utf8')); const markdownDefault = readFileSync(__dirname + '/templates/markdown.md'); - console.log(parsedTemplate({ default: markdownDefault})); + const parsedTemplate = template({ default: markdownDefault}); - const renderService = { - Renderer: { - URL: url, - isAuthenticated: auth, - }, + ///// possibly de-objectify this service + const service = { + url: url, + isAuthenticated: auth, parsedTemplate: parsedTemplate, markdownDefault: markdownDefault } - return renderService; - -}; -newServiceFromEnv(); -const registerHandlers = () => { -////// TODO: uses mux. might not need this method for node.js -}; - -const renderHandler = (req, res) => { + return service; }; module.exports = { - newServiceFromEnv, - registerHandlers, - renderHandler + renderService } \ No newline at end of file diff --git a/run/markdown-preview/editor/templates/index.html b/run/markdown-preview/editor/templates/index.html index bb23e475e1..f8c7a1af03 100644 --- a/run/markdown-preview/editor/templates/index.html +++ b/run/markdown-preview/editor/templates/index.html @@ -75,6 +75,7 @@

Rendered HTML

const preview = document.getElementById('preview'); const lp = new mdc.linearProgress.MDCLinearProgress(document.querySelector('.mdc-linear-progress')); async function render(data = {}) { + console.log(" indexed data: ", data) const response = await fetch('/render', { method: 'POST', headers: { diff --git a/run/markdown-preview/package.json b/run/markdown-preview/package.json index f785d9c706..d3f4b16a32 100644 --- a/run/markdown-preview/package.json +++ b/run/markdown-preview/package.json @@ -12,6 +12,8 @@ "license": "ISC", "dependencies": { "express": "^4.17.1", + "gcp-metadata": "^4.0.0", + "got": "^10.7.0", "handlebars": "^4.7.6", "showdown": "^1.9.1" } diff --git a/run/markdown-preview/renderer/Dockerfile b/run/markdown-preview/renderer/Dockerfile index e69de29bb2..e990c654e1 100644 --- a/run/markdown-preview/renderer/Dockerfile +++ b/run/markdown-preview/renderer/Dockerfile @@ -0,0 +1,20 @@ +# Use the official lightweight Node.js 12 image. +# https://hub.docker.com/_/node +FROM node:12-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 production dependencies. +RUN npm install --only=production + +# Copy local code to the container image. +COPY . ./ + +# Run the web service on container startup. +CMD [ "npm", "start" ] \ No newline at end of file diff --git a/run/markdown-preview/renderer/main.js b/run/markdown-preview/renderer/main.js index 79ea24b0d2..b938eced79 100644 --- a/run/markdown-preview/renderer/main.js +++ b/run/markdown-preview/renderer/main.js @@ -15,27 +15,39 @@ // Sample editor provides a frontend to a markdown rendering microservice. const showdown = require('showdown'); -const express = require('express') +const express = require('express'); const app = express(); -const main = () => { - app.get('/', (req, res) => {markdownHandler(req,res)}) +app.use(express.json()); +app.use(express.urlencoded()); - const port = process.env.PORT || 8080; - app.listen(port, err => { - if (err) console.log('Error: ', err); - console.log('Server is listening on port ', port) - }) -} +app.get('/', (req, res) => { + res.send('works') -const markdownHandler = (req, res) => { - console.log(req.headers); + // console.log('req.headers in renderer: ', req.headers); + // console.log('req.params in renderer: ', req.params); + // console.log('req.body in renderer: ', req.body); + // const markdown = req.body; + // const converter = new showdown.Converter(); + // const html = converter.makeHtml(markdown) + // res.send(html) +}) - const testMarkdown = 'this is *markdown* text'; +app.post('/', (req, res) => { + console.log('req.headers in renderer: ', req.headers); + console.log('req.params in renderer: ', req.params); + console.log('req.body in renderer: ', req.body); + const markdown = req.body; + console.log(markdown); const converter = new showdown.Converter(); - const html = converter.makeHtml(testMarkdown) - const fullString = testMarkdown + html; - res.send(fullString) -} -main(); \ No newline at end of file + const html = converter.makeHtml(markdown) + res.send(html) +}) + +const port = process.env.PORT || 8080; + +app.listen(port, err => { + if (err) console.log('Error: ', err); + console.log('Server is listening on port ', port) +}) diff --git a/run/markdown-preview/renderer/package.json b/run/markdown-preview/renderer/package.json new file mode 100644 index 0000000000..1ec588ea8f --- /dev/null +++ b/run/markdown-preview/renderer/package.json @@ -0,0 +1,18 @@ +{ + "name": "markdown-preview", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "start": "node main.js", + "serve": "node main.js", + "test": "test" + }, + "author": "krippaehne", + "license": "ISC", + "dependencies": { + "express": "^4.17.1", + "got": "^10.7.0", + "showdown": "^1.9.1" + } +} From 02dccb64b63ca6d2072fd7f9ba90a7aec5e2a144 Mon Sep 17 00:00:00 2001 From: kelsk Date: Tue, 21 Apr 2020 12:53:50 -0700 Subject: [PATCH 09/96] Markdown renderer & editor working --- run/markdown-preview/editor/main.js | 15 +++++++++------ run/markdown-preview/editor/render.js | 10 ++++------ run/markdown-preview/editor/service.js | 3 --- 3 files changed, 13 insertions(+), 15 deletions(-) diff --git a/run/markdown-preview/editor/main.js b/run/markdown-preview/editor/main.js index f947579576..e24b190c7e 100644 --- a/run/markdown-preview/editor/main.js +++ b/run/markdown-preview/editor/main.js @@ -22,7 +22,6 @@ const app = express() app.use(express.json()); - app.use(express.urlencoded()); const service = renderService(); @@ -31,18 +30,22 @@ const parsedTemplate = service.parsedTemplate; res.send(parsedTemplate) } catch (err) { - console.log(err) + console.log('Error: ', err) res.send('error', err) } }) app.post('/render', async (req, res) => { - console.log('body in post: ', req.body); - const markdown = req.body.data; // markdown text from index.html + const markdown = req.body.data; const render = await renderRequest(service, markdown) - res.send(render) + const response = JSON.parse(render); + res.send(response.data) }) const port = process.env.PORT || 8080; - app.listen(port, () => console.log(`app listening on port ${port}`)) + + app.listen(port, err => { + if (err) console.log('Error: ', err) + console.log(`Editor listening on port ${port}`) + }) diff --git a/run/markdown-preview/editor/render.js b/run/markdown-preview/editor/render.js index 20d236b963..9c808b1617 100644 --- a/run/markdown-preview/editor/render.js +++ b/run/markdown-preview/editor/render.js @@ -27,7 +27,7 @@ const newRequest = async (service, markdown) => { return null; } try { - console.log('markdown in render: ', markdown); + // Query the token with ?audience as the service URL const metadataServerTokenPath = `service-accounts/default/identity?audience=${service.url}`; // Fetch the token and then provide it in the request to the receiving service @@ -39,12 +39,11 @@ const newRequest = async (service, markdown) => { 'Authorization': 'bearer ' + token, 'Content-Type': 'application/json' }, - body: JSON.stringify({data: markdown}) + body: JSON.stringify({markdown: markdown}) }; const serviceRequest = await got(service.url, serviceRequestOptions); - const serviceResponse = serviceRequest; - console.log('service response: ', serviceResponse); + const serviceResponse = serviceRequest.body; return serviceResponse; } catch (error) { console.log('Metadata server could not respond to query ', error); @@ -65,9 +64,8 @@ const renderRequest = async (service, markdown) => { } else { return null; } - // ////// TODO: this needs to convert the markdown into html -// ////// Go uses 'ioutil' } + // [END run_secure_request_do] diff --git a/run/markdown-preview/editor/service.js b/run/markdown-preview/editor/service.js index 2ff63129d7..821988e427 100644 --- a/run/markdown-preview/editor/service.js +++ b/run/markdown-preview/editor/service.js @@ -14,15 +14,12 @@ const handlebars = require('handlebars'); const { readFileSync } = require('fs'); - const renderService = () => { const url = process.env.EDITOR_UPSTREAM_RENDER_URL; if (!url) throw Error ("no configuration for upstream render service: add EDITOR_UPSTREAM_RENDER_URL environment variable"); const auth = process.env.EDITOR_UPSTREAM_AUTHENTICATED; if (!auth) console.log("editor: starting in unauthenticated upstream mode"); - console.log('url: ', url); - console.log('auth: ', auth); // The use case of this service is the UI driven by these files. // Loading them as part of the server startup process keeps failures easily // discoverable and minimizes latency for the first request. From 9ef850f5d51d1913c144cace48f711afa457f5ae Mon Sep 17 00:00:00 2001 From: kelsk Date: Tue, 21 Apr 2020 13:35:02 -0700 Subject: [PATCH 10/96] Added xss sanitizer --- run/markdown-preview/editor/.gitignore | 2 ++ run/markdown-preview/editor/package.json | 3 +- run/markdown-preview/editor/service.js | 1 - run/markdown-preview/renderer/.gitignore | 2 ++ run/markdown-preview/renderer/main.js | 40 ++++++++++------------ run/markdown-preview/renderer/package.json | 3 +- 6 files changed, 26 insertions(+), 25 deletions(-) create mode 100644 run/markdown-preview/editor/.gitignore create mode 100644 run/markdown-preview/renderer/.gitignore diff --git a/run/markdown-preview/editor/.gitignore b/run/markdown-preview/editor/.gitignore new file mode 100644 index 0000000000..5e5272738b --- /dev/null +++ b/run/markdown-preview/editor/.gitignore @@ -0,0 +1,2 @@ +/node_modules +package-lock.json diff --git a/run/markdown-preview/editor/package.json b/run/markdown-preview/editor/package.json index fb20393c9d..e9132d970c 100644 --- a/run/markdown-preview/editor/package.json +++ b/run/markdown-preview/editor/package.json @@ -15,6 +15,7 @@ "express": "^4.17.1", "gcp-metadata": "^4.0.0", "got": "^10.7.0", - "handlebars": "^4.7.6" + "handlebars": "^4.7.6", + "xss": "^1.0.6" } } diff --git a/run/markdown-preview/editor/service.js b/run/markdown-preview/editor/service.js index 821988e427..4b1c5ab16b 100644 --- a/run/markdown-preview/editor/service.js +++ b/run/markdown-preview/editor/service.js @@ -29,7 +29,6 @@ const renderService = () => { const parsedTemplate = template({ default: markdownDefault}); - ///// possibly de-objectify this service const service = { url: url, isAuthenticated: auth, diff --git a/run/markdown-preview/renderer/.gitignore b/run/markdown-preview/renderer/.gitignore new file mode 100644 index 0000000000..5e5272738b --- /dev/null +++ b/run/markdown-preview/renderer/.gitignore @@ -0,0 +1,2 @@ +/node_modules +package-lock.json diff --git a/run/markdown-preview/renderer/main.js b/run/markdown-preview/renderer/main.js index b938eced79..6b475d21d7 100644 --- a/run/markdown-preview/renderer/main.js +++ b/run/markdown-preview/renderer/main.js @@ -16,38 +16,34 @@ const showdown = require('showdown'); const express = require('express'); +const xss = require('xss'); const app = express(); app.use(express.json()); -app.use(express.urlencoded()); - -app.get('/', (req, res) => { - res.send('works') - - // console.log('req.headers in renderer: ', req.headers); - // console.log('req.params in renderer: ', req.params); - // console.log('req.body in renderer: ', req.body); - // const markdown = req.body; - // const converter = new showdown.Converter(); - // const html = converter.makeHtml(markdown) - // res.send(html) -}) app.post('/', (req, res) => { - console.log('req.headers in renderer: ', req.headers); - console.log('req.params in renderer: ', req.params); - console.log('req.body in renderer: ', req.body); - const markdown = req.body; - console.log(markdown); - const converter = new showdown.Converter(); - const html = converter.makeHtml(markdown) - res.send(html) + try { + + // Get the Markdown text and convert it into HTML using Showdown + const markdown = req.body.markdown; + const converter = new showdown.Converter(); + const data = converter.makeHtml(markdown); + + // Add XSS sanitizer + const sanitizedData = xss(data); + const response = JSON.stringify({data: sanitizedData}); + res.send(response); + + } catch(err) { + console.log('errorr: ', err); + res.send(err) + } }) const port = process.env.PORT || 8080; app.listen(port, err => { if (err) console.log('Error: ', err); - console.log('Server is listening on port ', port) + console.log('Renderer is listening on port ', port) }) diff --git a/run/markdown-preview/renderer/package.json b/run/markdown-preview/renderer/package.json index 1ec588ea8f..68246929f3 100644 --- a/run/markdown-preview/renderer/package.json +++ b/run/markdown-preview/renderer/package.json @@ -13,6 +13,7 @@ "dependencies": { "express": "^4.17.1", "got": "^10.7.0", - "showdown": "^1.9.1" + "showdown": "^1.9.1", + "xss": "^1.0.6" } } From a56dca47b56d9e3168bc7652a8780b5d87c15cec Mon Sep 17 00:00:00 2001 From: kelsk Date: Tue, 21 Apr 2020 13:37:50 -0700 Subject: [PATCH 11/96] General cleanup --- run/markdown-preview/editor/.env | 2 +- run/markdown-preview/editor/.gitignore | 1 + run/markdown-preview/editor/main.js | 16 ++++++++++------ run/markdown-preview/editor/render.js | 11 ++++++----- run/markdown-preview/editor/service.js | 2 +- run/markdown-preview/package.json | 20 -------------------- 6 files changed, 19 insertions(+), 33 deletions(-) delete mode 100644 run/markdown-preview/package.json diff --git a/run/markdown-preview/editor/.env b/run/markdown-preview/editor/.env index 8063ab8d61..04c40b67e7 100644 --- a/run/markdown-preview/editor/.env +++ b/run/markdown-preview/editor/.env @@ -1,2 +1,2 @@ -EDITOR_UPSTREAM_RENDER_URL='https://renderer-l5x3ygs5pa-uc.a.run.app' +EDITOR_UPSTREAM_RENDER_URL='RENDER_SERVICE_URL' EDITOR_UPSTREAM_AUTHENTICATED=true diff --git a/run/markdown-preview/editor/.gitignore b/run/markdown-preview/editor/.gitignore index 5e5272738b..856f35acd4 100644 --- a/run/markdown-preview/editor/.gitignore +++ b/run/markdown-preview/editor/.gitignore @@ -1,2 +1,3 @@ /node_modules package-lock.json +.env \ No newline at end of file diff --git a/run/markdown-preview/editor/main.js b/run/markdown-preview/editor/main.js index e24b190c7e..33363306dc 100644 --- a/run/markdown-preview/editor/main.js +++ b/run/markdown-preview/editor/main.js @@ -20,7 +20,6 @@ const {renderRequest} = require('./render.js'); const app = express() - app.use(express.json()); const service = renderService(); @@ -31,15 +30,20 @@ res.send(parsedTemplate) } catch (err) { console.log('Error: ', err) - res.send('error', err) + res.send(err) } }) app.post('/render', async (req, res) => { - const markdown = req.body.data; - const render = await renderRequest(service, markdown) - const response = JSON.parse(render); - res.send(response.data) + try { + const markdown = req.body.data; + const render = await renderRequest(service, markdown) + const response = JSON.parse(render); + res.send(response.data) + } catch (err) { + console.log('Error: ', err) + res.send(err) + } }) const port = process.env.PORT || 8080; diff --git a/run/markdown-preview/editor/render.js b/run/markdown-preview/editor/render.js index 9c808b1617..2c5a2df76d 100644 --- a/run/markdown-preview/editor/render.js +++ b/run/markdown-preview/editor/render.js @@ -21,11 +21,13 @@ const got = require('got'); // NewRequest creates a new HTTP request with IAM ID Token credential. // This token is automatically handled by private Cloud Run (fully managed) and Cloud Functions. const newRequest = async (service, markdown) => { + // Skip authentication if not using HTTPS, such as for local development. if (!service.isAuthenticated) { console.log("Service is not authenticated") return null; } + try { // Query the token with ?audience as the service URL @@ -42,9 +44,10 @@ const newRequest = async (service, markdown) => { body: JSON.stringify({markdown: markdown}) }; - const serviceRequest = await got(service.url, serviceRequestOptions); - const serviceResponse = serviceRequest.body; - return serviceResponse; + // renderRequest converts the Markdown plaintext to HTML. + const renderRequest = await got(service.url, serviceRequestOptions); + const renderResponse = renderRequest.body; + return renderResponse; } catch (error) { console.log('Metadata server could not respond to query ', error); return error; @@ -55,8 +58,6 @@ const newRequest = async (service, markdown) => { // [START run_secure_request_do] -// Render converts the Markdown plaintext to HTML. - const renderRequest = async (service, markdown) => { const authedService = await newRequest(service, markdown); if (authedService) { diff --git a/run/markdown-preview/editor/service.js b/run/markdown-preview/editor/service.js index 4b1c5ab16b..3f96454b4a 100644 --- a/run/markdown-preview/editor/service.js +++ b/run/markdown-preview/editor/service.js @@ -11,6 +11,7 @@ // 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 handlebars = require('handlebars'); const { readFileSync } = require('fs'); @@ -26,7 +27,6 @@ const renderService = () => { const template = handlebars.compile(readFileSync(__dirname + '/templates/index.html', 'utf8')); const markdownDefault = readFileSync(__dirname + '/templates/markdown.md'); - const parsedTemplate = template({ default: markdownDefault}); const service = { diff --git a/run/markdown-preview/package.json b/run/markdown-preview/package.json deleted file mode 100644 index d3f4b16a32..0000000000 --- a/run/markdown-preview/package.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "name": "markdown-preview", - "version": "1.0.0", - "description": "", - "main": "index.js", - "scripts": { - "start": "node editor/main.js", - "serve": "node renderer/main.js", - "test": "test" - }, - "author": "krippaehne", - "license": "ISC", - "dependencies": { - "express": "^4.17.1", - "gcp-metadata": "^4.0.0", - "got": "^10.7.0", - "handlebars": "^4.7.6", - "showdown": "^1.9.1" - } -} From 0bafa821eaf7a173d17320ca42d7c498badb370a Mon Sep 17 00:00:00 2001 From: kelsk <38271546+kelsk@users.noreply.github.com> Date: Tue, 21 Apr 2020 13:40:06 -0700 Subject: [PATCH 12/96] Delete .gitignore --- run/markdown-preview/editor/.gitignore | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 run/markdown-preview/editor/.gitignore diff --git a/run/markdown-preview/editor/.gitignore b/run/markdown-preview/editor/.gitignore deleted file mode 100644 index 856f35acd4..0000000000 --- a/run/markdown-preview/editor/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -/node_modules -package-lock.json -.env \ No newline at end of file From bb14175cd2c38ac6fe4a9748b832923b8933309a Mon Sep 17 00:00:00 2001 From: kelsk <38271546+kelsk@users.noreply.github.com> Date: Tue, 21 Apr 2020 13:40:22 -0700 Subject: [PATCH 13/96] Delete .gitignore --- run/markdown-preview/renderer/.gitignore | 2 -- 1 file changed, 2 deletions(-) delete mode 100644 run/markdown-preview/renderer/.gitignore diff --git a/run/markdown-preview/renderer/.gitignore b/run/markdown-preview/renderer/.gitignore deleted file mode 100644 index 5e5272738b..0000000000 --- a/run/markdown-preview/renderer/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -/node_modules -package-lock.json From 1905b18b39d1ecd88160bfbb8844955a535d7347 Mon Sep 17 00:00:00 2001 From: kelsk <38271546+kelsk@users.noreply.github.com> Date: Tue, 21 Apr 2020 13:49:52 -0700 Subject: [PATCH 14/96] Removed errant console.log --- run/markdown-preview/editor/templates/index.html | 1 - 1 file changed, 1 deletion(-) diff --git a/run/markdown-preview/editor/templates/index.html b/run/markdown-preview/editor/templates/index.html index f8c7a1af03..bb23e475e1 100644 --- a/run/markdown-preview/editor/templates/index.html +++ b/run/markdown-preview/editor/templates/index.html @@ -75,7 +75,6 @@

Rendered HTML

const preview = document.getElementById('preview'); const lp = new mdc.linearProgress.MDCLinearProgress(document.querySelector('.mdc-linear-progress')); async function render(data = {}) { - console.log(" indexed data: ", data) const response = await fetch('/render', { method: 'POST', headers: { From 8353e526fd0ff6c8bb6529caa93468ffba7a0d47 Mon Sep 17 00:00:00 2001 From: kelsk Date: Thu, 23 Apr 2020 14:39:52 -0700 Subject: [PATCH 15/96] Update markdown converter, general code cleanup --- run/markdown-preview/editor/.env | 2 - run/markdown-preview/editor/main.js | 56 +++++++++----- run/markdown-preview/editor/package.json | 23 +++--- run/markdown-preview/editor/render.js | 87 +++++++++------------- run/markdown-preview/editor/service.js | 45 ----------- run/markdown-preview/renderer/main.js | 20 +++-- run/markdown-preview/renderer/package.json | 21 ++++-- 7 files changed, 113 insertions(+), 141 deletions(-) delete mode 100644 run/markdown-preview/editor/.env delete mode 100644 run/markdown-preview/editor/service.js diff --git a/run/markdown-preview/editor/.env b/run/markdown-preview/editor/.env deleted file mode 100644 index 04c40b67e7..0000000000 --- a/run/markdown-preview/editor/.env +++ /dev/null @@ -1,2 +0,0 @@ -EDITOR_UPSTREAM_RENDER_URL='RENDER_SERVICE_URL' -EDITOR_UPSTREAM_AUTHENTICATED=true diff --git a/run/markdown-preview/editor/main.js b/run/markdown-preview/editor/main.js index 33363306dc..468306a04e 100644 --- a/run/markdown-preview/editor/main.js +++ b/run/markdown-preview/editor/main.js @@ -12,39 +12,61 @@ // See the License for the specific language governing permissions and // limitations under the License. -// Sample editor provides a frontend to a markdown rendering microservice. - - const express = require('express'); - const {renderService} = require('./service.js'); - const {renderRequest} = require('./render.js'); + const handlebars = require('handlebars'); + const { readFileSync } = require('fs'); + const renderRequest = require('./render.js'); const app = express() app.use(express.json()); - - const service = renderService(); + + let url, isAuthenticated, markdownDefault, parsedTemplate; + + const buildService = async () => { + url = process.env.EDITOR_UPSTREAM_RENDER_URL; + if (!url) throw Error ("No configuration for upstream render service: add EDITOR_UPSTREAM_RENDER_URL environment variable"); + isAuthenticated = process.env.EDITOR_UPSTREAM_AUTHENTICATED; + if (!isAuthenticated) console.log("Editor: starting in unauthenticated upstream mode"); + return {url, isAuthenticated}; + } + + // Load the template files and serve them with the Editor service. + const buildTemplate = async () => { + try { + markdownDefault = readFileSync(__dirname + '/templates/markdown.md'); + const index = handlebars.compile(readFileSync(__dirname + '/templates/index.html', 'utf8')); + parsedTemplate = index({ default: markdownDefault}); + return parsedTemplate + } catch(err) { + console.log('Error: ', err); + return err + } + }; app.get('/', async (req, res) => { try { - const parsedTemplate = service.parsedTemplate; - res.send(parsedTemplate) + const template = await buildTemplate(); + res.send(template); } catch (err) { - console.log('Error: ', err) - res.send(err) + console.log('Error: ', err); + res.send(err); } - }) + }); + // The renderRequest makes a request to the Renderer service. + // The request returns the Markdown text converted to HTML. app.post('/render', async (req, res) => { try { const markdown = req.body.data; - const render = await renderRequest(service, markdown) + const service = await buildService(); + const render = await renderRequest(service, markdown); const response = JSON.parse(render); - res.send(response.data) + res.send(response.data); } catch (err) { - console.log('Error: ', err) - res.send(err) + console.log('Error: ', err); + res.send(err); } - }) + }); const port = process.env.PORT || 8080; diff --git a/run/markdown-preview/editor/package.json b/run/markdown-preview/editor/package.json index e9132d970c..c31d3bfb35 100644 --- a/run/markdown-preview/editor/package.json +++ b/run/markdown-preview/editor/package.json @@ -1,21 +1,26 @@ { "name": "markdown-preview", - "version": "1.0.0", "description": "", - "main": "index.js", + "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" + }, + "main": "main.js", "scripts": { "start": "node main.js", - "serve": "node renderer/main.js", - "test": "test" + "test": "mocha test/*.test.js" }, - "author": "krippaehne", - "license": "ISC", "dependencies": { - "dotenv": "^8.2.0", "express": "^4.17.1", "gcp-metadata": "^4.0.0", "got": "^10.7.0", - "handlebars": "^4.7.6", - "xss": "^1.0.6" + "handlebars": "^4.7.6" } } diff --git a/run/markdown-preview/editor/render.js b/run/markdown-preview/editor/render.js index 2c5a2df76d..9e8d1032fa 100644 --- a/run/markdown-preview/editor/render.js +++ b/run/markdown-preview/editor/render.js @@ -20,56 +20,43 @@ const got = require('got'); // NewRequest creates a new HTTP request with IAM ID Token credential. // This token is automatically handled by private Cloud Run (fully managed) and Cloud Functions. -const newRequest = async (service, markdown) => { - - // Skip authentication if not using HTTPS, such as for local development. - if (!service.isAuthenticated) { - console.log("Service is not authenticated") - return null; - } - - try { - - // Query the token with ?audience as the service URL +const renderRequest = async (service, markdown) => { + let token; + + // Build the request to the Renderer receiving service. + const serviceRequestOptions = { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({markdown: markdown}) + }; + + if (service.isAuthenticated) { + try { + // Query the token with ?audience as the service URL. const metadataServerTokenPath = `service-accounts/default/identity?audience=${service.url}`; - // Fetch the token and then provide it in the request to the receiving service - const token = await gcpMetadata.instance(metadataServerTokenPath); - - const serviceRequestOptions = { - method: 'POST', - headers: { - 'Authorization': 'bearer ' + token, - 'Content-Type': 'application/json' - }, - body: JSON.stringify({markdown: markdown}) - }; - - // renderRequest converts the Markdown plaintext to HTML. - const renderRequest = await got(service.url, serviceRequestOptions); - const renderResponse = renderRequest.body; - return renderResponse; - } catch (error) { - console.log('Metadata server could not respond to query ', error); - return error; - } + // Fetch the token and then add it to the request header. + token = await gcpMetadata.instance(metadataServerTokenPath); + serviceRequestOptions.headers['Authorization'] = 'bearer ' + token; + } catch(err) { + console.log('Metadata server could not respond to request ', err); + return err; + } + }; + // [END run_secure_request] + + // [START run_secure_request_do] + try { + // serviceRequest converts the Markdown plaintext to HTML. + const serviceRequest = await got(service.url, serviceRequestOptions); + const serviceResponse = serviceRequest.body; + return serviceResponse; + } catch (err) { + console.log('Renderer service could not respond to request ', err); + return err; + }; + // [END run_secure_request_do] }; -// [END run_secure_request] - -// [START run_secure_request_do] - -const renderRequest = async (service, markdown) => { - const authedService = await newRequest(service, markdown); - if (authedService) { - return authedService; - } else { - return null; - } -} - -// [END run_secure_request_do] - - -module.exports = { - renderRequest, -} \ No newline at end of file +module.exports = renderRequest; \ No newline at end of file diff --git a/run/markdown-preview/editor/service.js b/run/markdown-preview/editor/service.js deleted file mode 100644 index 3f96454b4a..0000000000 --- a/run/markdown-preview/editor/service.js +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2020 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 -// -// https://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 handlebars = require('handlebars'); -const { readFileSync } = require('fs'); - -const renderService = () => { - const url = process.env.EDITOR_UPSTREAM_RENDER_URL; - if (!url) throw Error ("no configuration for upstream render service: add EDITOR_UPSTREAM_RENDER_URL environment variable"); - const auth = process.env.EDITOR_UPSTREAM_AUTHENTICATED; - if (!auth) console.log("editor: starting in unauthenticated upstream mode"); - - // The use case of this service is the UI driven by these files. - // Loading them as part of the server startup process keeps failures easily - // discoverable and minimizes latency for the first request. - - const template = handlebars.compile(readFileSync(__dirname + '/templates/index.html', 'utf8')); - const markdownDefault = readFileSync(__dirname + '/templates/markdown.md'); - const parsedTemplate = template({ default: markdownDefault}); - - const service = { - url: url, - isAuthenticated: auth, - parsedTemplate: parsedTemplate, - markdownDefault: markdownDefault - } - - return service; - -}; - -module.exports = { - renderService -} \ No newline at end of file diff --git a/run/markdown-preview/renderer/main.js b/run/markdown-preview/renderer/main.js index 6b475d21d7..7f9833759d 100644 --- a/run/markdown-preview/renderer/main.js +++ b/run/markdown-preview/renderer/main.js @@ -12,10 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -// Sample editor provides a frontend to a markdown rendering microservice. - -const showdown = require('showdown'); const express = require('express'); +const MarkdownIt = require('markdown-it'); const xss = require('xss'); const app = express(); @@ -25,19 +23,19 @@ app.use(express.json()); app.post('/', (req, res) => { try { - // Get the Markdown text and convert it into HTML using Showdown + // Get the Markdown text and convert it into HTML using markdown-it const markdown = req.body.markdown; - const converter = new showdown.Converter(); - const data = converter.makeHtml(markdown); + const md = new MarkdownIt(); + const unsafeData = md.render(markdown); // Add XSS sanitizer - const sanitizedData = xss(data); - const response = JSON.stringify({data: sanitizedData}); + const safeData = xss(unsafeData); + const response = JSON.stringify({data: safeData}); res.send(response); } catch(err) { - console.log('errorr: ', err); - res.send(err) + console.log('Error: ', err); + res.send(err); } }) @@ -45,5 +43,5 @@ const port = process.env.PORT || 8080; app.listen(port, err => { if (err) console.log('Error: ', err); - console.log('Renderer is listening on port ', port) + console.log('Renderer is listening on port ', port); }) diff --git a/run/markdown-preview/renderer/package.json b/run/markdown-preview/renderer/package.json index 68246929f3..86bf7f578c 100644 --- a/run/markdown-preview/renderer/package.json +++ b/run/markdown-preview/renderer/package.json @@ -1,19 +1,26 @@ { "name": "markdown-preview", - "version": "1.0.0", "description": "", - "main": "index.js", + "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" + }, + "main": "main.js", "scripts": { "start": "node main.js", - "serve": "node main.js", - "test": "test" + "test": "mocha test/*.test.js" }, - "author": "krippaehne", - "license": "ISC", "dependencies": { "express": "^4.17.1", "got": "^10.7.0", - "showdown": "^1.9.1", + "markdown-it": "^10.0.0", "xss": "^1.0.6" } } From 039663d44c15da8028098372632d59e0d38ef471 Mon Sep 17 00:00:00 2001 From: kelsk Date: Fri, 24 Apr 2020 10:03:54 -0700 Subject: [PATCH 16/96] updated README --- run/markdown-preview/README.md | 22 ++++++++++++++++++++++ run/markdown-preview/editor/main.js | 2 +- run/markdown-preview/editor/render.js | 5 ++--- 3 files changed, 25 insertions(+), 4 deletions(-) diff --git a/run/markdown-preview/README.md b/run/markdown-preview/README.md index e69de29bb2..06facc9c65 100644 --- a/run/markdown-preview/README.md +++ b/run/markdown-preview/README.md @@ -0,0 +1,22 @@ +# Cloud Run Markdown Preview Sample + +[Securing Cloud Run services tutorial](https://cloud.google.com/run/docs/tutorials/secure-services) walks through how to create a secure two-service application running on Cloud Run. This application is a Markdown editor which includes a public "frontend" service which anyone can use to compose Markdown text and a private "backend" service which renders Markdown text to HTML. + +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). + +## Environment Variables + +Cloud Run services can be [configured with Environment Variables](https://cloud.google.com/run/docs/configuring/environment-variables). +Required variables for this sample include: + +* `EDITOR_UPSTREAM_RENDER_URL`: The URL of the restricted Cloud Run service that + renders Markdown to HTML. + +## Dependencies + +* **express**: Web server framework. +* **gcp-metadata**: Access Google Cloud Platform metadata server. +* **got**: Node.js library for HTTP requests. +* **handlebars** JavaScript template engine. +* **markdown-it**: JavaScript library for parsing and rendering Markdown text. +* **xss**: Node.js HTML sanitizer. \ No newline at end of file diff --git a/run/markdown-preview/editor/main.js b/run/markdown-preview/editor/main.js index 468306a04e..04ed692ddd 100644 --- a/run/markdown-preview/editor/main.js +++ b/run/markdown-preview/editor/main.js @@ -25,7 +25,7 @@ const buildService = async () => { url = process.env.EDITOR_UPSTREAM_RENDER_URL; if (!url) throw Error ("No configuration for upstream render service: add EDITOR_UPSTREAM_RENDER_URL environment variable"); - isAuthenticated = process.env.EDITOR_UPSTREAM_AUTHENTICATED; + isAuthenticated = !process.env.EDITOR_UPSTREAM_UNAUTHENTICATED; if (!isAuthenticated) console.log("Editor: starting in unauthenticated upstream mode"); return {url, isAuthenticated}; } diff --git a/run/markdown-preview/editor/render.js b/run/markdown-preview/editor/render.js index 9e8d1032fa..6ba0dc90f0 100644 --- a/run/markdown-preview/editor/render.js +++ b/run/markdown-preview/editor/render.js @@ -12,15 +12,14 @@ // See the License for the specific language governing permissions and // limitations under the License. -// [START run_secure_request] - // Import the Metadata API const gcpMetadata = require('gcp-metadata') const got = require('got'); -// NewRequest creates a new HTTP request with IAM ID Token credential. +// renderRequest creates a new HTTP request with IAM ID Token credential. // This token is automatically handled by private Cloud Run (fully managed) and Cloud Functions. const renderRequest = async (service, markdown) => { + // [START run_secure_request] let token; // Build the request to the Renderer receiving service. From 39dfd1f6cb6c8dcc903f7cbf3600a410349ccde6 Mon Sep 17 00:00:00 2001 From: kelsk Date: Fri, 24 Apr 2020 15:46:34 -0700 Subject: [PATCH 17/96] tests: added unit tests for editor & renderer --- run/markdown-preview/editor/main.js | 113 +++++++++--------- run/markdown-preview/editor/package.json | 4 +- run/markdown-preview/editor/test/main.test.js | 60 ++++++++++ run/markdown-preview/renderer/main.js | 3 + run/markdown-preview/renderer/package.json | 4 + .../renderer/test/main.test.js | 57 +++++++++ 6 files changed, 186 insertions(+), 55 deletions(-) create mode 100644 run/markdown-preview/editor/test/main.test.js create mode 100644 run/markdown-preview/renderer/test/main.test.js diff --git a/run/markdown-preview/editor/main.js b/run/markdown-preview/editor/main.js index 04ed692ddd..2285cad688 100644 --- a/run/markdown-preview/editor/main.js +++ b/run/markdown-preview/editor/main.js @@ -12,66 +12,71 @@ // See the License for the specific language governing permissions and // limitations under the License. - const express = require('express'); - const handlebars = require('handlebars'); - const { readFileSync } = require('fs'); - const renderRequest = require('./render.js'); +const express = require('express'); +const handlebars = require('handlebars'); +const { readFileSync } = require('fs'); +const renderRequest = require('./render.js'); - const app = express() - app.use(express.json()); +const app = express() +app.use(express.json()); - let url, isAuthenticated, markdownDefault, parsedTemplate; +let url, isAuthenticated, markdownDefault, parsedTemplate; - const buildService = async () => { - url = process.env.EDITOR_UPSTREAM_RENDER_URL; - if (!url) throw Error ("No configuration for upstream render service: add EDITOR_UPSTREAM_RENDER_URL environment variable"); - isAuthenticated = !process.env.EDITOR_UPSTREAM_UNAUTHENTICATED; - if (!isAuthenticated) console.log("Editor: starting in unauthenticated upstream mode"); - return {url, isAuthenticated}; +const buildService = async () => { + url = process.env.EDITOR_UPSTREAM_RENDER_URL; + if (!url) throw Error ("No configuration for upstream render service: add EDITOR_UPSTREAM_RENDER_URL environment variable"); + isAuthenticated = !process.env.EDITOR_UPSTREAM_UNAUTHENTICATED; + if (!isAuthenticated) console.log("Editor: starting in unauthenticated upstream mode"); + return {url, isAuthenticated}; +} + +// Load the template files and serve them with the Editor service. +const buildTemplate = async () => { + try { + markdownDefault = readFileSync(__dirname + '/templates/markdown.md'); + const index = handlebars.compile(readFileSync(__dirname + '/templates/index.html', 'utf8')); + parsedTemplate = index({ default: markdownDefault}); + return parsedTemplate + } catch(err) { + console.log('Error: ', err); + return err } +}; - // Load the template files and serve them with the Editor service. - const buildTemplate = async () => { - try { - markdownDefault = readFileSync(__dirname + '/templates/markdown.md'); - const index = handlebars.compile(readFileSync(__dirname + '/templates/index.html', 'utf8')); - parsedTemplate = index({ default: markdownDefault}); - return parsedTemplate - } catch(err) { - console.log('Error: ', err); - return err - } - }; +app.get('/', async (req, res) => { + try { + const template = await buildTemplate(); + res.send(template); + } catch (err) { + console.log('Error: ', err); + res.send(err); + } +}); - app.get('/', async (req, res) => { - try { - const template = await buildTemplate(); - res.send(template); - } catch (err) { - console.log('Error: ', err); - res.send(err); - } - }); +// The renderRequest makes a request to the Renderer service. +// The request returns the Markdown text converted to HTML. +app.post('/render', async (req, res) => { + try { + const markdown = req.body.data; + const service = await buildService(); + const render = await renderRequest(service, markdown); + const response = JSON.parse(render); + res.send(response.data); + } catch (err) { + console.log('Error: ', err); + res.send(err); + } +}); - // The renderRequest makes a request to the Renderer service. - // The request returns the Markdown text converted to HTML. - app.post('/render', async (req, res) => { - try { - const markdown = req.body.data; - const service = await buildService(); - const render = await renderRequest(service, markdown); - const response = JSON.parse(render); - res.send(response.data); - } catch (err) { - console.log('Error: ', err); - res.send(err); - } - }); +const port = process.env.PORT || 8080; - const port = process.env.PORT || 8080; +app.listen(port, err => { + if (err) console.log('Error: ', err) + console.log(`Editor listening on port ${port}`) +}) - app.listen(port, err => { - if (err) console.log('Error: ', err) - console.log(`Editor listening on port ${port}`) - }) - +// Exports for testing purposes. +module.exports = { + app, + buildTemplate +} \ No newline at end of file diff --git a/run/markdown-preview/editor/package.json b/run/markdown-preview/editor/package.json index c31d3bfb35..ffdd462d94 100644 --- a/run/markdown-preview/editor/package.json +++ b/run/markdown-preview/editor/package.json @@ -21,6 +21,8 @@ "express": "^4.17.1", "gcp-metadata": "^4.0.0", "got": "^10.7.0", - "handlebars": "^4.7.6" + "handlebars": "^4.7.6", + "mocha": "^7.1.1", + "supertest": "^4.0.2" } } diff --git a/run/markdown-preview/editor/test/main.test.js b/run/markdown-preview/editor/test/main.test.js new file mode 100644 index 0000000000..47f6f607c2 --- /dev/null +++ b/run/markdown-preview/editor/test/main.test.js @@ -0,0 +1,60 @@ +// Copyright 2020 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 +// +// https://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('assert'); +const path = require('path'); +const supertest = require('supertest'); + +let request, template, htmlString, markdownString, falseString; + +describe('Unit Tests', () => { + before(async () => { + const {app, buildTemplate} = require(path.join(__dirname, '..', 'main')); + request = supertest(app); + template = await buildTemplate(); + }); + + describe('Handlebars compiler', async () => { + it('includes HTML from the templates', () => { + htmlString = template.includes('Markdown Editor'); + assert.equal(htmlString, true); + }); + + it('includes Markdown from the templates', () => { + markdownString = template.includes("This UI allows a user to write Markdown text"); + assert.equal(markdownString, true) + }); + + it('accurately checks the template for nonexistant string', () => { + falseString = template.includes("not a string in the template"); + assert.equal(falseString, false); + }); + }); + + describe('Render request', () => { + it('should respond with Not Found for a GET request to the /render endpoint', async () => { + await request.get('/render').expect(404); + }); + + it('should respond with a Bad Request for a request with invalid JSON', async () => { + await request.post('/render').type('json').send('invalid string').expect(400); + }); + + it('should succeed with valid JSON', async () => { + await request.post('/render').type('json').send('{"markdown":"valid string"}').expect(200); + }); + }); +}); diff --git a/run/markdown-preview/renderer/main.js b/run/markdown-preview/renderer/main.js index 7f9833759d..9f110b610b 100644 --- a/run/markdown-preview/renderer/main.js +++ b/run/markdown-preview/renderer/main.js @@ -45,3 +45,6 @@ app.listen(port, err => { if (err) console.log('Error: ', err); console.log('Renderer is listening on port ', port); }) + +// Export for testing purposes. +module.exports = app; diff --git a/run/markdown-preview/renderer/package.json b/run/markdown-preview/renderer/package.json index 86bf7f578c..033b9f0981 100644 --- a/run/markdown-preview/renderer/package.json +++ b/run/markdown-preview/renderer/package.json @@ -21,6 +21,10 @@ "express": "^4.17.1", "got": "^10.7.0", "markdown-it": "^10.0.0", + "supertest": "^4.0.2", "xss": "^1.0.6" + }, + "devDependencies": { + "mocha": "^7.1.1" } } diff --git a/run/markdown-preview/renderer/test/main.test.js b/run/markdown-preview/renderer/test/main.test.js new file mode 100644 index 0000000000..79f0b90817 --- /dev/null +++ b/run/markdown-preview/renderer/test/main.test.js @@ -0,0 +1,57 @@ +// Copyright 2020 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 +// +// https://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('assert'); +const path = require('path'); +const supertest = require('supertest'); + +let request; + +describe('Unit Tests', () => { + before(() => { + const app = require(path.join(__dirname, '..', 'main')); + request = supertest(app); + }); + + it('should return Bad Request with an invalid payload', async () => { + await request.post('/').type('json').send('markdown: true').expect(400); + }); + + it('should succeed with a valid request', async () => { + const header = {"markdown": "**markdown text**"}; + await request.post('/').type('json').send(header).expect(200).then(res => { + const body = JSON.parse(res.text); + assert.equal(body.data, "

markdown text

\n") + }); + }); + + it('should succeed with a request that includes xss', async () => { + let markdown = ""; + let header = JSON.stringify({markdown}); + await request.post('/').type('json').send(header).expect(200).then(res => { + const body = JSON.parse(res.text); + assert.deepStrictEqual(body.data, "

<script>script</script>

\n"); + }); + markdown = 'Google' + header = JSON.stringify({markdown}); + await request.post('/').type('json').send(header).expect(200).then(res => { + const body = JSON.parse(res.text); + assert.deepStrictEqual(body.data, "

<a onblur="alert(secret)" href="http://www.google.com">Google</a>

\n"); + }); + + }) +}) + From 5985740318f963159da3e0b88ad82b63270df708 Mon Sep 17 00:00:00 2001 From: kelsk Date: Fri, 24 Apr 2020 16:59:09 -0700 Subject: [PATCH 18/96] added timeout --- run/markdown-preview/editor/render.js | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/run/markdown-preview/editor/render.js b/run/markdown-preview/editor/render.js index 6ba0dc90f0..4603c279dd 100644 --- a/run/markdown-preview/editor/render.js +++ b/run/markdown-preview/editor/render.js @@ -32,16 +32,18 @@ const renderRequest = async (service, markdown) => { }; if (service.isAuthenticated) { - try { - // Query the token with ?audience as the service URL. - const metadataServerTokenPath = `service-accounts/default/identity?audience=${service.url}`; - // Fetch the token and then add it to the request header. - token = await gcpMetadata.instance(metadataServerTokenPath); - serviceRequestOptions.headers['Authorization'] = 'bearer ' + token; - } catch(err) { - console.log('Metadata server could not respond to request ', err); - return err; - } + setTimeout( async () => { + try { + // Query the token with ?audience as the service URL. + const metadataServerTokenPath = `service-accounts/default/identity?audience=${service.url}`; + // Fetch the token and then add it to the request header. + token = await gcpMetadata.instance(metadataServerTokenPath); + serviceRequestOptions.headers['Authorization'] = 'bearer ' + token; + } catch(err) { + console.log('Metadata server could not respond to request ', err); + return err; + } + }, 2000); }; // [END run_secure_request] From 0d0e2efec07591e55fbed0beffc4fa5ad20509bd Mon Sep 17 00:00:00 2001 From: kelsk Date: Mon, 27 Apr 2020 09:37:50 -0700 Subject: [PATCH 19/96] tests: added editor & editor/render tests --- run/markdown-preview/editor/test/main.test.js | 2 +- .../editor/test/render.test.js | 39 +++++++++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 run/markdown-preview/editor/test/render.test.js diff --git a/run/markdown-preview/editor/test/main.test.js b/run/markdown-preview/editor/test/main.test.js index 47f6f607c2..ed97008342 100644 --- a/run/markdown-preview/editor/test/main.test.js +++ b/run/markdown-preview/editor/test/main.test.js @@ -20,7 +20,7 @@ const supertest = require('supertest'); let request, template, htmlString, markdownString, falseString; -describe('Unit Tests', () => { +describe('Editor unit tests', () => { before(async () => { const {app, buildTemplate} = require(path.join(__dirname, '..', 'main')); request = supertest(app); diff --git a/run/markdown-preview/editor/test/render.test.js b/run/markdown-preview/editor/test/render.test.js new file mode 100644 index 0000000000..4565bdbef2 --- /dev/null +++ b/run/markdown-preview/editor/test/render.test.js @@ -0,0 +1,39 @@ +// Copyright 2020 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 +// +// https://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('assert'); +const path = require('path'); + +let request, service, markdown; + +describe('Editor renderRequest unit tests', () => { + before(async () => { + request = require(path.join(__dirname, '..', 'render')); + service = {url: 'https://www.google.com', isAuthenticated: false}; + markdown = "**markdown text**"; + }); + + it('can make an unauthenticated request', async () => { + const res = await request(service, markdown); + assert.deepStrictEqual(res.name, 'HTTPError'); + }) + + it('can make an authenticated request with an invalid url', async () => { + service.isAuthenticated = true; + const res = await request(service, markdown); + assert.deepStrictEqual(res.name, 'HTTPError'); + }) +}); \ No newline at end of file From 7f32b8159a144043c99e794857438b8c837f1a0a Mon Sep 17 00:00:00 2001 From: kelsk Date: Mon, 27 Apr 2020 15:54:36 -0700 Subject: [PATCH 20/96] tests: added & modified test in package.json --- run/markdown-preview/editor/main.js | 1 + run/markdown-preview/editor/package.json | 2 +- run/markdown-preview/editor/test/main.test.js | 28 +++++++++++++++++-- run/markdown-preview/renderer/package.json | 2 +- 4 files changed, 29 insertions(+), 4 deletions(-) diff --git a/run/markdown-preview/editor/main.js b/run/markdown-preview/editor/main.js index 2285cad688..445e10eaff 100644 --- a/run/markdown-preview/editor/main.js +++ b/run/markdown-preview/editor/main.js @@ -78,5 +78,6 @@ app.listen(port, err => { // Exports for testing purposes. module.exports = { app, + buildService, buildTemplate } \ No newline at end of file diff --git a/run/markdown-preview/editor/package.json b/run/markdown-preview/editor/package.json index ffdd462d94..51d3378afd 100644 --- a/run/markdown-preview/editor/package.json +++ b/run/markdown-preview/editor/package.json @@ -15,7 +15,7 @@ "main": "main.js", "scripts": { "start": "node main.js", - "test": "mocha test/*.test.js" + "test": "mocha test/*.test.js --exit" }, "dependencies": { "express": "^4.17.1", diff --git a/run/markdown-preview/editor/test/main.test.js b/run/markdown-preview/editor/test/main.test.js index ed97008342..0b7491da51 100644 --- a/run/markdown-preview/editor/test/main.test.js +++ b/run/markdown-preview/editor/test/main.test.js @@ -18,12 +18,15 @@ const assert = require('assert'); const path = require('path'); const supertest = require('supertest'); -let request, template, htmlString, markdownString, falseString; +const env = Object.assign({}, process.env); + +let request, service, template, htmlString, markdownString, falseString; describe('Editor unit tests', () => { before(async () => { - const {app, buildTemplate} = require(path.join(__dirname, '..', 'main')); + const {app, buildService, buildTemplate} = require(path.join(__dirname, '..', 'main')); request = supertest(app); + service = async () => { return await buildService()}; template = await buildTemplate(); }); @@ -57,4 +60,25 @@ describe('Editor unit tests', () => { await request.post('/render').type('json').send('{"markdown":"valid string"}').expect(200); }); }); + + describe('Service builder', () => { + it('should respond with an error for no EDITOR_UPSTREAM_RENDER_URL var', async () => { + assert.rejects(await service, 'Error') + }); + + // Reload the server with updated env vars. + before(async () => { + process.env.EDITOR_UPSTREAM_RENDER_URL = 'https://www.example.com/'; + const {app, buildService} = require(path.join(__dirname, '..', 'main')); + request = supertest(app); + service = async () => { return await buildService()}; + }) + + it('should return an object with an EDITOR_UPSTREAM_RENDER_URL var', async () => { + const response = await service(); + assert.equal(response.url, process.env.EDITOR_UPSTREAM_RENDER_URL); + assert.equal(response.url, 'https://www.example.com/'); + assert.equal(response.isAuthenticated, true); + }) + }) }); diff --git a/run/markdown-preview/renderer/package.json b/run/markdown-preview/renderer/package.json index 033b9f0981..50d988db22 100644 --- a/run/markdown-preview/renderer/package.json +++ b/run/markdown-preview/renderer/package.json @@ -15,7 +15,7 @@ "main": "main.js", "scripts": { "start": "node main.js", - "test": "mocha test/*.test.js" + "test": "mocha test/*.test.js --exit" }, "dependencies": { "express": "^4.17.1", From f43a2aff2681b214588986cd0298205c79e3ac30 Mon Sep 17 00:00:00 2001 From: kelsk <38271546+kelsk@users.noreply.github.com> Date: Mon, 27 Apr 2020 15:57:07 -0700 Subject: [PATCH 21/96] README: added env var --- run/markdown-preview/README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/run/markdown-preview/README.md b/run/markdown-preview/README.md index 06facc9c65..0c53f6cd9c 100644 --- a/run/markdown-preview/README.md +++ b/run/markdown-preview/README.md @@ -11,6 +11,7 @@ Required variables for this sample include: * `EDITOR_UPSTREAM_RENDER_URL`: The URL of the restricted Cloud Run service that renders Markdown to HTML. +* `EDITOR_UPSTREAM_UNAUTHENTICATED`: A boolean that indicates whether the service is unauthenticated. ## Dependencies @@ -19,4 +20,4 @@ Required variables for this sample include: * **got**: Node.js library for HTTP requests. * **handlebars** JavaScript template engine. * **markdown-it**: JavaScript library for parsing and rendering Markdown text. -* **xss**: Node.js HTML sanitizer. \ No newline at end of file +* **xss**: Node.js HTML sanitizer. From 2db15ae60785d4d348618b83564bef502bf5eed4 Mon Sep 17 00:00:00 2001 From: kelsk Date: Mon, 27 Apr 2020 16:09:56 -0700 Subject: [PATCH 22/96] Revert "README: added env var" This reverts commit f43a2aff2681b214588986cd0298205c79e3ac30. --- run/markdown-preview/README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/run/markdown-preview/README.md b/run/markdown-preview/README.md index 0c53f6cd9c..06facc9c65 100644 --- a/run/markdown-preview/README.md +++ b/run/markdown-preview/README.md @@ -11,7 +11,6 @@ Required variables for this sample include: * `EDITOR_UPSTREAM_RENDER_URL`: The URL of the restricted Cloud Run service that renders Markdown to HTML. -* `EDITOR_UPSTREAM_UNAUTHENTICATED`: A boolean that indicates whether the service is unauthenticated. ## Dependencies @@ -20,4 +19,4 @@ Required variables for this sample include: * **got**: Node.js library for HTTP requests. * **handlebars** JavaScript template engine. * **markdown-it**: JavaScript library for parsing and rendering Markdown text. -* **xss**: Node.js HTML sanitizer. +* **xss**: Node.js HTML sanitizer. \ No newline at end of file From 7706dd15e2d3c0d388eccaeed52acf61c375b7c0 Mon Sep 17 00:00:00 2001 From: kelsk Date: Mon, 27 Apr 2020 16:12:24 -0700 Subject: [PATCH 23/96] README: added env var --- run/markdown-preview/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/run/markdown-preview/README.md b/run/markdown-preview/README.md index 06facc9c65..ddac303d30 100644 --- a/run/markdown-preview/README.md +++ b/run/markdown-preview/README.md @@ -12,6 +12,8 @@ Required variables for this sample include: * `EDITOR_UPSTREAM_RENDER_URL`: The URL of the restricted Cloud Run service that renders Markdown to HTML. +* `EDITOR_UPSTREAM_UNAUTHENTICATED`: A boolean that indicates whether the service is unauthenticated + ## Dependencies * **express**: Web server framework. From 0cdf727c8569d6366b75cc0fb11a48b3025259b1 Mon Sep 17 00:00:00 2001 From: kelsk Date: Tue, 28 Apr 2020 10:45:25 -0700 Subject: [PATCH 24/96] updated author email From 1d17988ba1161064f0417ccb1808dafa14921c38 Mon Sep 17 00:00:00 2001 From: kelsk <38271546+kelsk@users.noreply.github.com> Date: Tue, 28 Apr 2020 14:04:24 -0700 Subject: [PATCH 25/96] Update run/markdown-preview/README.md Co-Authored-By: Averi Kitsch --- run/markdown-preview/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/run/markdown-preview/README.md b/run/markdown-preview/README.md index ddac303d30..70ec4780d4 100644 --- a/run/markdown-preview/README.md +++ b/run/markdown-preview/README.md @@ -12,7 +12,7 @@ Required variables for this sample include: * `EDITOR_UPSTREAM_RENDER_URL`: The URL of the restricted Cloud Run service that renders Markdown to HTML. -* `EDITOR_UPSTREAM_UNAUTHENTICATED`: A boolean that indicates whether the service is unauthenticated +* `EDITOR_UPSTREAM_UNAUTHENTICATED`: (Optional) A boolean that indicates whether the render service requires an authenticated request. ## Dependencies @@ -21,4 +21,4 @@ Required variables for this sample include: * **got**: Node.js library for HTTP requests. * **handlebars** JavaScript template engine. * **markdown-it**: JavaScript library for parsing and rendering Markdown text. -* **xss**: Node.js HTML sanitizer. \ No newline at end of file +* **xss**: Node.js HTML sanitizer. From 5fb25b13395b9641420bbba158a2375d4faccf96 Mon Sep 17 00:00:00 2001 From: kelsk Date: Wed, 29 Apr 2020 11:21:48 -0700 Subject: [PATCH 26/96] add license to Dockerfile --- run/markdown-preview/editor/Dockerfile | 14 ++++++++++++++ run/markdown-preview/renderer/Dockerfile | 14 ++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/run/markdown-preview/editor/Dockerfile b/run/markdown-preview/editor/Dockerfile index e990c654e1..7a079dc314 100644 --- a/run/markdown-preview/editor/Dockerfile +++ b/run/markdown-preview/editor/Dockerfile @@ -1,3 +1,17 @@ +# Copyright 2020 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 +# +# https://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 the official lightweight Node.js 12 image. # https://hub.docker.com/_/node FROM node:12-slim diff --git a/run/markdown-preview/renderer/Dockerfile b/run/markdown-preview/renderer/Dockerfile index e990c654e1..7a079dc314 100644 --- a/run/markdown-preview/renderer/Dockerfile +++ b/run/markdown-preview/renderer/Dockerfile @@ -1,3 +1,17 @@ +# Copyright 2020 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 +# +# https://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 the official lightweight Node.js 12 image. # https://hub.docker.com/_/node FROM node:12-slim From 43bb924047107486b7b8d52d4db5b8482a2abc3b Mon Sep 17 00:00:00 2001 From: kelsk Date: Wed, 29 Apr 2020 11:22:21 -0700 Subject: [PATCH 27/96] fixed json parsing error --- run/markdown-preview/editor/main.js | 2 +- run/markdown-preview/editor/render.js | 24 +++++++++---------- .../editor/test/render.test.js | 8 ++++--- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/run/markdown-preview/editor/main.js b/run/markdown-preview/editor/main.js index 445e10eaff..3a648bf16c 100644 --- a/run/markdown-preview/editor/main.js +++ b/run/markdown-preview/editor/main.js @@ -17,7 +17,7 @@ const handlebars = require('handlebars'); const { readFileSync } = require('fs'); const renderRequest = require('./render.js'); -const app = express() +const app = express(); app.use(express.json()); let url, isAuthenticated, markdownDefault, parsedTemplate; diff --git a/run/markdown-preview/editor/render.js b/run/markdown-preview/editor/render.js index 4603c279dd..6963ca1f98 100644 --- a/run/markdown-preview/editor/render.js +++ b/run/markdown-preview/editor/render.js @@ -28,22 +28,20 @@ const renderRequest = async (service, markdown) => { headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({markdown: markdown}) + body: {markdown: markdown} }; if (service.isAuthenticated) { - setTimeout( async () => { - try { - // Query the token with ?audience as the service URL. - const metadataServerTokenPath = `service-accounts/default/identity?audience=${service.url}`; - // Fetch the token and then add it to the request header. - token = await gcpMetadata.instance(metadataServerTokenPath); - serviceRequestOptions.headers['Authorization'] = 'bearer ' + token; - } catch(err) { - console.log('Metadata server could not respond to request ', err); - return err; - } - }, 2000); + try { + // Query the token with ?audience as the service URL. + const metadataServerTokenPath = `service-accounts/default/identity?audience=${service.url}`; + // Fetch the token and then add it to the request header. + token = await gcpMetadata.instance(metadataServerTokenPath); + serviceRequestOptions.headers['Authorization'] = 'bearer ' + token; + } catch(err) { + console.log('Metadata server could not respond to request ', err); + return err; + } }; // [END run_secure_request] diff --git a/run/markdown-preview/editor/test/render.test.js b/run/markdown-preview/editor/test/render.test.js index 4565bdbef2..2a3f247d78 100644 --- a/run/markdown-preview/editor/test/render.test.js +++ b/run/markdown-preview/editor/test/render.test.js @@ -19,7 +19,9 @@ const path = require('path'); let request, service, markdown; -describe('Editor renderRequest unit tests', () => { +describe('Editor renderRequest unit tests', function () { + this.timeout(6000); + before(async () => { request = require(path.join(__dirname, '..', 'render')); service = {url: 'https://www.google.com', isAuthenticated: false}; @@ -28,12 +30,12 @@ describe('Editor renderRequest unit tests', () => { it('can make an unauthenticated request', async () => { const res = await request(service, markdown); - assert.deepStrictEqual(res.name, 'HTTPError'); + assert.deepStrictEqual(res.name, 'TypeError'); }) it('can make an authenticated request with an invalid url', async () => { service.isAuthenticated = true; const res = await request(service, markdown); - assert.deepStrictEqual(res.name, 'HTTPError'); + assert.deepStrictEqual(res.name, 'FetchError'); }) }); \ No newline at end of file From bea221b93a6803327c1e0d1418e1944dcc2a6563 Mon Sep 17 00:00:00 2001 From: kelsk Date: Wed, 29 Apr 2020 11:22:59 -0700 Subject: [PATCH 28/96] remove dependencies from root package.json --- package.json | 4 ---- 1 file changed, 4 deletions(-) diff --git a/package.json b/package.json index d9fb36c015..2cb490bc93 100644 --- a/package.json +++ b/package.json @@ -25,9 +25,5 @@ "eslint-plugin-promise": "^4.1.1", "prettier": "^2.0.0", "requestretry": "^4.0.0" - }, - "dependencies": { - "express": "^4.17.1", - "showdown": "^1.9.1" } } From 5277daf16e4a7e0fb4a2e8ed7eda52a43133173a Mon Sep 17 00:00:00 2001 From: kelsk Date: Wed, 29 Apr 2020 19:09:37 -0700 Subject: [PATCH 29/96] remove xss (not needed) --- run/markdown-preview/renderer/main.js | 26 ++++++++-------------- run/markdown-preview/renderer/package.json | 11 +++++---- 2 files changed, 14 insertions(+), 23 deletions(-) diff --git a/run/markdown-preview/renderer/main.js b/run/markdown-preview/renderer/main.js index 9f110b610b..ce95423430 100644 --- a/run/markdown-preview/renderer/main.js +++ b/run/markdown-preview/renderer/main.js @@ -14,36 +14,28 @@ const express = require('express'); const MarkdownIt = require('markdown-it'); -const xss = require('xss'); const app = express(); - app.use(express.json()); app.post('/', (req, res) => { try { - // Get the Markdown text and convert it into HTML using markdown-it - const markdown = req.body.markdown; + const markdown = req.body.markdown.data ? req.body.markdown.data : ''; const md = new MarkdownIt(); - const unsafeData = md.render(markdown); - - // Add XSS sanitizer - const safeData = xss(unsafeData); - const response = JSON.stringify({data: safeData}); - res.send(response); - + const html = md.render(markdown); + const response = {data: html}; + res.status(200).send(response); } catch(err) { - console.log('Error: ', err); - res.send(err); + console.log('Error rendering Markdown: ', err); + res.status(400).send(err); } }) -const port = process.env.PORT || 8080; +const PORT = process.env.PORT || 8080; -app.listen(port, err => { - if (err) console.log('Error: ', err); - console.log('Renderer is listening on port ', port); +app.listen(PORT, err => { + console.log(`Renderer is listening on port ${PORT}`); }) // Export for testing purposes. diff --git a/run/markdown-preview/renderer/package.json b/run/markdown-preview/renderer/package.json index 50d988db22..a9667f3525 100644 --- a/run/markdown-preview/renderer/package.json +++ b/run/markdown-preview/renderer/package.json @@ -1,10 +1,10 @@ { - "name": "markdown-preview", - "description": "", + "name": "markdown-preview-renderer", + "description": "Cloud Run service to demonstrate service-to-service authentication, paired with Editor service.", "version": "0.0.1", "private": true, "license": "Apache-2.0", - "author": "Google Inc.", + "author": "Google LLC", "repository": { "type": "git", "url": "https://github.com/GoogleCloudPlatform/nodejs-docs-samples.git" @@ -21,10 +21,9 @@ "express": "^4.17.1", "got": "^10.7.0", "markdown-it": "^10.0.0", - "supertest": "^4.0.2", - "xss": "^1.0.6" }, "devDependencies": { - "mocha": "^7.1.1" + "mocha": "^7.1.1", + "supertest": "^4.0.2" } } From a3597867a4348b719b583bba5a163a815f676d10 Mon Sep 17 00:00:00 2001 From: kelsk Date: Wed, 29 Apr 2020 19:12:01 -0700 Subject: [PATCH 30/96] update node version in Dockerfile --- run/markdown-preview/editor/Dockerfile | 2 +- run/markdown-preview/renderer/Dockerfile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/run/markdown-preview/editor/Dockerfile b/run/markdown-preview/editor/Dockerfile index 7a079dc314..f1f0cf442e 100644 --- a/run/markdown-preview/editor/Dockerfile +++ b/run/markdown-preview/editor/Dockerfile @@ -14,7 +14,7 @@ # Use the official lightweight Node.js 12 image. # https://hub.docker.com/_/node -FROM node:12-slim +FROM node:10-slim # Create and change to the app directory. WORKDIR /usr/src/app diff --git a/run/markdown-preview/renderer/Dockerfile b/run/markdown-preview/renderer/Dockerfile index 7a079dc314..f1f0cf442e 100644 --- a/run/markdown-preview/renderer/Dockerfile +++ b/run/markdown-preview/renderer/Dockerfile @@ -14,7 +14,7 @@ # Use the official lightweight Node.js 12 image. # https://hub.docker.com/_/node -FROM node:12-slim +FROM node:10-slim # Create and change to the app directory. WORKDIR /usr/src/app From 4391a1b2111aa0eee807eb2d0e7e95432e40f9ce Mon Sep 17 00:00:00 2001 From: kelsk Date: Wed, 29 Apr 2020 20:56:52 -0700 Subject: [PATCH 31/96] refactored code to reflect code review --- run/markdown-preview/editor/main.js | 41 +++++++++++++-------------- run/markdown-preview/editor/render.js | 10 +++---- 2 files changed, 24 insertions(+), 27 deletions(-) diff --git a/run/markdown-preview/editor/main.js b/run/markdown-preview/editor/main.js index 3a648bf16c..e8c660c329 100644 --- a/run/markdown-preview/editor/main.js +++ b/run/markdown-preview/editor/main.js @@ -20,9 +20,9 @@ const renderRequest = require('./render.js'); const app = express(); app.use(express.json()); -let url, isAuthenticated, markdownDefault, parsedTemplate; +let url, isAuthenticated, markdownDefault, compiledIndex, template; -const buildService = async () => { +const init = () => { url = process.env.EDITOR_UPSTREAM_RENDER_URL; if (!url) throw Error ("No configuration for upstream render service: add EDITOR_UPSTREAM_RENDER_URL environment variable"); isAuthenticated = !process.env.EDITOR_UPSTREAM_UNAUTHENTICATED; @@ -30,26 +30,27 @@ const buildService = async () => { return {url, isAuthenticated}; } +const service = init(); + // Load the template files and serve them with the Editor service. const buildTemplate = async () => { try { markdownDefault = readFileSync(__dirname + '/templates/markdown.md'); const index = handlebars.compile(readFileSync(__dirname + '/templates/index.html', 'utf8')); - parsedTemplate = index({ default: markdownDefault}); - return parsedTemplate + compiledIndex = index({ default: markdownDefault}); + return compiledIndex } catch(err) { - console.log('Error: ', err); - return err + throw Error ('Error loading template: ', err); } }; app.get('/', async (req, res) => { try { - const template = await buildTemplate(); - res.send(template); + template = await buildTemplate(); + res.status(200).send(template); } catch (err) { - console.log('Error: ', err); - res.send(err); + console.log('Error loading the Editor service: ', err); + res.status(500).send(err); } }); @@ -57,27 +58,25 @@ app.get('/', async (req, res) => { // The request returns the Markdown text converted to HTML. app.post('/render', async (req, res) => { try { - const markdown = req.body.data; - const service = await buildService(); + const markdown = req.body; const render = await renderRequest(service, markdown); - const response = JSON.parse(render); - res.send(response.data); + const response = render; + res.status(200).send(response.data); } catch (err) { - console.log('Error: ', err); - res.send(err); + console.log('Error querying the Renderer service: ', err); + res.status(500).send(err); } }); -const port = process.env.PORT || 8080; +const PORT = process.env.PORT || 8080; -app.listen(port, err => { - if (err) console.log('Error: ', err) - console.log(`Editor listening on port ${port}`) +app.listen(PORT, err => { + console.log(`Renderer is listening on port ${PORT}`); }) // Exports for testing purposes. module.exports = { app, - buildService, + init, buildTemplate } \ No newline at end of file diff --git a/run/markdown-preview/editor/render.js b/run/markdown-preview/editor/render.js index 6963ca1f98..fc328470a1 100644 --- a/run/markdown-preview/editor/render.js +++ b/run/markdown-preview/editor/render.js @@ -12,7 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -// Import the Metadata API const gcpMetadata = require('gcp-metadata') const got = require('got'); @@ -28,7 +27,8 @@ const renderRequest = async (service, markdown) => { headers: { 'Content-Type': 'application/json' }, - body: {markdown: markdown} + json: {markdown}, + responseType: 'json' }; if (service.isAuthenticated) { @@ -39,8 +39,7 @@ const renderRequest = async (service, markdown) => { token = await gcpMetadata.instance(metadataServerTokenPath); serviceRequestOptions.headers['Authorization'] = 'bearer ' + token; } catch(err) { - console.log('Metadata server could not respond to request ', err); - return err; + throw Error('Metadata server could not respond to request: ', err); } }; // [END run_secure_request] @@ -52,8 +51,7 @@ const renderRequest = async (service, markdown) => { const serviceResponse = serviceRequest.body; return serviceResponse; } catch (err) { - console.log('Renderer service could not respond to request ', err); - return err; + throw Error('Renderer service could not respond to request ', err); }; // [END run_secure_request_do] }; From d2410d893ad67297049563a36f6f3a51c0501469 Mon Sep 17 00:00:00 2001 From: kelsk Date: Wed, 29 Apr 2020 20:57:31 -0700 Subject: [PATCH 32/96] updated tests to respond to new code --- run/markdown-preview/editor/test/main.test.js | 64 +++++++++++-------- .../editor/test/render.test.js | 6 +- .../renderer/test/main.test.js | 17 +++-- 3 files changed, 47 insertions(+), 40 deletions(-) diff --git a/run/markdown-preview/editor/test/main.test.js b/run/markdown-preview/editor/test/main.test.js index 0b7491da51..3b7bea1871 100644 --- a/run/markdown-preview/editor/test/main.test.js +++ b/run/markdown-preview/editor/test/main.test.js @@ -18,19 +18,41 @@ const assert = require('assert'); const path = require('path'); const supertest = require('supertest'); -const env = Object.assign({}, process.env); - let request, service, template, htmlString, markdownString, falseString; describe('Editor unit tests', () => { - before(async () => { - const {app, buildService, buildTemplate} = require(path.join(__dirname, '..', 'main')); - request = supertest(app); - service = async () => { return await buildService()}; - template = await buildTemplate(); + describe('Service init', () => { + it('should respond with an error for no EDITOR_UPSTREAM_RENDER_URL var', async () => { + const {app, init} = require(path.join(__dirname, '..', 'main')); + request = supertest(app); + service = () => init(); + assert.rejects(service, 'Error') + }); + + // Reload the server with updated env vars. + before(async () => { + process.env.EDITOR_UPSTREAM_RENDER_URL = 'https://www.example.com/'; + const {app, init} = require(path.join(__dirname, '..', 'main')); + request = supertest(app); + service = () => { return init()}; + }) + + it('should return an object with an EDITOR_UPSTREAM_RENDER_URL var', async () => { + const response = service(); + assert.equal(response.url, process.env.EDITOR_UPSTREAM_RENDER_URL); + assert.equal(response.url, 'https://www.example.com/'); + assert.equal(response.isAuthenticated, true); + }) }); describe('Handlebars compiler', async () => { + before(async () => { + process.env.EDITOR_UPSTREAM_RENDER_URL = 'https://www.example.com/'; + const {app, init, buildTemplate} = require(path.join(__dirname, '..', 'main')); + request = supertest(app); + template = await buildTemplate(init()); + }) + it('includes HTML from the templates', () => { htmlString = template.includes('Markdown Editor'); assert.equal(htmlString, true); @@ -55,30 +77,18 @@ describe('Editor unit tests', () => { it('should respond with a Bad Request for a request with invalid JSON', async () => { await request.post('/render').type('json').send('invalid string').expect(400); }); - - it('should succeed with valid JSON', async () => { - await request.post('/render').type('json').send('{"markdown":"valid string"}').expect(200); - }); - }); - - describe('Service builder', () => { - it('should respond with an error for no EDITOR_UPSTREAM_RENDER_URL var', async () => { - assert.rejects(await service, 'Error') - }); - + // Reload the server with updated env vars. before(async () => { process.env.EDITOR_UPSTREAM_RENDER_URL = 'https://www.example.com/'; - const {app, buildService} = require(path.join(__dirname, '..', 'main')); + const {app, init} = require(path.join(__dirname, '..', 'main')); request = supertest(app); - service = async () => { return await buildService()}; + service = init(); }) - it('should return an object with an EDITOR_UPSTREAM_RENDER_URL var', async () => { - const response = await service(); - assert.equal(response.url, process.env.EDITOR_UPSTREAM_RENDER_URL); - assert.equal(response.url, 'https://www.example.com/'); - assert.equal(response.isAuthenticated, true); - }) - }) + it('should successfully make a request with valid JSON', async function () { + this.timeout(6000); + await request.post('/render').type('json').send({data:"valid string"}).expect(500); + }); + }); }); diff --git a/run/markdown-preview/editor/test/render.test.js b/run/markdown-preview/editor/test/render.test.js index 2a3f247d78..65e1003f26 100644 --- a/run/markdown-preview/editor/test/render.test.js +++ b/run/markdown-preview/editor/test/render.test.js @@ -29,13 +29,11 @@ describe('Editor renderRequest unit tests', function () { }); it('can make an unauthenticated request', async () => { - const res = await request(service, markdown); - assert.deepStrictEqual(res.name, 'TypeError'); + assert.rejects(request(service, markdown)); }) it('can make an authenticated request with an invalid url', async () => { service.isAuthenticated = true; - const res = await request(service, markdown); - assert.deepStrictEqual(res.name, 'FetchError'); + assert.rejects(request(service, markdown)); }) }); \ No newline at end of file diff --git a/run/markdown-preview/renderer/test/main.test.js b/run/markdown-preview/renderer/test/main.test.js index 79f0b90817..f0df48fc18 100644 --- a/run/markdown-preview/renderer/test/main.test.js +++ b/run/markdown-preview/renderer/test/main.test.js @@ -31,27 +31,26 @@ describe('Unit Tests', () => { }); it('should succeed with a valid request', async () => { - const header = {"markdown": "**markdown text**"}; + const header = {markdown: { data: "**markdown text**"}}; await request.post('/').type('json').send(header).expect(200).then(res => { - const body = JSON.parse(res.text); + const body = res.body; assert.equal(body.data, "

markdown text

\n") }); }); it('should succeed with a request that includes xss', async () => { - let markdown = ""; - let header = JSON.stringify({markdown}); + let text = ""; + let header = {markdown: { data: text}}; await request.post('/').type('json').send(header).expect(200).then(res => { - const body = JSON.parse(res.text); + const body = res.body; assert.deepStrictEqual(body.data, "

<script>script</script>

\n"); }); - markdown = 'Google' - header = JSON.stringify({markdown}); + text = 'Google' + header = {markdown: { data: text}}; await request.post('/').type('json').send(header).expect(200).then(res => { - const body = JSON.parse(res.text); + const body = res.body; assert.deepStrictEqual(body.data, "

<a onblur="alert(secret)" href="http://www.google.com">Google</a>

\n"); }); - }) }) From f2fe58ee400007c2e68bae442a3ae7e0496a3fe0 Mon Sep 17 00:00:00 2001 From: kelsk Date: Wed, 29 Apr 2020 20:58:15 -0700 Subject: [PATCH 33/96] update packages.json --- run/markdown-preview/editor/package.json | 8 +++++--- run/markdown-preview/renderer/package.json | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/run/markdown-preview/editor/package.json b/run/markdown-preview/editor/package.json index 51d3378afd..a1d1429248 100644 --- a/run/markdown-preview/editor/package.json +++ b/run/markdown-preview/editor/package.json @@ -1,10 +1,10 @@ { "name": "markdown-preview", - "description": "", + "description": "Cloud Run service to demonstrate service-to-service authentication, paired with Renderer service.", "version": "0.0.1", "private": true, "license": "Apache-2.0", - "author": "Google Inc.", + "author": "Google LLC", "repository": { "type": "git", "url": "https://github.com/GoogleCloudPlatform/nodejs-docs-samples.git" @@ -21,7 +21,9 @@ "express": "^4.17.1", "gcp-metadata": "^4.0.0", "got": "^10.7.0", - "handlebars": "^4.7.6", + "handlebars": "^4.7.6" + }, + "devDependencies": { "mocha": "^7.1.1", "supertest": "^4.0.2" } diff --git a/run/markdown-preview/renderer/package.json b/run/markdown-preview/renderer/package.json index a9667f3525..bb675bc32d 100644 --- a/run/markdown-preview/renderer/package.json +++ b/run/markdown-preview/renderer/package.json @@ -20,7 +20,7 @@ "dependencies": { "express": "^4.17.1", "got": "^10.7.0", - "markdown-it": "^10.0.0", + "markdown-it": "^10.0.0" }, "devDependencies": { "mocha": "^7.1.1", From 88dd67d53ae3406d31f2c8e880436e73a31879e3 Mon Sep 17 00:00:00 2001 From: kelsk Date: Thu, 30 Apr 2020 10:53:00 -0700 Subject: [PATCH 34/96] run/markdown-preview: add sample --- .kokoro/run/markdown-preview-editor.cfg | 8 ++++++++ .kokoro/run/markdown-preview-renderer.cfg | 8 ++++++++ 2 files changed, 16 insertions(+) create mode 100644 .kokoro/run/markdown-preview-editor.cfg create mode 100644 .kokoro/run/markdown-preview-renderer.cfg diff --git a/.kokoro/run/markdown-preview-editor.cfg b/.kokoro/run/markdown-preview-editor.cfg new file mode 100644 index 0000000000..15a02abb79 --- /dev/null +++ b/.kokoro/run/markdown-preview-editor.cfg @@ -0,0 +1,8 @@ + +# Format: //devtools/kokoro/config/proto/build.proto + +# Set the folder in which the tests are run +env_vars: { + key: "PROJECT" + value: "run/markdown/editor" +} \ No newline at end of file diff --git a/.kokoro/run/markdown-preview-renderer.cfg b/.kokoro/run/markdown-preview-renderer.cfg new file mode 100644 index 0000000000..d411b163ad --- /dev/null +++ b/.kokoro/run/markdown-preview-renderer.cfg @@ -0,0 +1,8 @@ + +# Format: //devtools/kokoro/config/proto/build.proto + +# Set the folder in which the tests are run +env_vars: { + key: "PROJECT" + value: "run/markdown/renderer" +} \ No newline at end of file From d70d1a621a53426d3218b9120e48dc9e88697564 Mon Sep 17 00:00:00 2001 From: kelsk Date: Thu, 30 Apr 2020 12:03:35 -0700 Subject: [PATCH 35/96] update: syntax & grammar fixes --- run/markdown-preview/editor/Dockerfile | 2 +- run/markdown-preview/editor/main.js | 12 ++++++------ run/markdown-preview/renderer/Dockerfile | 2 +- run/markdown-preview/renderer/main.js | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/run/markdown-preview/editor/Dockerfile b/run/markdown-preview/editor/Dockerfile index f1f0cf442e..143b8248b6 100644 --- a/run/markdown-preview/editor/Dockerfile +++ b/run/markdown-preview/editor/Dockerfile @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -# Use the official lightweight Node.js 12 image. +# Use the official lightweight Node.js 10 image. # https://hub.docker.com/_/node FROM node:10-slim diff --git a/run/markdown-preview/editor/main.js b/run/markdown-preview/editor/main.js index e8c660c329..52a0298edc 100644 --- a/run/markdown-preview/editor/main.js +++ b/run/markdown-preview/editor/main.js @@ -28,7 +28,7 @@ const init = () => { isAuthenticated = !process.env.EDITOR_UPSTREAM_UNAUTHENTICATED; if (!isAuthenticated) console.log("Editor: starting in unauthenticated upstream mode"); return {url, isAuthenticated}; -} +}; const service = init(); @@ -36,9 +36,9 @@ const service = init(); const buildTemplate = async () => { try { markdownDefault = readFileSync(__dirname + '/templates/markdown.md'); - const index = handlebars.compile(readFileSync(__dirname + '/templates/index.html', 'utf8')); - compiledIndex = index({ default: markdownDefault}); - return compiledIndex + const indexTemplate = handlebars.compile(readFileSync(__dirname + '/templates/index.html', 'utf8')); + compiledTemplate = indexTemplate({ default: markdownDefault}); + return compiledTemplate } catch(err) { throw Error ('Error loading template: ', err); } @@ -72,11 +72,11 @@ const PORT = process.env.PORT || 8080; app.listen(PORT, err => { console.log(`Renderer is listening on port ${PORT}`); -}) +}); // Exports for testing purposes. module.exports = { app, init, buildTemplate -} \ No newline at end of file +}; \ No newline at end of file diff --git a/run/markdown-preview/renderer/Dockerfile b/run/markdown-preview/renderer/Dockerfile index f1f0cf442e..143b8248b6 100644 --- a/run/markdown-preview/renderer/Dockerfile +++ b/run/markdown-preview/renderer/Dockerfile @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -# Use the official lightweight Node.js 12 image. +# Use the official lightweight Node.js 10 image. # https://hub.docker.com/_/node FROM node:10-slim diff --git a/run/markdown-preview/renderer/main.js b/run/markdown-preview/renderer/main.js index ce95423430..200d91e8c8 100644 --- a/run/markdown-preview/renderer/main.js +++ b/run/markdown-preview/renderer/main.js @@ -20,7 +20,7 @@ app.use(express.json()); app.post('/', (req, res) => { try { - // Get the Markdown text and convert it into HTML using markdown-it + // Get the Markdown text and convert it into HTML using markdown-it. const markdown = req.body.markdown.data ? req.body.markdown.data : ''; const md = new MarkdownIt(); const html = md.render(markdown); From a8952faf515a52a70319efcf17e15b9be8284ca7 Mon Sep 17 00:00:00 2001 From: kelsk Date: Thu, 30 Apr 2020 12:04:24 -0700 Subject: [PATCH 36/96] change: throw error if markdown data absent from req --- run/markdown-preview/renderer/main.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/run/markdown-preview/renderer/main.js b/run/markdown-preview/renderer/main.js index 200d91e8c8..876f41f496 100644 --- a/run/markdown-preview/renderer/main.js +++ b/run/markdown-preview/renderer/main.js @@ -19,11 +19,16 @@ const app = express(); app.use(express.json()); app.post('/', (req, res) => { + let markdown; + if (req.body.markdown.data) { + markdown = req.body.markdown.data + } else { + res.status(400).send('Error: Markdown data could not be retrieved.') + }; try { // Get the Markdown text and convert it into HTML using markdown-it. - const markdown = req.body.markdown.data ? req.body.markdown.data : ''; const md = new MarkdownIt(); - const html = md.render(markdown); + const html = md.render(markdown()); const response = {data: html}; res.status(200).send(response); } catch(err) { From f2706f0af2b2e22193bb83ef8dcc078b6e110dfe Mon Sep 17 00:00:00 2001 From: kelsk Date: Fri, 1 May 2020 09:33:48 -0700 Subject: [PATCH 37/96] change req.body validation --- run/markdown-preview/renderer/main.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/run/markdown-preview/renderer/main.js b/run/markdown-preview/renderer/main.js index 876f41f496..d5cf2ecda8 100644 --- a/run/markdown-preview/renderer/main.js +++ b/run/markdown-preview/renderer/main.js @@ -20,11 +20,14 @@ app.use(express.json()); app.post('/', (req, res) => { let markdown; - if (req.body.markdown.data) { - markdown = req.body.markdown.data + if (!req.body.markdown.data) { + const msg = 'Markdown data could not be retrieved.'; + console.log(msg); + res.status(400).send(`Error: ${msg}`) } else { - res.status(400).send('Error: Markdown data could not be retrieved.') + markdown = req.body.markdown.data; }; + try { // Get the Markdown text and convert it into HTML using markdown-it. const md = new MarkdownIt(); From bca1bb3e4a8518aa9f704e1cf40bf98b5da99fac Mon Sep 17 00:00:00 2001 From: kelsk Date: Fri, 1 May 2020 09:51:47 -0700 Subject: [PATCH 38/96] fix syntax error --- run/markdown-preview/renderer/main.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/run/markdown-preview/renderer/main.js b/run/markdown-preview/renderer/main.js index d5cf2ecda8..5f6963db81 100644 --- a/run/markdown-preview/renderer/main.js +++ b/run/markdown-preview/renderer/main.js @@ -27,11 +27,11 @@ app.post('/', (req, res) => { } else { markdown = req.body.markdown.data; }; - + try { // Get the Markdown text and convert it into HTML using markdown-it. const md = new MarkdownIt(); - const html = md.render(markdown()); + const html = md.render(markdown); const response = {data: html}; res.status(200).send(response); } catch(err) { From 48907291413bbf6f7092a3aefbd2b25fee776ba8 Mon Sep 17 00:00:00 2001 From: kelsk Date: Fri, 1 May 2020 19:10:50 -0700 Subject: [PATCH 39/96] change: fixed file name --- .kokoro/run/markdown-preview-editor.cfg | 2 +- .kokoro/run/markdown-preview-renderer.cfg | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.kokoro/run/markdown-preview-editor.cfg b/.kokoro/run/markdown-preview-editor.cfg index 15a02abb79..034c274b61 100644 --- a/.kokoro/run/markdown-preview-editor.cfg +++ b/.kokoro/run/markdown-preview-editor.cfg @@ -4,5 +4,5 @@ # Set the folder in which the tests are run env_vars: { key: "PROJECT" - value: "run/markdown/editor" + value: "run/markdown-preview/editor" } \ No newline at end of file diff --git a/.kokoro/run/markdown-preview-renderer.cfg b/.kokoro/run/markdown-preview-renderer.cfg index d411b163ad..e9507ec5a6 100644 --- a/.kokoro/run/markdown-preview-renderer.cfg +++ b/.kokoro/run/markdown-preview-renderer.cfg @@ -4,5 +4,5 @@ # Set the folder in which the tests are run env_vars: { key: "PROJECT" - value: "run/markdown/renderer" + value: "run/markdown-preview/renderer" } \ No newline at end of file From 5de6e9cf5b612e98a44f9da3f876d2fe085d8042 Mon Sep 17 00:00:00 2001 From: kelsk Date: Tue, 5 May 2020 09:49:11 -0700 Subject: [PATCH 40/96] syntax: remove newline --- .kokoro/run/markdown-preview-editor.cfg | 1 - .kokoro/run/markdown-preview-renderer.cfg | 1 - 2 files changed, 2 deletions(-) diff --git a/.kokoro/run/markdown-preview-editor.cfg b/.kokoro/run/markdown-preview-editor.cfg index 034c274b61..e73fee6b7b 100644 --- a/.kokoro/run/markdown-preview-editor.cfg +++ b/.kokoro/run/markdown-preview-editor.cfg @@ -1,4 +1,3 @@ - # Format: //devtools/kokoro/config/proto/build.proto # Set the folder in which the tests are run diff --git a/.kokoro/run/markdown-preview-renderer.cfg b/.kokoro/run/markdown-preview-renderer.cfg index e9507ec5a6..dec772aeb2 100644 --- a/.kokoro/run/markdown-preview-renderer.cfg +++ b/.kokoro/run/markdown-preview-renderer.cfg @@ -1,4 +1,3 @@ - # Format: //devtools/kokoro/config/proto/build.proto # Set the folder in which the tests are run From c97f02b18d38691338d8efdd4c39c223974a5d61 Mon Sep 17 00:00:00 2001 From: kelsk Date: Tue, 5 May 2020 09:50:01 -0700 Subject: [PATCH 41/96] update: engine version --- run/markdown-preview/editor/package.json | 2 +- run/markdown-preview/renderer/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/run/markdown-preview/editor/package.json b/run/markdown-preview/editor/package.json index a1d1429248..a56045ac30 100644 --- a/run/markdown-preview/editor/package.json +++ b/run/markdown-preview/editor/package.json @@ -10,7 +10,7 @@ "url": "https://github.com/GoogleCloudPlatform/nodejs-docs-samples.git" }, "engines": { - "node": ">= 12.0.0" + "node": ">= 10.0.0" }, "main": "main.js", "scripts": { diff --git a/run/markdown-preview/renderer/package.json b/run/markdown-preview/renderer/package.json index bb675bc32d..c12c202378 100644 --- a/run/markdown-preview/renderer/package.json +++ b/run/markdown-preview/renderer/package.json @@ -10,7 +10,7 @@ "url": "https://github.com/GoogleCloudPlatform/nodejs-docs-samples.git" }, "engines": { - "node": ">= 12.0.0" + "node": ">= 10.0.0" }, "main": "main.js", "scripts": { From 6c0897c6538ace79011de1b7c18eb759235270e3 Mon Sep 17 00:00:00 2001 From: kelsk Date: Tue, 5 May 2020 11:56:20 -0700 Subject: [PATCH 42/96] syntax: general fixes --- run/markdown-preview/editor/main.js | 15 +++++++-------- run/markdown-preview/editor/render.js | 4 ++-- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/run/markdown-preview/editor/main.js b/run/markdown-preview/editor/main.js index 52a0298edc..77cfda9d91 100644 --- a/run/markdown-preview/editor/main.js +++ b/run/markdown-preview/editor/main.js @@ -33,20 +33,20 @@ const init = () => { const service = init(); // Load the template files and serve them with the Editor service. -const buildTemplate = async () => { +const buildTemplate = () => { try { markdownDefault = readFileSync(__dirname + '/templates/markdown.md'); const indexTemplate = handlebars.compile(readFileSync(__dirname + '/templates/index.html', 'utf8')); compiledTemplate = indexTemplate({ default: markdownDefault}); - return compiledTemplate + return compiledTemplate; } catch(err) { throw Error ('Error loading template: ', err); } }; -app.get('/', async (req, res) => { +app.get('/', (req, res) => { try { - template = await buildTemplate(); + template = buildTemplate(); res.status(200).send(template); } catch (err) { console.log('Error loading the Editor service: ', err); @@ -59,9 +59,8 @@ app.get('/', async (req, res) => { app.post('/render', async (req, res) => { try { const markdown = req.body; - const render = await renderRequest(service, markdown); - const response = render; - res.status(200).send(response.data); + const response = await renderRequest(service, markdown); + res.status(200).send(response.body.data); } catch (err) { console.log('Error querying the Renderer service: ', err); res.status(500).send(err); @@ -71,7 +70,7 @@ app.post('/render', async (req, res) => { const PORT = process.env.PORT || 8080; app.listen(PORT, err => { - console.log(`Renderer is listening on port ${PORT}`); + console.log(`Editor service is listening on port ${PORT}`); }); // Exports for testing purposes. diff --git a/run/markdown-preview/editor/render.js b/run/markdown-preview/editor/render.js index fc328470a1..1d20a65b2d 100644 --- a/run/markdown-preview/editor/render.js +++ b/run/markdown-preview/editor/render.js @@ -47,8 +47,8 @@ const renderRequest = async (service, markdown) => { // [START run_secure_request_do] try { // serviceRequest converts the Markdown plaintext to HTML. - const serviceRequest = await got(service.url, serviceRequestOptions); - const serviceResponse = serviceRequest.body; + const serviceResponse = await got(service.url, serviceRequestOptions); + console.log('serviceResponseee: ', serviceResponse); return serviceResponse; } catch (err) { throw Error('Renderer service could not respond to request ', err); From 8842611d53ffbd367188661ce65120f9ecf50d4a Mon Sep 17 00:00:00 2001 From: kelsk Date: Wed, 6 May 2020 10:55:02 -0700 Subject: [PATCH 43/96] code: replace json with plain text --- run/markdown-preview/editor/main.js | 6 +++--- run/markdown-preview/editor/render.js | 6 ++---- run/markdown-preview/renderer/main.js | 9 ++++----- 3 files changed, 9 insertions(+), 12 deletions(-) diff --git a/run/markdown-preview/editor/main.js b/run/markdown-preview/editor/main.js index 77cfda9d91..4bf53a82c2 100644 --- a/run/markdown-preview/editor/main.js +++ b/run/markdown-preview/editor/main.js @@ -20,7 +20,7 @@ const renderRequest = require('./render.js'); const app = express(); app.use(express.json()); -let url, isAuthenticated, markdownDefault, compiledIndex, template; +let url, isAuthenticated, markdownDefault, compiledTemplate, template; const init = () => { url = process.env.EDITOR_UPSTREAM_RENDER_URL; @@ -37,7 +37,7 @@ const buildTemplate = () => { try { markdownDefault = readFileSync(__dirname + '/templates/markdown.md'); const indexTemplate = handlebars.compile(readFileSync(__dirname + '/templates/index.html', 'utf8')); - compiledTemplate = indexTemplate({ default: markdownDefault}); + compiledTemplate = indexTemplate({default: markdownDefault}); return compiledTemplate; } catch(err) { throw Error ('Error loading template: ', err); @@ -60,7 +60,7 @@ app.post('/render', async (req, res) => { try { const markdown = req.body; const response = await renderRequest(service, markdown); - res.status(200).send(response.body.data); + res.status(200).send(response.body); } catch (err) { console.log('Error querying the Renderer service: ', err); res.status(500).send(err); diff --git a/run/markdown-preview/editor/render.js b/run/markdown-preview/editor/render.js index 1d20a65b2d..0c45e6f823 100644 --- a/run/markdown-preview/editor/render.js +++ b/run/markdown-preview/editor/render.js @@ -25,10 +25,9 @@ const renderRequest = async (service, markdown) => { const serviceRequestOptions = { method: 'POST', headers: { - 'Content-Type': 'application/json' + 'Content-Type': 'text/plain' }, - json: {markdown}, - responseType: 'json' + body: markdown.data }; if (service.isAuthenticated) { @@ -48,7 +47,6 @@ const renderRequest = async (service, markdown) => { try { // serviceRequest converts the Markdown plaintext to HTML. const serviceResponse = await got(service.url, serviceRequestOptions); - console.log('serviceResponseee: ', serviceResponse); return serviceResponse; } catch (err) { throw Error('Renderer service could not respond to request ', err); diff --git a/run/markdown-preview/renderer/main.js b/run/markdown-preview/renderer/main.js index 5f6963db81..06227c7233 100644 --- a/run/markdown-preview/renderer/main.js +++ b/run/markdown-preview/renderer/main.js @@ -16,24 +16,23 @@ const express = require('express'); const MarkdownIt = require('markdown-it'); const app = express(); -app.use(express.json()); +app.use(express.text()); app.post('/', (req, res) => { let markdown; - if (!req.body.markdown.data) { + if (!req.body) { const msg = 'Markdown data could not be retrieved.'; console.log(msg); res.status(400).send(`Error: ${msg}`) } else { - markdown = req.body.markdown.data; + markdown = req.body; }; try { // Get the Markdown text and convert it into HTML using markdown-it. const md = new MarkdownIt(); const html = md.render(markdown); - const response = {data: html}; - res.status(200).send(response); + res.status(200).send(html); } catch(err) { console.log('Error rendering Markdown: ', err); res.status(400).send(err); From b81c140e0449283ec7ee1011a8ba860f91861b79 Mon Sep 17 00:00:00 2001 From: kelsk Date: Wed, 6 May 2020 11:53:47 -0700 Subject: [PATCH 44/96] test: modified tests for accuracy --- run/markdown-preview/editor/test/main.test.js | 34 +++++++------------ .../editor/test/render.test.js | 3 +- 2 files changed, 14 insertions(+), 23 deletions(-) diff --git a/run/markdown-preview/editor/test/main.test.js b/run/markdown-preview/editor/test/main.test.js index 3b7bea1871..01f7e40efd 100644 --- a/run/markdown-preview/editor/test/main.test.js +++ b/run/markdown-preview/editor/test/main.test.js @@ -18,27 +18,21 @@ const assert = require('assert'); const path = require('path'); const supertest = require('supertest'); -let request, service, template, htmlString, markdownString, falseString; +let request, template, htmlString, markdownString, falseString; describe('Editor unit tests', () => { describe('Service init', () => { it('should respond with an error for no EDITOR_UPSTREAM_RENDER_URL var', async () => { - const {app, init} = require(path.join(__dirname, '..', 'main')); - request = supertest(app); - service = () => init(); - assert.rejects(service, 'Error') + request = () => require(path.join(__dirname, '..', 'main')); + assert.throws(request, {name: 'Error', message: 'No configuration for upstream render service: add EDITOR_UPSTREAM_RENDER_URL environment variable'}) }); - // Reload the server with updated env vars. - before(async () => { + it('should return an object with an EDITOR_UPSTREAM_RENDER_URL var', async () => { + // Reload the server with updated env vars. process.env.EDITOR_UPSTREAM_RENDER_URL = 'https://www.example.com/'; const {app, init} = require(path.join(__dirname, '..', 'main')); request = supertest(app); - service = () => { return init()}; - }) - - it('should return an object with an EDITOR_UPSTREAM_RENDER_URL var', async () => { - const response = service(); + const response = init(); assert.equal(response.url, process.env.EDITOR_UPSTREAM_RENDER_URL); assert.equal(response.url, 'https://www.example.com/'); assert.equal(response.isAuthenticated, true); @@ -48,9 +42,9 @@ describe('Editor unit tests', () => { describe('Handlebars compiler', async () => { before(async () => { process.env.EDITOR_UPSTREAM_RENDER_URL = 'https://www.example.com/'; - const {app, init, buildTemplate} = require(path.join(__dirname, '..', 'main')); + const {app, buildTemplate} = require(path.join(__dirname, '..', 'main')); request = supertest(app); - template = await buildTemplate(init()); + template = buildTemplate(); }) it('includes HTML from the templates', () => { @@ -78,16 +72,12 @@ describe('Editor unit tests', () => { await request.post('/render').type('json').send('invalid string').expect(400); }); - // Reload the server with updated env vars. - before(async () => { + it('should successfully make a request with valid JSON', async function () { + this.timeout(5000); + // Reload the server with updated env vars. process.env.EDITOR_UPSTREAM_RENDER_URL = 'https://www.example.com/'; - const {app, init} = require(path.join(__dirname, '..', 'main')); + const {app} = require(path.join(__dirname, '..', 'main')); request = supertest(app); - service = init(); - }) - - it('should successfully make a request with valid JSON', async function () { - this.timeout(6000); await request.post('/render').type('json').send({data:"valid string"}).expect(500); }); }); diff --git a/run/markdown-preview/editor/test/render.test.js b/run/markdown-preview/editor/test/render.test.js index 65e1003f26..f6acbed11b 100644 --- a/run/markdown-preview/editor/test/render.test.js +++ b/run/markdown-preview/editor/test/render.test.js @@ -24,11 +24,12 @@ describe('Editor renderRequest unit tests', function () { before(async () => { request = require(path.join(__dirname, '..', 'render')); - service = {url: 'https://www.google.com', isAuthenticated: false}; + service = {url: 'https://www.google.com'}; markdown = "**markdown text**"; }); it('can make an unauthenticated request', async () => { + service.isAuthenticated = false; assert.rejects(request(service, markdown)); }) From 5a1d84ebed343daeb937014140661d6a837b6233 Mon Sep 17 00:00:00 2001 From: kelsk Date: Wed, 6 May 2020 11:55:29 -0700 Subject: [PATCH 45/96] syntax: minor fixes --- run/markdown-preview/editor/test/main.test.js | 5 ++++- run/markdown-preview/editor/test/render.test.js | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/run/markdown-preview/editor/test/main.test.js b/run/markdown-preview/editor/test/main.test.js index 01f7e40efd..4d63a5eb14 100644 --- a/run/markdown-preview/editor/test/main.test.js +++ b/run/markdown-preview/editor/test/main.test.js @@ -24,7 +24,10 @@ describe('Editor unit tests', () => { describe('Service init', () => { it('should respond with an error for no EDITOR_UPSTREAM_RENDER_URL var', async () => { request = () => require(path.join(__dirname, '..', 'main')); - assert.throws(request, {name: 'Error', message: 'No configuration for upstream render service: add EDITOR_UPSTREAM_RENDER_URL environment variable'}) + assert.throws(request, { + name: 'Error', + message: 'No configuration for upstream render service: add EDITOR_UPSTREAM_RENDER_URL environment variable' + }); }); it('should return an object with an EDITOR_UPSTREAM_RENDER_URL var', async () => { diff --git a/run/markdown-preview/editor/test/render.test.js b/run/markdown-preview/editor/test/render.test.js index f6acbed11b..45d2b69f08 100644 --- a/run/markdown-preview/editor/test/render.test.js +++ b/run/markdown-preview/editor/test/render.test.js @@ -20,7 +20,7 @@ const path = require('path'); let request, service, markdown; describe('Editor renderRequest unit tests', function () { - this.timeout(6000); + this.timeout(5000); before(async () => { request = require(path.join(__dirname, '..', 'render')); From 971bff92bb7faf402d43aeddd0d7d65322026680 Mon Sep 17 00:00:00 2001 From: kelsk Date: Wed, 6 May 2020 12:09:59 -0700 Subject: [PATCH 46/96] test: modified for new code --- .../renderer/test/main.test.js | 28 +++++++++---------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/run/markdown-preview/renderer/test/main.test.js b/run/markdown-preview/renderer/test/main.test.js index f0df48fc18..968cce6164 100644 --- a/run/markdown-preview/renderer/test/main.test.js +++ b/run/markdown-preview/renderer/test/main.test.js @@ -26,30 +26,28 @@ describe('Unit Tests', () => { request = supertest(app); }); - it('should return Bad Request with an invalid payload', async () => { + it('should return Bad Request with an invalid type', async () => { await request.post('/').type('json').send('markdown: true').expect(400); }); it('should succeed with a valid request', async () => { - const header = {markdown: { data: "**markdown text**"}}; - await request.post('/').type('json').send(header).expect(200).then(res => { - const body = res.body; - assert.equal(body.data, "

markdown text

\n") + const markdown = "**markdown text**"; + await request.post('/').type('text').send(markdown).expect(200).then(res => { + const body = res.text; + assert.equal(body, "

markdown text

\n") }); }); it('should succeed with a request that includes xss', async () => { - let text = ""; - let header = {markdown: { data: text}}; - await request.post('/').type('json').send(header).expect(200).then(res => { - const body = res.body; - assert.deepStrictEqual(body.data, "

<script>script</script>

\n"); + let markdown = ""; + await request.post('/').type('text').send(markdown).expect(200).then(res => { + const body = res.text; + assert.deepStrictEqual(body, "

<script>script</script>

\n"); }); - text = 'Google' - header = {markdown: { data: text}}; - await request.post('/').type('json').send(header).expect(200).then(res => { - const body = res.body; - assert.deepStrictEqual(body.data, "

<a onblur="alert(secret)" href="http://www.google.com">Google</a>

\n"); + markdown = 'Google' + await request.post('/').type('text').send(markdown).expect(200).then(res => { + const body = res.text; + assert.deepStrictEqual(body, "

<a onblur="alert(secret)" href="http://www.google.com">Google</a>

\n"); }); }) }) From 3c4ed75cc6a3510c7d3b3490b8d5695a7e3d738a Mon Sep 17 00:00:00 2001 From: kelsk Date: Wed, 6 May 2020 12:12:54 -0700 Subject: [PATCH 47/96] added comment about xss --- run/markdown-preview/renderer/main.js | 1 + 1 file changed, 1 insertion(+) diff --git a/run/markdown-preview/renderer/main.js b/run/markdown-preview/renderer/main.js index 06227c7233..06e6b1fe78 100644 --- a/run/markdown-preview/renderer/main.js +++ b/run/markdown-preview/renderer/main.js @@ -30,6 +30,7 @@ app.post('/', (req, res) => { try { // Get the Markdown text and convert it into HTML using markdown-it. + // Markdown-it prohibits some kinds of links making it safe from XSS. const md = new MarkdownIt(); const html = md.render(markdown); res.status(200).send(html); From f4d8a994f2a6adbf9328505f4c46e6210ce71a93 Mon Sep 17 00:00:00 2001 From: kelsk Date: Thu, 7 May 2020 12:28:42 -0700 Subject: [PATCH 48/96] code: unwrapped body.data --- run/markdown-preview/editor/main.js | 4 ++-- run/markdown-preview/editor/render.js | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/run/markdown-preview/editor/main.js b/run/markdown-preview/editor/main.js index 4bf53a82c2..604835222b 100644 --- a/run/markdown-preview/editor/main.js +++ b/run/markdown-preview/editor/main.js @@ -58,9 +58,9 @@ app.get('/', (req, res) => { // The request returns the Markdown text converted to HTML. app.post('/render', async (req, res) => { try { - const markdown = req.body; + const markdown = req.body.data; const response = await renderRequest(service, markdown); - res.status(200).send(response.body); + res.status(200).send(response); } catch (err) { console.log('Error querying the Renderer service: ', err); res.status(500).send(err); diff --git a/run/markdown-preview/editor/render.js b/run/markdown-preview/editor/render.js index 0c45e6f823..2ecb824592 100644 --- a/run/markdown-preview/editor/render.js +++ b/run/markdown-preview/editor/render.js @@ -27,7 +27,7 @@ const renderRequest = async (service, markdown) => { headers: { 'Content-Type': 'text/plain' }, - body: markdown.data + body: markdown }; if (service.isAuthenticated) { @@ -47,7 +47,7 @@ const renderRequest = async (service, markdown) => { try { // serviceRequest converts the Markdown plaintext to HTML. const serviceResponse = await got(service.url, serviceRequestOptions); - return serviceResponse; + return serviceResponse.body; } catch (err) { throw Error('Renderer service could not respond to request ', err); }; From 330206e21d2dfa577f2efb4edeb644c2337b71e7 Mon Sep 17 00:00:00 2001 From: kelsk Date: Thu, 7 May 2020 12:32:34 -0700 Subject: [PATCH 49/96] test: updated tests per code review --- run/markdown-preview/editor/test/main.test.js | 43 +++++++++---------- .../editor/test/render.test.js | 6 ++- .../renderer/test/main.test.js | 2 +- 3 files changed, 26 insertions(+), 25 deletions(-) diff --git a/run/markdown-preview/editor/test/main.test.js b/run/markdown-preview/editor/test/main.test.js index 4d63a5eb14..198359901f 100644 --- a/run/markdown-preview/editor/test/main.test.js +++ b/run/markdown-preview/editor/test/main.test.js @@ -18,13 +18,11 @@ const assert = require('assert'); const path = require('path'); const supertest = require('supertest'); -let request, template, htmlString, markdownString, falseString; - describe('Editor unit tests', () => { describe('Service init', () => { it('should respond with an error for no EDITOR_UPSTREAM_RENDER_URL var', async () => { - request = () => require(path.join(__dirname, '..', 'main')); - assert.throws(request, { + let app = () => require(path.join(__dirname, '..', 'main')); + assert.throws(app, { name: 'Error', message: 'No configuration for upstream render service: add EDITOR_UPSTREAM_RENDER_URL environment variable' }); @@ -33,9 +31,9 @@ describe('Editor unit tests', () => { it('should return an object with an EDITOR_UPSTREAM_RENDER_URL var', async () => { // Reload the server with updated env vars. process.env.EDITOR_UPSTREAM_RENDER_URL = 'https://www.example.com/'; - const {app, init} = require(path.join(__dirname, '..', 'main')); - request = supertest(app); + const {init} = require(path.join(__dirname, '..', 'main')); const response = init(); + // Successfully creates an init object. assert.equal(response.url, process.env.EDITOR_UPSTREAM_RENDER_URL); assert.equal(response.url, 'https://www.example.com/'); assert.equal(response.isAuthenticated, true); @@ -43,45 +41,46 @@ describe('Editor unit tests', () => { }); describe('Handlebars compiler', async () => { + let template; + before(async () => { process.env.EDITOR_UPSTREAM_RENDER_URL = 'https://www.example.com/'; - const {app, buildTemplate} = require(path.join(__dirname, '..', 'main')); - request = supertest(app); + const {buildTemplate} = require(path.join(__dirname, '..', 'main')); template = buildTemplate(); }) it('includes HTML from the templates', () => { - htmlString = template.includes('Markdown Editor'); + let htmlString = template.includes('Markdown Editor'); assert.equal(htmlString, true); }); it('includes Markdown from the templates', () => { - markdownString = template.includes("This UI allows a user to write Markdown text"); + let markdownString = template.includes("This UI allows a user to write Markdown text"); assert.equal(markdownString, true) }); it('accurately checks the template for nonexistant string', () => { - falseString = template.includes("not a string in the template"); + let falseString = template.includes("not a string in the template"); assert.equal(falseString, false); }); }); describe('Render request', () => { + let request; + + before(async () => { + const {app} = require(path.join(__dirname, '..', 'main')); + request = supertest(app); + }); + it('should respond with Not Found for a GET request to the /render endpoint', async () => { await request.get('/render').expect(404); }); - it('should respond with a Bad Request for a request with invalid JSON', async () => { - await request.post('/render').type('json').send('invalid string').expect(400); - }); - - it('should successfully make a request with valid JSON', async function () { - this.timeout(5000); - // Reload the server with updated env vars. - process.env.EDITOR_UPSTREAM_RENDER_URL = 'https://www.example.com/'; - const {app} = require(path.join(__dirname, '..', 'main')); - request = supertest(app); - await request.post('/render').type('json').send({data:"valid string"}).expect(500); + it('should respond with a Bad Request for a request with invalid type', async function () { + this.timeout(9000); + // Request is expecting plain text and will not accept json. + await request.post('/render').type('json').send({body: {"data":"markdown"}}).expect(500); }); }); }); diff --git a/run/markdown-preview/editor/test/render.test.js b/run/markdown-preview/editor/test/render.test.js index 45d2b69f08..2e65776bc6 100644 --- a/run/markdown-preview/editor/test/render.test.js +++ b/run/markdown-preview/editor/test/render.test.js @@ -20,21 +20,23 @@ const path = require('path'); let request, service, markdown; describe('Editor renderRequest unit tests', function () { - this.timeout(5000); + this.timeout(9000); before(async () => { request = require(path.join(__dirname, '..', 'render')); - service = {url: 'https://www.google.com'}; + service = {url: 'https://www.example.com'}; markdown = "**markdown text**"; }); it('can make an unauthenticated request', async () => { service.isAuthenticated = false; + // Request should be rejected with an error if it's not authenticated. assert.rejects(request(service, markdown)); }) it('can make an authenticated request with an invalid url', async () => { service.isAuthenticated = true; + // Request will be rejected if it's authenticated but given an invalid url. assert.rejects(request(service, markdown)); }) }); \ No newline at end of file diff --git a/run/markdown-preview/renderer/test/main.test.js b/run/markdown-preview/renderer/test/main.test.js index 968cce6164..365d661045 100644 --- a/run/markdown-preview/renderer/test/main.test.js +++ b/run/markdown-preview/renderer/test/main.test.js @@ -27,7 +27,7 @@ describe('Unit Tests', () => { }); it('should return Bad Request with an invalid type', async () => { - await request.post('/').type('json').send('markdown: true').expect(400); + await request.post('/').type('json').send({"json": "json"}).expect(400); }); it('should succeed with a valid request', async () => { From 9f239455033987c2061c2b877e1044b98a066b1e Mon Sep 17 00:00:00 2001 From: kelsk Date: Fri, 8 May 2020 09:20:53 -0700 Subject: [PATCH 50/96] code: added timeout to got request --- run/markdown-preview/editor/render.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/run/markdown-preview/editor/render.js b/run/markdown-preview/editor/render.js index 2ecb824592..abf762ca0d 100644 --- a/run/markdown-preview/editor/render.js +++ b/run/markdown-preview/editor/render.js @@ -27,7 +27,8 @@ const renderRequest = async (service, markdown) => { headers: { 'Content-Type': 'text/plain' }, - body: markdown + body: markdown, + timeout: 3000 }; if (service.isAuthenticated) { @@ -49,7 +50,7 @@ const renderRequest = async (service, markdown) => { const serviceResponse = await got(service.url, serviceRequestOptions); return serviceResponse.body; } catch (err) { - throw Error('Renderer service could not respond to request ', err); + throw Error('Renderer service could not respond to request: ', err); }; // [END run_secure_request_do] }; From 8a0aa374a0fd60c645edf804212949d86c6dd8e5 Mon Sep 17 00:00:00 2001 From: kelsk Date: Fri, 8 May 2020 09:37:06 -0700 Subject: [PATCH 51/96] test: updated with sinon & error message reading --- run/markdown-preview/editor/package.json | 1 + run/markdown-preview/editor/test/main.test.js | 11 ++++++++--- .../editor/test/render.test.js | 19 +++++++++++++++---- 3 files changed, 24 insertions(+), 7 deletions(-) diff --git a/run/markdown-preview/editor/package.json b/run/markdown-preview/editor/package.json index a56045ac30..2385334b04 100644 --- a/run/markdown-preview/editor/package.json +++ b/run/markdown-preview/editor/package.json @@ -25,6 +25,7 @@ }, "devDependencies": { "mocha": "^7.1.1", + "sinon": "^9.0.2", "supertest": "^4.0.2" } } diff --git a/run/markdown-preview/editor/test/main.test.js b/run/markdown-preview/editor/test/main.test.js index 198359901f..cd6728805a 100644 --- a/run/markdown-preview/editor/test/main.test.js +++ b/run/markdown-preview/editor/test/main.test.js @@ -16,6 +16,7 @@ const assert = require('assert'); const path = require('path'); +const sinon = require('sinon'); const supertest = require('supertest'); describe('Editor unit tests', () => { @@ -77,10 +78,14 @@ describe('Editor unit tests', () => { await request.get('/render').expect(404); }); - it('should respond with a Bad Request for a request with invalid type', async function () { + it('can make a POST request, with an error thrown by the metadata server', async function () { this.timeout(9000); - // Request is expecting plain text and will not accept json. - await request.post('/render').type('json').send({body: {"data":"markdown"}}).expect(500); + const consoleStub = sinon.stub(console, 'log'); + // Ensure that the expected error is logged. + await request.post('/render').type('json').send({body: {"data":"markdown"}}); + const message = console.log.getCall(0).args[1].message; + assert.equal(message, "Metadata server could not respond to request: "); + consoleStub.restore(); }); }); }); diff --git a/run/markdown-preview/editor/test/render.test.js b/run/markdown-preview/editor/test/render.test.js index 2e65776bc6..2e0aab2158 100644 --- a/run/markdown-preview/editor/test/render.test.js +++ b/run/markdown-preview/editor/test/render.test.js @@ -16,6 +16,7 @@ const assert = require('assert'); const path = require('path'); +const sinon = require('sinon'); let request, service, markdown; @@ -31,12 +32,22 @@ describe('Editor renderRequest unit tests', function () { it('can make an unauthenticated request', async () => { service.isAuthenticated = false; // Request should be rejected with an error if it's not authenticated. - assert.rejects(request(service, markdown)); - }) + assert.rejects(async () => { + await request(service, markdown) + }, { + name: 'Error', + message: 'Renderer service could not respond to request: ' + }); + }); it('can make an authenticated request with an invalid url', async () => { service.isAuthenticated = true; // Request will be rejected if it's authenticated but given an invalid url. - assert.rejects(request(service, markdown)); - }) + assert.rejects(async () => { + await request(service, markdown) + }, { + name: 'Error', + message: 'Metadata server could not respond to request: ' + }); + }); }); \ No newline at end of file From 6bdf2b657e95f931ccfacf31bfb46bb24e34fc3f Mon Sep 17 00:00:00 2001 From: kelsk Date: Fri, 8 May 2020 09:51:01 -0700 Subject: [PATCH 52/96] test: updated with sinon to read console log --- run/markdown-preview/renderer/package.json | 1 + run/markdown-preview/renderer/test/main.test.js | 8 +++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/run/markdown-preview/renderer/package.json b/run/markdown-preview/renderer/package.json index c12c202378..409163ad7f 100644 --- a/run/markdown-preview/renderer/package.json +++ b/run/markdown-preview/renderer/package.json @@ -24,6 +24,7 @@ }, "devDependencies": { "mocha": "^7.1.1", + "sinon": "^9.0.2", "supertest": "^4.0.2" } } diff --git a/run/markdown-preview/renderer/test/main.test.js b/run/markdown-preview/renderer/test/main.test.js index 365d661045..316523069c 100644 --- a/run/markdown-preview/renderer/test/main.test.js +++ b/run/markdown-preview/renderer/test/main.test.js @@ -16,6 +16,7 @@ const assert = require('assert'); const path = require('path'); +const sinon = require('sinon'); const supertest = require('supertest'); let request; @@ -27,7 +28,12 @@ describe('Unit Tests', () => { }); it('should return Bad Request with an invalid type', async () => { - await request.post('/').type('json').send({"json": "json"}).expect(400); + const consoleStub = sinon.stub(console, 'log'); + // Ensure that the expected error is logged. + await request.post('/').type('json').send({"json": "json"}); + const message = console.log.getCall(0).args[1].message; + assert.equal(message, "Input data should be a String"); + consoleStub.restore(); }); it('should succeed with a valid request', async () => { From 42c6eb85a775631479b9a6be9dbcf875e248aa12 Mon Sep 17 00:00:00 2001 From: kelsk Date: Fri, 8 May 2020 15:04:25 -0700 Subject: [PATCH 53/96] reformatted markdown declaration --- run/markdown-preview/renderer/main.js | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/run/markdown-preview/renderer/main.js b/run/markdown-preview/renderer/main.js index 06e6b1fe78..6fcbcf71ca 100644 --- a/run/markdown-preview/renderer/main.js +++ b/run/markdown-preview/renderer/main.js @@ -19,15 +19,13 @@ const app = express(); app.use(express.text()); app.post('/', (req, res) => { - let markdown; if (!req.body) { const msg = 'Markdown data could not be retrieved.'; console.log(msg); - res.status(400).send(`Error: ${msg}`) - } else { - markdown = req.body; + res.status(400).send(`Error: ${msg}`); + return; }; - + const markdown = req.body; try { // Get the Markdown text and convert it into HTML using markdown-it. // Markdown-it prohibits some kinds of links making it safe from XSS. From 675b19fda15712641ad1b948fb639f0113d211bd Mon Sep 17 00:00:00 2001 From: kelsk Date: Fri, 8 May 2020 15:05:25 -0700 Subject: [PATCH 54/96] test: refactor to make valid tests --- run/markdown-preview/editor/test/main.test.js | 1 - .../editor/test/render.test.js | 24 +++++++++---------- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/run/markdown-preview/editor/test/main.test.js b/run/markdown-preview/editor/test/main.test.js index cd6728805a..36ab869d79 100644 --- a/run/markdown-preview/editor/test/main.test.js +++ b/run/markdown-preview/editor/test/main.test.js @@ -36,7 +36,6 @@ describe('Editor unit tests', () => { const response = init(); // Successfully creates an init object. assert.equal(response.url, process.env.EDITOR_UPSTREAM_RENDER_URL); - assert.equal(response.url, 'https://www.example.com/'); assert.equal(response.isAuthenticated, true); }) }); diff --git a/run/markdown-preview/editor/test/render.test.js b/run/markdown-preview/editor/test/render.test.js index 2e0aab2158..259607cf4c 100644 --- a/run/markdown-preview/editor/test/render.test.js +++ b/run/markdown-preview/editor/test/render.test.js @@ -32,22 +32,22 @@ describe('Editor renderRequest unit tests', function () { it('can make an unauthenticated request', async () => { service.isAuthenticated = false; // Request should be rejected with an error if it's not authenticated. - assert.rejects(async () => { - await request(service, markdown) - }, { - name: 'Error', - message: 'Renderer service could not respond to request: ' - }); + try { + const response = await request(service, markdown); + assert.ok(response.body.length > 0, 'Metadata server sent empty value') + } catch (e) { + assert.equal(e.message, 'Renderer service could not respond to request: ') + }; }); it('can make an authenticated request with an invalid url', async () => { service.isAuthenticated = true; // Request will be rejected if it's authenticated but given an invalid url. - assert.rejects(async () => { - await request(service, markdown) - }, { - name: 'Error', - message: 'Metadata server could not respond to request: ' - }); + try { + const response = await request(service, markdown); + assert.ok(response.body.length > 0, 'Metadata server sent empty value') + } catch (e) { + assert.equal(e.message, 'Metadata server could not respond to request: ') + }; }); }); \ No newline at end of file From 3503649d1e61caecba694259ed783fc6c55ef22b Mon Sep 17 00:00:00 2001 From: kelsk Date: Mon, 18 May 2020 22:05:58 -0700 Subject: [PATCH 55/96] Updated sample to use google-auth-library --- run/markdown-preview/README.md | 2 +- run/markdown-preview/editor/main.js | 20 +++++++++--------- run/markdown-preview/editor/package.json | 2 +- run/markdown-preview/editor/render.js | 27 ++++++++++++------------ 4 files changed, 25 insertions(+), 26 deletions(-) diff --git a/run/markdown-preview/README.md b/run/markdown-preview/README.md index 70ec4780d4..ef105a37e5 100644 --- a/run/markdown-preview/README.md +++ b/run/markdown-preview/README.md @@ -17,7 +17,7 @@ Required variables for this sample include: ## Dependencies * **express**: Web server framework. -* **gcp-metadata**: Access Google Cloud Platform metadata server. +* **google-auth-library**: OAuth2.0 library for authentication and authorization. * **got**: Node.js library for HTTP requests. * **handlebars** JavaScript template engine. * **markdown-it**: JavaScript library for parsing and rendering Markdown text. diff --git a/run/markdown-preview/editor/main.js b/run/markdown-preview/editor/main.js index 604835222b..7da195c5f8 100644 --- a/run/markdown-preview/editor/main.js +++ b/run/markdown-preview/editor/main.js @@ -14,13 +14,13 @@ const express = require('express'); const handlebars = require('handlebars'); -const { readFileSync } = require('fs'); +const { readFile } = require('fs').promises; const renderRequest = require('./render.js'); const app = express(); app.use(express.json()); -let url, isAuthenticated, markdownDefault, compiledTemplate, template; +let url, isAuthenticated, markdownDefault, compiledTemplate, renderedHtml = undefined; const init = () => { url = process.env.EDITOR_UPSTREAM_RENDER_URL; @@ -33,12 +33,12 @@ const init = () => { const service = init(); // Load the template files and serve them with the Editor service. -const buildTemplate = () => { +const buildRenderedHtml = async () => { try { - markdownDefault = readFileSync(__dirname + '/templates/markdown.md'); - const indexTemplate = handlebars.compile(readFileSync(__dirname + '/templates/index.html', 'utf8')); - compiledTemplate = indexTemplate({default: markdownDefault}); - return compiledTemplate; + markdownDefault = await readFile(__dirname + '/templates/markdown.md'); + compiledTemplate = handlebars.compile(await readFile(__dirname + '/templates/index.html', 'utf8')); + renderedHtml = compiledTemplate({default: markdownDefault}); + return renderedHtml; } catch(err) { throw Error ('Error loading template: ', err); } @@ -46,8 +46,8 @@ const buildTemplate = () => { app.get('/', (req, res) => { try { - template = buildTemplate(); - res.status(200).send(template); + if (!renderedHtml) renderedHtml = buildRenderedHtml(); + res.status(200).send(renderedHtml); } catch (err) { console.log('Error loading the Editor service: ', err); res.status(500).send(err); @@ -77,5 +77,5 @@ app.listen(PORT, err => { module.exports = { app, init, - buildTemplate + buildRenderedHtml }; \ No newline at end of file diff --git a/run/markdown-preview/editor/package.json b/run/markdown-preview/editor/package.json index 2385334b04..36531430d1 100644 --- a/run/markdown-preview/editor/package.json +++ b/run/markdown-preview/editor/package.json @@ -19,7 +19,7 @@ }, "dependencies": { "express": "^4.17.1", - "gcp-metadata": "^4.0.0", + "google-auth-library": "^6.0.0", "got": "^10.7.0", "handlebars": "^4.7.6" }, diff --git a/run/markdown-preview/editor/render.js b/run/markdown-preview/editor/render.js index abf762ca0d..b1e5585ec4 100644 --- a/run/markdown-preview/editor/render.js +++ b/run/markdown-preview/editor/render.js @@ -12,15 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. -const gcpMetadata = require('gcp-metadata') +const {GoogleAuth} = require('google-auth-library'); const got = require('got'); +const auth = new GoogleAuth(); // renderRequest creates a new HTTP request with IAM ID Token credential. // This token is automatically handled by private Cloud Run (fully managed) and Cloud Functions. const renderRequest = async (service, markdown) => { // [START run_secure_request] - let token; - + const targetAudience = service.url; // Build the request to the Renderer receiving service. const serviceRequestOptions = { method: 'POST', @@ -31,22 +31,21 @@ const renderRequest = async (service, markdown) => { timeout: 3000 }; - if (service.isAuthenticated) { - try { - // Query the token with ?audience as the service URL. - const metadataServerTokenPath = `service-accounts/default/identity?audience=${service.url}`; - // Fetch the token and then add it to the request header. - token = await gcpMetadata.instance(metadataServerTokenPath); - serviceRequestOptions.headers['Authorization'] = 'bearer ' + token; - } catch(err) { - throw Error('Metadata server could not respond to request: ', err); - } + try { + // Create a Google Auth client with the Renderer url as the target audience. + const client = await auth.getIdTokenClient(targetAudience); + // Fetch the client request headers and add them to the service request headers. + // The client request headers include an ID token that authenticates the request. + const clientHeaders = await client.getRequestHeaders(); + serviceRequestOptions.headers['Authorization'] = clientHeaders['Authorization']; + } catch(err) { + throw Error('Metadata server could not respond to request: ', err); }; // [END run_secure_request] // [START run_secure_request_do] try { - // serviceRequest converts the Markdown plaintext to HTML. + // serviceResponse converts the Markdown plaintext to HTML. const serviceResponse = await got(service.url, serviceRequestOptions); return serviceResponse.body; } catch (err) { From 0bbd13329fc8336b657c956ab1b60e232d18895c Mon Sep 17 00:00:00 2001 From: kelsk Date: Mon, 18 May 2020 23:58:02 -0700 Subject: [PATCH 56/96] updated req.body conditional & markdown-it comment --- run/markdown-preview/renderer/main.js | 4 ++-- run/markdown-preview/renderer/package.json | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/run/markdown-preview/renderer/main.js b/run/markdown-preview/renderer/main.js index 6fcbcf71ca..84ceb8df6c 100644 --- a/run/markdown-preview/renderer/main.js +++ b/run/markdown-preview/renderer/main.js @@ -19,7 +19,7 @@ const app = express(); app.use(express.text()); app.post('/', (req, res) => { - if (!req.body) { + if (typeof(req.body) !== 'string') { const msg = 'Markdown data could not be retrieved.'; console.log(msg); res.status(400).send(`Error: ${msg}`); @@ -28,7 +28,7 @@ app.post('/', (req, res) => { const markdown = req.body; try { // Get the Markdown text and convert it into HTML using markdown-it. - // Markdown-it prohibits some kinds of links making it safe from XSS. + // Info about XSS prevention in markdown-it can be found here: https://github.com/markdown-it/markdown-it/blob/master/docs/security.md const md = new MarkdownIt(); const md = new MarkdownIt(); const html = md.render(markdown); res.status(200).send(html); diff --git a/run/markdown-preview/renderer/package.json b/run/markdown-preview/renderer/package.json index 409163ad7f..4ce7d1fa8e 100644 --- a/run/markdown-preview/renderer/package.json +++ b/run/markdown-preview/renderer/package.json @@ -19,7 +19,6 @@ }, "dependencies": { "express": "^4.17.1", - "got": "^10.7.0", "markdown-it": "^10.0.0" }, "devDependencies": { From d5fff1662d50a35c5aa57f094ba3e0fba0c2a1cb Mon Sep 17 00:00:00 2001 From: kelsk Date: Mon, 18 May 2020 23:59:34 -0700 Subject: [PATCH 57/96] Removed init(); added Renderer url to /render --- run/markdown-preview/editor/main.js | 15 ++------------- run/markdown-preview/editor/render.js | 10 +++++----- 2 files changed, 7 insertions(+), 18 deletions(-) diff --git a/run/markdown-preview/editor/main.js b/run/markdown-preview/editor/main.js index 7da195c5f8..3bf94b508a 100644 --- a/run/markdown-preview/editor/main.js +++ b/run/markdown-preview/editor/main.js @@ -20,17 +20,7 @@ const renderRequest = require('./render.js'); const app = express(); app.use(express.json()); -let url, isAuthenticated, markdownDefault, compiledTemplate, renderedHtml = undefined; - -const init = () => { - url = process.env.EDITOR_UPSTREAM_RENDER_URL; - if (!url) throw Error ("No configuration for upstream render service: add EDITOR_UPSTREAM_RENDER_URL environment variable"); - isAuthenticated = !process.env.EDITOR_UPSTREAM_UNAUTHENTICATED; - if (!isAuthenticated) console.log("Editor: starting in unauthenticated upstream mode"); - return {url, isAuthenticated}; -}; - -const service = init(); +let markdownDefault, compiledTemplate, renderedHtml; // Load the template files and serve them with the Editor service. const buildRenderedHtml = async () => { @@ -59,7 +49,7 @@ app.get('/', (req, res) => { app.post('/render', async (req, res) => { try { const markdown = req.body.data; - const response = await renderRequest(service, markdown); + const response = await renderRequest(markdown); res.status(200).send(response); } catch (err) { console.log('Error querying the Renderer service: ', err); @@ -76,6 +66,5 @@ app.listen(PORT, err => { // Exports for testing purposes. module.exports = { app, - init, buildRenderedHtml }; \ No newline at end of file diff --git a/run/markdown-preview/editor/render.js b/run/markdown-preview/editor/render.js index b1e5585ec4..7844af0a46 100644 --- a/run/markdown-preview/editor/render.js +++ b/run/markdown-preview/editor/render.js @@ -18,9 +18,9 @@ const auth = new GoogleAuth(); // renderRequest creates a new HTTP request with IAM ID Token credential. // This token is automatically handled by private Cloud Run (fully managed) and Cloud Functions. -const renderRequest = async (service, markdown) => { +const renderRequest = async (markdown) => { // [START run_secure_request] - const targetAudience = service.url; + const serviceUrl = process.env.EDITOR_UPSTREAM_RENDER_URL; // Build the request to the Renderer receiving service. const serviceRequestOptions = { method: 'POST', @@ -32,8 +32,8 @@ const renderRequest = async (service, markdown) => { }; try { - // Create a Google Auth client with the Renderer url as the target audience. - const client = await auth.getIdTokenClient(targetAudience); + // Create a Google Auth client with the Renderer service url as the target audience. + const client = await auth.getIdTokenClient(serviceUrl); // Fetch the client request headers and add them to the service request headers. // The client request headers include an ID token that authenticates the request. const clientHeaders = await client.getRequestHeaders(); @@ -46,7 +46,7 @@ const renderRequest = async (service, markdown) => { // [START run_secure_request_do] try { // serviceResponse converts the Markdown plaintext to HTML. - const serviceResponse = await got(service.url, serviceRequestOptions); + const serviceResponse = await got(serviceUrl, serviceRequestOptions); return serviceResponse.body; } catch (err) { throw Error('Renderer service could not respond to request: ', err); From 5a7a2ec8606a0e74b15fd7f24374614b40aade17 Mon Sep 17 00:00:00 2001 From: kelsk Date: Tue, 19 May 2020 09:15:11 -0700 Subject: [PATCH 58/96] Updated renderRequest client --- run/markdown-preview/editor/render.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/run/markdown-preview/editor/render.js b/run/markdown-preview/editor/render.js index 7844af0a46..a26ff5f5a6 100644 --- a/run/markdown-preview/editor/render.js +++ b/run/markdown-preview/editor/render.js @@ -16,6 +16,8 @@ const {GoogleAuth} = require('google-auth-library'); const got = require('got'); const auth = new GoogleAuth(); +let client; + // renderRequest creates a new HTTP request with IAM ID Token credential. // This token is automatically handled by private Cloud Run (fully managed) and Cloud Functions. const renderRequest = async (markdown) => { @@ -33,7 +35,7 @@ const renderRequest = async (markdown) => { try { // Create a Google Auth client with the Renderer service url as the target audience. - const client = await auth.getIdTokenClient(serviceUrl); + if (!client) client = await auth.getIdTokenClient(serviceUrl); // Fetch the client request headers and add them to the service request headers. // The client request headers include an ID token that authenticates the request. const clientHeaders = await client.getRequestHeaders(); From f369cccecb0fd7c9ca8d7a054c634a2f06d792af Mon Sep 17 00:00:00 2001 From: kelsk Date: Tue, 19 May 2020 09:34:19 -0700 Subject: [PATCH 59/96] Updated get to be async --- run/markdown-preview/editor/main.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/run/markdown-preview/editor/main.js b/run/markdown-preview/editor/main.js index 3bf94b508a..589dce123f 100644 --- a/run/markdown-preview/editor/main.js +++ b/run/markdown-preview/editor/main.js @@ -34,9 +34,9 @@ const buildRenderedHtml = async () => { } }; -app.get('/', (req, res) => { +app.get('/', async (req, res) => { try { - if (!renderedHtml) renderedHtml = buildRenderedHtml(); + if (!renderedHtml) renderedHtml = await buildRenderedHtml(); res.status(200).send(renderedHtml); } catch (err) { console.log('Error loading the Editor service: ', err); From 394dc63411a2ae8e41d56ebd503789a16e5a4011 Mon Sep 17 00:00:00 2001 From: kelsk Date: Tue, 19 May 2020 10:05:24 -0700 Subject: [PATCH 60/96] Updated tests to reflect new code --- run/markdown-preview/editor/render.js | 2 +- run/markdown-preview/editor/test/main.test.js | 32 ++++++------------- .../editor/test/render.test.js | 23 +++---------- .../renderer/test/main.test.js | 4 +-- 4 files changed, 18 insertions(+), 43 deletions(-) diff --git a/run/markdown-preview/editor/render.js b/run/markdown-preview/editor/render.js index a26ff5f5a6..5b0a214775 100644 --- a/run/markdown-preview/editor/render.js +++ b/run/markdown-preview/editor/render.js @@ -41,7 +41,7 @@ const renderRequest = async (markdown) => { const clientHeaders = await client.getRequestHeaders(); serviceRequestOptions.headers['Authorization'] = clientHeaders['Authorization']; } catch(err) { - throw Error('Metadata server could not respond to request: ', err); + throw Error('GoogleAuth server could not respond to request: ', err); }; // [END run_secure_request] diff --git a/run/markdown-preview/editor/test/main.test.js b/run/markdown-preview/editor/test/main.test.js index 36ab869d79..3ce4a29ee1 100644 --- a/run/markdown-preview/editor/test/main.test.js +++ b/run/markdown-preview/editor/test/main.test.js @@ -20,33 +20,21 @@ const sinon = require('sinon'); const supertest = require('supertest'); describe('Editor unit tests', () => { - describe('Service init', () => { - it('should respond with an error for no EDITOR_UPSTREAM_RENDER_URL var', async () => { - let app = () => require(path.join(__dirname, '..', 'main')); - assert.throws(app, { - name: 'Error', - message: 'No configuration for upstream render service: add EDITOR_UPSTREAM_RENDER_URL environment variable' - }); - }); - - it('should return an object with an EDITOR_UPSTREAM_RENDER_URL var', async () => { - // Reload the server with updated env vars. - process.env.EDITOR_UPSTREAM_RENDER_URL = 'https://www.example.com/'; - const {init} = require(path.join(__dirname, '..', 'main')); - const response = init(); - // Successfully creates an init object. - assert.equal(response.url, process.env.EDITOR_UPSTREAM_RENDER_URL); - assert.equal(response.isAuthenticated, true); + describe('Initialize app', () => { + it('should successfully start the app', async function () { + const {app} = require(path.join(__dirname, '..', 'main')); + const request = supertest(app); + await request.get('/').expect(200); }) }); describe('Handlebars compiler', async () => { let template; - before(async () => { + before( async () => { process.env.EDITOR_UPSTREAM_RENDER_URL = 'https://www.example.com/'; - const {buildTemplate} = require(path.join(__dirname, '..', 'main')); - template = buildTemplate(); + const {buildRenderedHtml} = require(path.join(__dirname, '..', 'main')); + template = await buildRenderedHtml(); }) it('includes HTML from the templates', () => { @@ -77,13 +65,13 @@ describe('Editor unit tests', () => { await request.get('/render').expect(404); }); - it('can make a POST request, with an error thrown by the metadata server', async function () { + it('can make a POST request, with an error thrown by the Google Auth server', async function () { this.timeout(9000); const consoleStub = sinon.stub(console, 'log'); // Ensure that the expected error is logged. await request.post('/render').type('json').send({body: {"data":"markdown"}}); const message = console.log.getCall(0).args[1].message; - assert.equal(message, "Metadata server could not respond to request: "); + assert.equal(message, "GoogleAuth server could not respond to request: "); consoleStub.restore(); }); }); diff --git a/run/markdown-preview/editor/test/render.test.js b/run/markdown-preview/editor/test/render.test.js index 259607cf4c..de12b9943a 100644 --- a/run/markdown-preview/editor/test/render.test.js +++ b/run/markdown-preview/editor/test/render.test.js @@ -16,7 +16,6 @@ const assert = require('assert'); const path = require('path'); -const sinon = require('sinon'); let request, service, markdown; @@ -29,25 +28,13 @@ describe('Editor renderRequest unit tests', function () { markdown = "**markdown text**"; }); - it('can make an unauthenticated request', async () => { - service.isAuthenticated = false; - // Request should be rejected with an error if it's not authenticated. + it('can make a request with an invalid url', async () => { + // Request will be rejected if it's given an invalid url. try { - const response = await request(service, markdown); - assert.ok(response.body.length > 0, 'Metadata server sent empty value') + const response = await request(markdown); + assert.ok(response.body.length > 0, 'ID token client sent empty value') } catch (e) { - assert.equal(e.message, 'Renderer service could not respond to request: ') - }; - }); - - it('can make an authenticated request with an invalid url', async () => { - service.isAuthenticated = true; - // Request will be rejected if it's authenticated but given an invalid url. - try { - const response = await request(service, markdown); - assert.ok(response.body.length > 0, 'Metadata server sent empty value') - } catch (e) { - assert.equal(e.message, 'Metadata server could not respond to request: ') + assert.equal(e.message, 'GoogleAuth server could not respond to request: ') }; }); }); \ No newline at end of file diff --git a/run/markdown-preview/renderer/test/main.test.js b/run/markdown-preview/renderer/test/main.test.js index 316523069c..10e440a65a 100644 --- a/run/markdown-preview/renderer/test/main.test.js +++ b/run/markdown-preview/renderer/test/main.test.js @@ -31,8 +31,8 @@ describe('Unit Tests', () => { const consoleStub = sinon.stub(console, 'log'); // Ensure that the expected error is logged. await request.post('/').type('json').send({"json": "json"}); - const message = console.log.getCall(0).args[1].message; - assert.equal(message, "Input data should be a String"); + const message = console.log.getCall(0).args[0]; + assert.equal(message, "Markdown data could not be retrieved."); consoleStub.restore(); }); From 618b88ee41c488a8236cd4a411f418391a827e3b Mon Sep 17 00:00:00 2001 From: kelsk Date: Tue, 19 May 2020 10:12:18 -0700 Subject: [PATCH 61/96] Removed erroneous comment & outdated env var --- run/markdown-preview/README.md | 1 - run/markdown-preview/renderer/main.js | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/run/markdown-preview/README.md b/run/markdown-preview/README.md index ef105a37e5..c72e0e3b08 100644 --- a/run/markdown-preview/README.md +++ b/run/markdown-preview/README.md @@ -12,7 +12,6 @@ Required variables for this sample include: * `EDITOR_UPSTREAM_RENDER_URL`: The URL of the restricted Cloud Run service that renders Markdown to HTML. -* `EDITOR_UPSTREAM_UNAUTHENTICATED`: (Optional) A boolean that indicates whether the render service requires an authenticated request. ## Dependencies diff --git a/run/markdown-preview/renderer/main.js b/run/markdown-preview/renderer/main.js index 84ceb8df6c..d2ddbdcddd 100644 --- a/run/markdown-preview/renderer/main.js +++ b/run/markdown-preview/renderer/main.js @@ -28,7 +28,7 @@ app.post('/', (req, res) => { const markdown = req.body; try { // Get the Markdown text and convert it into HTML using markdown-it. - // Info about XSS prevention in markdown-it can be found here: https://github.com/markdown-it/markdown-it/blob/master/docs/security.md const md = new MarkdownIt(); + // Info about XSS prevention in markdown-it can be found here: https://github.com/markdown-it/markdown-it/blob/master/docs/security.md const md = new MarkdownIt(); const html = md.render(markdown); res.status(200).send(html); From a7bfaae748d1ec8d2ba3a0603fdad84e3c9e93b4 Mon Sep 17 00:00:00 2001 From: kelsk Date: Tue, 19 May 2020 10:14:03 -0700 Subject: [PATCH 62/96] Removed unused js package --- run/markdown-preview/README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/run/markdown-preview/README.md b/run/markdown-preview/README.md index c72e0e3b08..5f714ffe3a 100644 --- a/run/markdown-preview/README.md +++ b/run/markdown-preview/README.md @@ -12,7 +12,6 @@ Required variables for this sample include: * `EDITOR_UPSTREAM_RENDER_URL`: The URL of the restricted Cloud Run service that renders Markdown to HTML. - ## Dependencies * **express**: Web server framework. @@ -20,4 +19,3 @@ Required variables for this sample include: * **got**: Node.js library for HTTP requests. * **handlebars** JavaScript template engine. * **markdown-it**: JavaScript library for parsing and rendering Markdown text. -* **xss**: Node.js HTML sanitizer. From 2b2844ce0347607c62912cc288c8dd830b545a79 Mon Sep 17 00:00:00 2001 From: kelsk Date: Tue, 19 May 2020 10:38:26 -0700 Subject: [PATCH 63/96] Added check for service url --- run/markdown-preview/editor/render.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/run/markdown-preview/editor/render.js b/run/markdown-preview/editor/render.js index 5b0a214775..f28549efb3 100644 --- a/run/markdown-preview/editor/render.js +++ b/run/markdown-preview/editor/render.js @@ -16,13 +16,13 @@ const {GoogleAuth} = require('google-auth-library'); const got = require('got'); const auth = new GoogleAuth(); -let client; +let client, serviceUrl; // renderRequest creates a new HTTP request with IAM ID Token credential. // This token is automatically handled by private Cloud Run (fully managed) and Cloud Functions. const renderRequest = async (markdown) => { // [START run_secure_request] - const serviceUrl = process.env.EDITOR_UPSTREAM_RENDER_URL; + if (process.env.EDITOR_UPSTREAM_RENDER_URL) serviceUrl = process.env.EDITOR_UPSTREAM_RENDER_URL; // Build the request to the Renderer receiving service. const serviceRequestOptions = { method: 'POST', From eb6bbf1a277deafe6ab67d8b0d8d5b26ccaedb02 Mon Sep 17 00:00:00 2001 From: kelsk Date: Tue, 19 May 2020 11:23:13 -0700 Subject: [PATCH 64/96] Updated service url in test/render --- run/markdown-preview/editor/test/render.test.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/run/markdown-preview/editor/test/render.test.js b/run/markdown-preview/editor/test/render.test.js index de12b9943a..efca223751 100644 --- a/run/markdown-preview/editor/test/render.test.js +++ b/run/markdown-preview/editor/test/render.test.js @@ -17,14 +17,14 @@ const assert = require('assert'); const path = require('path'); -let request, service, markdown; +let request, markdown; describe('Editor renderRequest unit tests', function () { this.timeout(9000); before(async () => { request = require(path.join(__dirname, '..', 'render')); - service = {url: 'https://www.example.com'}; + process.env.EDITOR_UPSTREAM_RENDER_URL = 'https://www.example.com/'; markdown = "**markdown text**"; }); @@ -32,7 +32,7 @@ describe('Editor renderRequest unit tests', function () { // Request will be rejected if it's given an invalid url. try { const response = await request(markdown); - assert.ok(response.body.length > 0, 'ID token client sent empty value') + if (response) assert.ok(response.body.length > 0, 'ID token client sent empty value') } catch (e) { assert.equal(e.message, 'GoogleAuth server could not respond to request: ') }; From a2ca8b5a86624a2945c3c5084a79cc20bc8318ac Mon Sep 17 00:00:00 2001 From: kelsk Date: Tue, 19 May 2020 11:34:13 -0700 Subject: [PATCH 65/96] Updated tests with urls & removed incorrect assertion --- run/markdown-preview/editor/test/main.test.js | 1 + run/markdown-preview/editor/test/render.test.js | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/run/markdown-preview/editor/test/main.test.js b/run/markdown-preview/editor/test/main.test.js index 3ce4a29ee1..a430b1ded1 100644 --- a/run/markdown-preview/editor/test/main.test.js +++ b/run/markdown-preview/editor/test/main.test.js @@ -57,6 +57,7 @@ describe('Editor unit tests', () => { let request; before(async () => { + process.env.EDITOR_UPSTREAM_RENDER_URL = 'https://www.example.com/'; const {app} = require(path.join(__dirname, '..', 'main')); request = supertest(app); }); diff --git a/run/markdown-preview/editor/test/render.test.js b/run/markdown-preview/editor/test/render.test.js index efca223751..6eb2a25bea 100644 --- a/run/markdown-preview/editor/test/render.test.js +++ b/run/markdown-preview/editor/test/render.test.js @@ -32,7 +32,7 @@ describe('Editor renderRequest unit tests', function () { // Request will be rejected if it's given an invalid url. try { const response = await request(markdown); - if (response) assert.ok(response.body.length > 0, 'ID token client sent empty value') + assert.ok(!response.body, 'ID token client sent empty value') } catch (e) { assert.equal(e.message, 'GoogleAuth server could not respond to request: ') }; From 13fb1a05bbaf17f8a046961ce18331d9c149db3f Mon Sep 17 00:00:00 2001 From: kelsk Date: Tue, 19 May 2020 12:19:45 -0700 Subject: [PATCH 66/96] updated POST request test --- run/markdown-preview/editor/test/main.test.js | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/run/markdown-preview/editor/test/main.test.js b/run/markdown-preview/editor/test/main.test.js index a430b1ded1..ada3e21ff9 100644 --- a/run/markdown-preview/editor/test/main.test.js +++ b/run/markdown-preview/editor/test/main.test.js @@ -16,7 +16,6 @@ const assert = require('assert'); const path = require('path'); -const sinon = require('sinon'); const supertest = require('supertest'); describe('Editor unit tests', () => { @@ -66,14 +65,18 @@ describe('Editor unit tests', () => { await request.get('/render').expect(404); }); - it('can make a POST request, with an error thrown by the Google Auth server', async function () { + it('can make a POST request, with an error thrown if data type is incorrect', async function () { this.timeout(9000); - const consoleStub = sinon.stub(console, 'log'); // Ensure that the expected error is logged. - await request.post('/render').type('json').send({body: {"data":"markdown"}}); - const message = console.log.getCall(0).args[1].message; - assert.equal(message, "GoogleAuth server could not respond to request: "); - consoleStub.restore(); + let response = await request.post('/render').type('json').send({"data":"markdown"}).expect(500); + assert.equal(response.error.message, 'cannot POST /render (500)'); + + try { + await request.post('/render').type('text/plain').send({"data":"markdown"}).expect(500); + } catch (e) { + const message = e.message.includes('The "string" argument must be of type string'); + assert.equal(message, true); + }; }); }); }); From 3084e6f9fdbffa10773f3722a899c923e20b6514 Mon Sep 17 00:00:00 2001 From: kelsk Date: Tue, 19 May 2020 12:51:06 -0700 Subject: [PATCH 67/96] updated POST request test --- run/markdown-preview/editor/test/main.test.js | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/run/markdown-preview/editor/test/main.test.js b/run/markdown-preview/editor/test/main.test.js index ada3e21ff9..f25e2b468a 100644 --- a/run/markdown-preview/editor/test/main.test.js +++ b/run/markdown-preview/editor/test/main.test.js @@ -67,16 +67,10 @@ describe('Editor unit tests', () => { it('can make a POST request, with an error thrown if data type is incorrect', async function () { this.timeout(9000); - // Ensure that the expected error is logged. - let response = await request.post('/render').type('json').send({"data":"markdown"}).expect(500); - assert.equal(response.error.message, 'cannot POST /render (500)'); - - try { - await request.post('/render').type('text/plain').send({"data":"markdown"}).expect(500); - } catch (e) { - const message = e.message.includes('The "string" argument must be of type string'); - assert.equal(message, true); - }; + // A valid type will make a request to the /render endpoint. + await request.post('/render').type('json').send({"data":"markdown"}).expect(500); + // An incorrect type will not successfully make a request and will print an error in the console. + await request.post('/render').type('json').send('string: incorrect data type').expect(400); }); }); }); From 829039b48b114ce8b057a7aec793c370731c9036 Mon Sep 17 00:00:00 2001 From: kelsk Date: Tue, 19 May 2020 12:57:24 -0700 Subject: [PATCH 68/96] removed 'then' from renderer test --- run/markdown-preview/renderer/test/main.test.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/run/markdown-preview/renderer/test/main.test.js b/run/markdown-preview/renderer/test/main.test.js index 10e440a65a..417a697ae2 100644 --- a/run/markdown-preview/renderer/test/main.test.js +++ b/run/markdown-preview/renderer/test/main.test.js @@ -38,10 +38,9 @@ describe('Unit Tests', () => { it('should succeed with a valid request', async () => { const markdown = "**markdown text**"; - await request.post('/').type('text').send(markdown).expect(200).then(res => { - const body = res.text; - assert.equal(body, "

markdown text

\n") - }); + const response = await request.post('/').type('text').send(markdown).expect(200); + const body = response.text; + assert.equal(body, "

markdown text

\n") }); it('should succeed with a request that includes xss', async () => { From b7a7eaa3379e59ba66ca84ca2e519426be22e3ba Mon Sep 17 00:00:00 2001 From: kelsk Date: Tue, 19 May 2020 14:37:12 -0700 Subject: [PATCH 69/96] test modifications --- run/markdown-preview/editor/test/main.test.js | 2 +- run/markdown-preview/editor/test/render.test.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/run/markdown-preview/editor/test/main.test.js b/run/markdown-preview/editor/test/main.test.js index f25e2b468a..1d3ef4d1ff 100644 --- a/run/markdown-preview/editor/test/main.test.js +++ b/run/markdown-preview/editor/test/main.test.js @@ -68,7 +68,7 @@ describe('Editor unit tests', () => { it('can make a POST request, with an error thrown if data type is incorrect', async function () { this.timeout(9000); // A valid type will make a request to the /render endpoint. - await request.post('/render').type('json').send({"data":"markdown"}).expect(500); + await request.post('/render').type('json').send({"data":"markdown"}).expect(200); // An incorrect type will not successfully make a request and will print an error in the console. await request.post('/render').type('json').send('string: incorrect data type').expect(400); }); diff --git a/run/markdown-preview/editor/test/render.test.js b/run/markdown-preview/editor/test/render.test.js index 6eb2a25bea..246c2fd4a5 100644 --- a/run/markdown-preview/editor/test/render.test.js +++ b/run/markdown-preview/editor/test/render.test.js @@ -34,7 +34,7 @@ describe('Editor renderRequest unit tests', function () { const response = await request(markdown); assert.ok(!response.body, 'ID token client sent empty value') } catch (e) { - assert.equal(e.message, 'GoogleAuth server could not respond to request: ') + assert.equal(e.message, 'Renderer service could not respond to request: ') }; }); }); \ No newline at end of file From 0a6e84c276566a69dfb82fb4ddfa0cda1d8adb4d Mon Sep 17 00:00:00 2001 From: kelsk Date: Tue, 19 May 2020 16:53:47 -0700 Subject: [PATCH 70/96] Reconfigured serviceUrl --- run/markdown-preview/editor/render.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/run/markdown-preview/editor/render.js b/run/markdown-preview/editor/render.js index f28549efb3..d17256d3af 100644 --- a/run/markdown-preview/editor/render.js +++ b/run/markdown-preview/editor/render.js @@ -22,7 +22,9 @@ let client, serviceUrl; // This token is automatically handled by private Cloud Run (fully managed) and Cloud Functions. const renderRequest = async (markdown) => { // [START run_secure_request] - if (process.env.EDITOR_UPSTREAM_RENDER_URL) serviceUrl = process.env.EDITOR_UPSTREAM_RENDER_URL; + if (!process.env.EDITOR_UPSTREAM_RENDER_URL) throw Error('EDITOR_UPSTREAM_RENDER_URL needs to be set.'); + serviceUrl = process.env.EDITOR_UPSTREAM_RENDER_URL; + // Build the request to the Renderer receiving service. const serviceRequestOptions = { method: 'POST', From 719b304730a7f237f54f1e9fc688e26df167cceb Mon Sep 17 00:00:00 2001 From: kelsk Date: Thu, 21 May 2020 10:53:51 -0700 Subject: [PATCH 71/96] Removed sinon --- run/markdown-preview/editor/package.json | 1 - run/markdown-preview/renderer/package.json | 1 - 2 files changed, 2 deletions(-) diff --git a/run/markdown-preview/editor/package.json b/run/markdown-preview/editor/package.json index 36531430d1..bb8982bfda 100644 --- a/run/markdown-preview/editor/package.json +++ b/run/markdown-preview/editor/package.json @@ -25,7 +25,6 @@ }, "devDependencies": { "mocha": "^7.1.1", - "sinon": "^9.0.2", "supertest": "^4.0.2" } } diff --git a/run/markdown-preview/renderer/package.json b/run/markdown-preview/renderer/package.json index 4ce7d1fa8e..cb2ac554c6 100644 --- a/run/markdown-preview/renderer/package.json +++ b/run/markdown-preview/renderer/package.json @@ -23,7 +23,6 @@ }, "devDependencies": { "mocha": "^7.1.1", - "sinon": "^9.0.2", "supertest": "^4.0.2" } } From 5cfb4e6d2ecec886ceac4846c530bab40c8d0c08 Mon Sep 17 00:00:00 2001 From: kelsk Date: Thu, 21 May 2020 10:54:07 -0700 Subject: [PATCH 72/96] Updated region tags --- run/markdown-preview/editor/render.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/run/markdown-preview/editor/render.js b/run/markdown-preview/editor/render.js index d17256d3af..94d034528e 100644 --- a/run/markdown-preview/editor/render.js +++ b/run/markdown-preview/editor/render.js @@ -24,7 +24,7 @@ const renderRequest = async (markdown) => { // [START run_secure_request] if (!process.env.EDITOR_UPSTREAM_RENDER_URL) throw Error('EDITOR_UPSTREAM_RENDER_URL needs to be set.'); serviceUrl = process.env.EDITOR_UPSTREAM_RENDER_URL; - + // Build the request to the Renderer receiving service. const serviceRequestOptions = { method: 'POST', @@ -45,9 +45,7 @@ const renderRequest = async (markdown) => { } catch(err) { throw Error('GoogleAuth server could not respond to request: ', err); }; - // [END run_secure_request] - // [START run_secure_request_do] try { // serviceResponse converts the Markdown plaintext to HTML. const serviceResponse = await got(serviceUrl, serviceRequestOptions); @@ -55,7 +53,7 @@ const renderRequest = async (markdown) => { } catch (err) { throw Error('Renderer service could not respond to request: ', err); }; - // [END run_secure_request_do] + // [END run_secure_request] }; module.exports = renderRequest; \ No newline at end of file From 59e9e137c5a080e33d569be1d6c0d24a405776f0 Mon Sep 17 00:00:00 2001 From: kelsk Date: Thu, 21 May 2020 12:26:05 -0700 Subject: [PATCH 73/96] Updated valid request test --- run/markdown-preview/editor/test/render.test.js | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/run/markdown-preview/editor/test/render.test.js b/run/markdown-preview/editor/test/render.test.js index 246c2fd4a5..3c76a34cd0 100644 --- a/run/markdown-preview/editor/test/render.test.js +++ b/run/markdown-preview/editor/test/render.test.js @@ -17,24 +17,21 @@ const assert = require('assert'); const path = require('path'); -let request, markdown; +let renderRequest, markdown; describe('Editor renderRequest unit tests', function () { this.timeout(9000); before(async () => { - request = require(path.join(__dirname, '..', 'render')); + renderRequest = require(path.join(__dirname, '..', 'render')); process.env.EDITOR_UPSTREAM_RENDER_URL = 'https://www.example.com/'; markdown = "**markdown text**"; }); - it('can make a request with an invalid url', async () => { - // Request will be rejected if it's given an invalid url. - try { - const response = await request(markdown); - assert.ok(!response.body, 'ID token client sent empty value') - } catch (e) { - assert.equal(e.message, 'Renderer service could not respond to request: ') - }; + it('can make a request with an valid url', async () => { + // Request will be successful and return the converted markdown as a string. + const response = await renderRequest(markdown); + let exampleString = response.includes('This domain is for use in illustrative examples in documents.'); + assert.equal(exampleString, true); }); }); \ No newline at end of file From 77d997cd401f5512ed7c3ebd7b969a8b40bf1a8d Mon Sep 17 00:00:00 2001 From: kelsk Date: Wed, 27 May 2020 16:42:31 -0700 Subject: [PATCH 74/96] Update: node 10 => node 12 --- run/markdown-preview/editor/Dockerfile | 2 +- run/markdown-preview/editor/package.json | 2 +- run/markdown-preview/renderer/Dockerfile | 2 +- run/markdown-preview/renderer/package.json | 3 +-- 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/run/markdown-preview/editor/Dockerfile b/run/markdown-preview/editor/Dockerfile index 143b8248b6..af09d47614 100644 --- a/run/markdown-preview/editor/Dockerfile +++ b/run/markdown-preview/editor/Dockerfile @@ -14,7 +14,7 @@ # Use the official lightweight Node.js 10 image. # https://hub.docker.com/_/node -FROM node:10-slim +FROM node:12-slim # Create and change to the app directory. WORKDIR /usr/src/app diff --git a/run/markdown-preview/editor/package.json b/run/markdown-preview/editor/package.json index bb8982bfda..3f3a3eee54 100644 --- a/run/markdown-preview/editor/package.json +++ b/run/markdown-preview/editor/package.json @@ -10,7 +10,7 @@ "url": "https://github.com/GoogleCloudPlatform/nodejs-docs-samples.git" }, "engines": { - "node": ">= 10.0.0" + "node": ">= 12.0.0" }, "main": "main.js", "scripts": { diff --git a/run/markdown-preview/renderer/Dockerfile b/run/markdown-preview/renderer/Dockerfile index 143b8248b6..af09d47614 100644 --- a/run/markdown-preview/renderer/Dockerfile +++ b/run/markdown-preview/renderer/Dockerfile @@ -14,7 +14,7 @@ # Use the official lightweight Node.js 10 image. # https://hub.docker.com/_/node -FROM node:10-slim +FROM node:12-slim # Create and change to the app directory. WORKDIR /usr/src/app diff --git a/run/markdown-preview/renderer/package.json b/run/markdown-preview/renderer/package.json index cb2ac554c6..ca9a65e213 100644 --- a/run/markdown-preview/renderer/package.json +++ b/run/markdown-preview/renderer/package.json @@ -1,7 +1,6 @@ { "name": "markdown-preview-renderer", "description": "Cloud Run service to demonstrate service-to-service authentication, paired with Editor service.", - "version": "0.0.1", "private": true, "license": "Apache-2.0", "author": "Google LLC", @@ -10,7 +9,7 @@ "url": "https://github.com/GoogleCloudPlatform/nodejs-docs-samples.git" }, "engines": { - "node": ">= 10.0.0" + "node": ">= 12.0.0" }, "main": "main.js", "scripts": { From 9f9e9bdd4d935546c95eecd242d2464e931bb007 Mon Sep 17 00:00:00 2001 From: kelsk Date: Thu, 4 Jun 2020 08:27:54 -0700 Subject: [PATCH 75/96] Added end to end tests --- run/markdown-preview/editor/package.json | 1 + run/markdown-preview/editor/test/deploy.sh | 38 +++++++++++ run/markdown-preview/editor/test/runner.sh | 63 +++++++++++++++++++ run/markdown-preview/editor/test/service.sh | 43 +++++++++++++ .../editor/test/system.test.js | 48 ++++++++++++++ run/markdown-preview/editor/test/url.sh | 30 +++++++++ 6 files changed, 223 insertions(+) create mode 100644 run/markdown-preview/editor/test/deploy.sh create mode 100644 run/markdown-preview/editor/test/runner.sh create mode 100644 run/markdown-preview/editor/test/service.sh create mode 100644 run/markdown-preview/editor/test/system.test.js create mode 100644 run/markdown-preview/editor/test/url.sh diff --git a/run/markdown-preview/editor/package.json b/run/markdown-preview/editor/package.json index 3f3a3eee54..608f36a64c 100644 --- a/run/markdown-preview/editor/package.json +++ b/run/markdown-preview/editor/package.json @@ -14,6 +14,7 @@ }, "main": "main.js", "scripts": { + "e2e-test": "test/runner.sh mocha test/system.test.js --timeout=30000", "start": "node main.js", "test": "mocha test/*.test.js --exit" }, diff --git a/run/markdown-preview/editor/test/deploy.sh b/run/markdown-preview/editor/test/deploy.sh new file mode 100644 index 0000000000..d5411ab67e --- /dev/null +++ b/run/markdown-preview/editor/test/deploy.sh @@ -0,0 +1,38 @@ +#!/usr/bin/env bash + +# Copyright 2020 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. + +set -eo pipefail; + +requireEnv() { + test "${!1}" || (echo "Environment Variable '$1' not found" && exit 1) +} + +requireEnv SERVICE_NAME +requireEnv CONTAINER_IMAGE + +# Deploy the service +set -x +gcloud run deploy "${SERVICE_NAME}" \ + --image="${CONTAINER_IMAGE}" \ + --region="${REGION:-us-central1}" \ + ${FLAGS} \ + --platform=managed \ + --quiet +set +x + +echo 'Cloud Run Links:' +echo "- Logs: https://console.cloud.google.com/logs/viewer?project=${GOOGLE_CLOUD_PROJECT}&resource=cloud_run_revision%2Fservice_name%2F${SERVICE_NAME}" +echo "- Console: https://console.cloud.google.com/run/detail/${REGION:-us-central1}/${SERVICE_NAME}/metrics?project=${GOOGLE_CLOUD_PROJECT}" diff --git a/run/markdown-preview/editor/test/runner.sh b/run/markdown-preview/editor/test/runner.sh new file mode 100644 index 0000000000..55fc4d4577 --- /dev/null +++ b/run/markdown-preview/editor/test/runner.sh @@ -0,0 +1,63 @@ +#!/usr/bin/env bash + +# Copyright 2020 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. + +set -eo pipefail; + +requireEnv() { + test "${!1}" || (echo "Environment Variable '$1' not found" && exit 1) +} +requireEnv SERVICE_NAME + +# The markdown-preview sample needs to be tested with both the editor and renderer services deployed. +echo '---' + +# Build the Renderer service. +test/service.sh +requireEnv UPSTREAM_SERVICE_NAME + +# Deploy the Renderer service. +FLAGS="--no-allow-unauthenticated" SERVICE_NAME=${UPSTREAM_SERVICE_NAME} CONTAINER_IMAGE=${UPSTREAM_CONTAINER_IMAGE} test/deploy.sh + +# Assign the upstream Renderer service url. +export EDITOR_UPSTREAM_RENDER_URL=$(SERVICE_NAME=${UPSTREAM_SERVICE_NAME} test/url.sh) + +# Deploy the Editor service. +FLAGS="--allow-unauthenticated --set-env-vars EDITOR_UPSTREAM_RENDER_URL=$EDITOR_UPSTREAM_RENDER_URL" test/deploy.sh + +# Assign the Editor service url. +export BASE_URL=$(SERVICE_NAME=${SERVICE_NAME} test/url.sh) + +echo +echo '---' +echo + +# Register post-test cleanup. +# Only needed if deploy completed. +function cleanup { + set -x + gcloud run services delete ${SERVICE_NAME} \ + --platform=managed \ + --region="${REGION:-us-central1}" \ + --quiet + gcloud run services delete ${UPSTREAM_SERVICE_NAME} \ + --platform=managed \ + --region="${REGION:-us-central1}" \ + --quiet +} +trap cleanup EXIT + +# Do not use exec to preserve trap behavior. +"$@" \ No newline at end of file diff --git a/run/markdown-preview/editor/test/service.sh b/run/markdown-preview/editor/test/service.sh new file mode 100644 index 0000000000..1ba54b8959 --- /dev/null +++ b/run/markdown-preview/editor/test/service.sh @@ -0,0 +1,43 @@ +#!/usr/bin/env bash + +# Copyright 2020 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. + +# Navigate to the Renderer folder +pushd ../renderer + +# Install dependencies +npm install + +# Version is in the format -. +# Ensures PR-based triggers of the same branch don't collide if Kokoro attempts +# to run them concurrently. +export UPSTREAM_SAMPLE_VERSION="${KOKORO_GIT_COMMIT:-latest}" +export UPSTREAM_SAMPLE_NAME="renderer" + +# Builds not triggered by a PR will fall back to the commit hash then "latest". +SUFFIX=${KOKORO_BUILD_ID} +export UPSTREAM_SERVICE_NAME="${UPSTREAM_SAMPLE_NAME}-${SUFFIX}" +export UPSTREAM_CONTAINER_IMAGE="gcr.io/${GOOGLE_CLOUD_PROJECT}/run-${UPSTREAM_SAMPLE_NAME}:${UPSTREAM_SAMPLE_VERSION}" + +# Build the service +set -x +gcloud builds submit --tag="${UPSTREAM_CONTAINER_IMAGE}" +set +x + +# Register post-test cleanup. +function cleanup { + gcloud --quiet container images delete "${UPSTREAM_CONTAINER_IMAGE}" +} +trap cleanup EXIT HUP \ No newline at end of file diff --git a/run/markdown-preview/editor/test/system.test.js b/run/markdown-preview/editor/test/system.test.js new file mode 100644 index 0000000000..65f428ba5c --- /dev/null +++ b/run/markdown-preview/editor/test/system.test.js @@ -0,0 +1,48 @@ +// 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 +// +// https://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 assert = require('assert'); +const got = require('got'); + +describe('End-to-End Tests', () => { + describe('Service-to-service authenticated request', () => { + const {BASE_URL} = process.env.BASE_URL; + if (!BASE_URL) { + throw Error( + '"BASE_URL" environment variable is required. For example: https://service-x8xabcdefg-uc.a.run.app' + ); + }; + + it('Can successfully make a request', async () => { + const options = { + prefixUrl: BASE_URL.trim() + }; + const response = await got('/', options); + assert.strictEqual(response.statusCode, 200); + }); + + it('Can successfully make a request to the Renderer', async () => { + const options = { + prefixUrl: BASE_URL.trim(), + method: 'POST', + json: { + "data": "**markdown**" + } + }; + const response = await got('/render', options); + assert.strictEqual(response.statusCode, 200); + }); + }); + +}); diff --git a/run/markdown-preview/editor/test/url.sh b/run/markdown-preview/editor/test/url.sh new file mode 100644 index 0000000000..cb0143f7b0 --- /dev/null +++ b/run/markdown-preview/editor/test/url.sh @@ -0,0 +1,30 @@ +#!/usr/bin/env bash + +# Copyright 2020 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. + +set -eo pipefail; + +requireEnv() { + test "${!1}" || (echo "Environment Variable '$1' not found" && exit 1) +} + +requireEnv SERVICE_NAME + +set -x +gcloud run services \ + describe "${SERVICE_NAME}" \ + --region="${REGION:-us-central1}" \ + --format='value(status.url)' \ + --platform=managed \ No newline at end of file From 258b7f20ef537ef2f54923cb6a80ec5b0a4acf63 Mon Sep 17 00:00:00 2001 From: kelsk <38271546+kelsk@users.noreply.github.com> Date: Thu, 4 Jun 2020 09:31:50 -0700 Subject: [PATCH 76/96] Update run/markdown-preview/editor/test/system.test.js Co-authored-by: Averi Kitsch --- run/markdown-preview/editor/test/system.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/run/markdown-preview/editor/test/system.test.js b/run/markdown-preview/editor/test/system.test.js index 65f428ba5c..e9aea401a6 100644 --- a/run/markdown-preview/editor/test/system.test.js +++ b/run/markdown-preview/editor/test/system.test.js @@ -1,4 +1,4 @@ -// Copyright 2019 Google LLC +// Copyright 2020 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. From 4fbb5f474310894beb79aa421e16979d6ee7eedf Mon Sep 17 00:00:00 2001 From: kelsk Date: Thu, 4 Jun 2020 10:59:04 -0700 Subject: [PATCH 77/96] Added test to system.test.js --- run/markdown-preview/editor/test/system.test.js | 1 + 1 file changed, 1 insertion(+) diff --git a/run/markdown-preview/editor/test/system.test.js b/run/markdown-preview/editor/test/system.test.js index 65f428ba5c..a00f2e2c60 100644 --- a/run/markdown-preview/editor/test/system.test.js +++ b/run/markdown-preview/editor/test/system.test.js @@ -42,6 +42,7 @@ describe('End-to-End Tests', () => { }; const response = await got('/render', options); assert.strictEqual(response.statusCode, 200); + assert.strictEqual(response.body, '

markdown

'); }); }); From b071dfb735c71f149d09173c5af9404563c1a674 Mon Sep 17 00:00:00 2001 From: kelsk Date: Thu, 4 Jun 2020 10:59:17 -0700 Subject: [PATCH 78/96] Fixed npm test --- run/markdown-preview/editor/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/run/markdown-preview/editor/package.json b/run/markdown-preview/editor/package.json index 608f36a64c..77a3347031 100644 --- a/run/markdown-preview/editor/package.json +++ b/run/markdown-preview/editor/package.json @@ -16,7 +16,7 @@ "scripts": { "e2e-test": "test/runner.sh mocha test/system.test.js --timeout=30000", "start": "node main.js", - "test": "mocha test/*.test.js --exit" + "test": "mocha test/main.test.js test/render.test.js --exit" }, "dependencies": { "express": "^4.17.1", From 6bfc88b95c859ed583e9500f1dcc649a56732c2d Mon Sep 17 00:00:00 2001 From: kelsk Date: Thu, 4 Jun 2020 15:57:25 -0700 Subject: [PATCH 79/96] Fixed renderer test --- run/markdown-preview/renderer/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/run/markdown-preview/renderer/package.json b/run/markdown-preview/renderer/package.json index ca9a65e213..516a7399e3 100644 --- a/run/markdown-preview/renderer/package.json +++ b/run/markdown-preview/renderer/package.json @@ -22,6 +22,7 @@ }, "devDependencies": { "mocha": "^7.1.1", + "sinon": "^9.0.2", "supertest": "^4.0.2" } } From 0d2d33ac26d2f1ca23624951ba0093330cfa0b3a Mon Sep 17 00:00:00 2001 From: kelsk Date: Thu, 4 Jun 2020 16:09:41 -0700 Subject: [PATCH 80/96] Updated shell permissions --- run/markdown-preview/editor/test/deploy.sh | 0 run/markdown-preview/editor/test/runner.sh | 0 run/markdown-preview/editor/test/service.sh | 0 run/markdown-preview/editor/test/url.sh | 0 4 files changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 run/markdown-preview/editor/test/deploy.sh mode change 100644 => 100755 run/markdown-preview/editor/test/runner.sh mode change 100644 => 100755 run/markdown-preview/editor/test/service.sh mode change 100644 => 100755 run/markdown-preview/editor/test/url.sh diff --git a/run/markdown-preview/editor/test/deploy.sh b/run/markdown-preview/editor/test/deploy.sh old mode 100644 new mode 100755 diff --git a/run/markdown-preview/editor/test/runner.sh b/run/markdown-preview/editor/test/runner.sh old mode 100644 new mode 100755 diff --git a/run/markdown-preview/editor/test/service.sh b/run/markdown-preview/editor/test/service.sh old mode 100644 new mode 100755 diff --git a/run/markdown-preview/editor/test/url.sh b/run/markdown-preview/editor/test/url.sh old mode 100644 new mode 100755 From 4658d62bd6498ec7bd2895a8f31fd5e94ccb3cb5 Mon Sep 17 00:00:00 2001 From: kelsk Date: Mon, 8 Jun 2020 15:45:23 -0700 Subject: [PATCH 81/96] Updated cleanup func --- run/markdown-preview/editor/test/runner.sh | 2 ++ run/markdown-preview/editor/test/service.sh | 6 ------ 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/run/markdown-preview/editor/test/runner.sh b/run/markdown-preview/editor/test/runner.sh index 55fc4d4577..b34df96c1d 100755 --- a/run/markdown-preview/editor/test/runner.sh +++ b/run/markdown-preview/editor/test/runner.sh @@ -56,6 +56,8 @@ function cleanup { --platform=managed \ --region="${REGION:-us-central1}" \ --quiet + gcloud container images delete "${UPSTREAM_CONTAINER_IMAGE}" \ + --quiet } trap cleanup EXIT diff --git a/run/markdown-preview/editor/test/service.sh b/run/markdown-preview/editor/test/service.sh index 1ba54b8959..7638262e6a 100755 --- a/run/markdown-preview/editor/test/service.sh +++ b/run/markdown-preview/editor/test/service.sh @@ -35,9 +35,3 @@ export UPSTREAM_CONTAINER_IMAGE="gcr.io/${GOOGLE_CLOUD_PROJECT}/run-${UPSTREAM_S set -x gcloud builds submit --tag="${UPSTREAM_CONTAINER_IMAGE}" set +x - -# Register post-test cleanup. -function cleanup { - gcloud --quiet container images delete "${UPSTREAM_CONTAINER_IMAGE}" -} -trap cleanup EXIT HUP \ No newline at end of file From 9cc0974460075ab1ce2b9ec2ec82499fe253499e Mon Sep 17 00:00:00 2001 From: kelsk Date: Mon, 8 Jun 2020 16:06:00 -0700 Subject: [PATCH 82/96] Update var exports --- run/markdown-preview/editor/test/runner.sh | 2 +- run/markdown-preview/editor/test/service.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/run/markdown-preview/editor/test/runner.sh b/run/markdown-preview/editor/test/runner.sh index b34df96c1d..73971651d7 100755 --- a/run/markdown-preview/editor/test/runner.sh +++ b/run/markdown-preview/editor/test/runner.sh @@ -25,7 +25,7 @@ requireEnv SERVICE_NAME echo '---' # Build the Renderer service. -test/service.sh +export UPSTREAM_SERVICE_NAME=$(test/service.sh) requireEnv UPSTREAM_SERVICE_NAME # Deploy the Renderer service. diff --git a/run/markdown-preview/editor/test/service.sh b/run/markdown-preview/editor/test/service.sh index 7638262e6a..dffb74aba9 100755 --- a/run/markdown-preview/editor/test/service.sh +++ b/run/markdown-preview/editor/test/service.sh @@ -34,4 +34,4 @@ export UPSTREAM_CONTAINER_IMAGE="gcr.io/${GOOGLE_CLOUD_PROJECT}/run-${UPSTREAM_S # Build the service set -x gcloud builds submit --tag="${UPSTREAM_CONTAINER_IMAGE}" -set +x +echo "$UPSTREAM_SERVICE_NAME" From ef432183c60e80da08f7abeb54dbc7e164eb0602 Mon Sep 17 00:00:00 2001 From: kelsk Date: Mon, 8 Jun 2020 17:15:48 -0700 Subject: [PATCH 83/96] Added service-name.sh --- run/markdown-preview/editor/test/runner.sh | 6 ++++- .../editor/test/service-name.sh | 23 +++++++++++++++++++ run/markdown-preview/editor/test/service.sh | 4 +--- 3 files changed, 29 insertions(+), 4 deletions(-) create mode 100644 run/markdown-preview/editor/test/service-name.sh diff --git a/run/markdown-preview/editor/test/runner.sh b/run/markdown-preview/editor/test/runner.sh index 73971651d7..d63147d005 100755 --- a/run/markdown-preview/editor/test/runner.sh +++ b/run/markdown-preview/editor/test/runner.sh @@ -25,7 +25,11 @@ requireEnv SERVICE_NAME echo '---' # Build the Renderer service. -export UPSTREAM_SERVICE_NAME=$(test/service.sh) +export UPSTREAM_CONTAINER_IMAGE=$(test/service.sh) +requireEnv UPSTREAM_CONTAINER_IMAGE + +# Assign the Renderer service container image. +export UPSTREAM_SERVICE_NAME=$(test/service-name.sh) requireEnv UPSTREAM_SERVICE_NAME # Deploy the Renderer service. diff --git a/run/markdown-preview/editor/test/service-name.sh b/run/markdown-preview/editor/test/service-name.sh new file mode 100644 index 0000000000..8717c23f4b --- /dev/null +++ b/run/markdown-preview/editor/test/service-name.sh @@ -0,0 +1,23 @@ +#!/usr/bin/env bash + +# Copyright 2020 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. + +set -eo pipefail; + +UPSTREAM_SAMPLE_NAME="renderer" +SUFFIX=${KOKORO_BUILD_ID} +export UPSTREAM_SERVICE_NAME="${UPSTREAM_SAMPLE_NAME}-${SUFFIX}" + +echo "$UPSTREAM_SERVICE_NAME" \ No newline at end of file diff --git a/run/markdown-preview/editor/test/service.sh b/run/markdown-preview/editor/test/service.sh index dffb74aba9..9120a829ca 100755 --- a/run/markdown-preview/editor/test/service.sh +++ b/run/markdown-preview/editor/test/service.sh @@ -27,11 +27,9 @@ export UPSTREAM_SAMPLE_VERSION="${KOKORO_GIT_COMMIT:-latest}" export UPSTREAM_SAMPLE_NAME="renderer" # Builds not triggered by a PR will fall back to the commit hash then "latest". -SUFFIX=${KOKORO_BUILD_ID} -export UPSTREAM_SERVICE_NAME="${UPSTREAM_SAMPLE_NAME}-${SUFFIX}" export UPSTREAM_CONTAINER_IMAGE="gcr.io/${GOOGLE_CLOUD_PROJECT}/run-${UPSTREAM_SAMPLE_NAME}:${UPSTREAM_SAMPLE_VERSION}" # Build the service set -x gcloud builds submit --tag="${UPSTREAM_CONTAINER_IMAGE}" -echo "$UPSTREAM_SERVICE_NAME" +echo "$UPSTREAM_CONTAINER_IMAGE" From 080645999015f0ce692a481a7d33bc3a440f6236 Mon Sep 17 00:00:00 2001 From: kelsk Date: Tue, 9 Jun 2020 09:22:13 -0700 Subject: [PATCH 84/96] Removed service-name.sh --- run/markdown-preview/editor/test/runner.sh | 3 ++- .../editor/test/service-name.sh | 23 ------------------- run/markdown-preview/editor/test/service.sh | 3 +-- 3 files changed, 3 insertions(+), 26 deletions(-) delete mode 100644 run/markdown-preview/editor/test/service-name.sh diff --git a/run/markdown-preview/editor/test/runner.sh b/run/markdown-preview/editor/test/runner.sh index d63147d005..8c0e95b815 100755 --- a/run/markdown-preview/editor/test/runner.sh +++ b/run/markdown-preview/editor/test/runner.sh @@ -29,7 +29,8 @@ export UPSTREAM_CONTAINER_IMAGE=$(test/service.sh) requireEnv UPSTREAM_CONTAINER_IMAGE # Assign the Renderer service container image. -export UPSTREAM_SERVICE_NAME=$(test/service-name.sh) +SUFFIX=${KOKORO_BUILD_ID} +export UPSTREAM_SERVICE_NAME="renderer-${SUFFIX}" requireEnv UPSTREAM_SERVICE_NAME # Deploy the Renderer service. diff --git a/run/markdown-preview/editor/test/service-name.sh b/run/markdown-preview/editor/test/service-name.sh deleted file mode 100644 index 8717c23f4b..0000000000 --- a/run/markdown-preview/editor/test/service-name.sh +++ /dev/null @@ -1,23 +0,0 @@ -#!/usr/bin/env bash - -# Copyright 2020 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. - -set -eo pipefail; - -UPSTREAM_SAMPLE_NAME="renderer" -SUFFIX=${KOKORO_BUILD_ID} -export UPSTREAM_SERVICE_NAME="${UPSTREAM_SAMPLE_NAME}-${SUFFIX}" - -echo "$UPSTREAM_SERVICE_NAME" \ No newline at end of file diff --git a/run/markdown-preview/editor/test/service.sh b/run/markdown-preview/editor/test/service.sh index 9120a829ca..f829fcb3f3 100755 --- a/run/markdown-preview/editor/test/service.sh +++ b/run/markdown-preview/editor/test/service.sh @@ -24,10 +24,9 @@ npm install # Ensures PR-based triggers of the same branch don't collide if Kokoro attempts # to run them concurrently. export UPSTREAM_SAMPLE_VERSION="${KOKORO_GIT_COMMIT:-latest}" -export UPSTREAM_SAMPLE_NAME="renderer" # Builds not triggered by a PR will fall back to the commit hash then "latest". -export UPSTREAM_CONTAINER_IMAGE="gcr.io/${GOOGLE_CLOUD_PROJECT}/run-${UPSTREAM_SAMPLE_NAME}:${UPSTREAM_SAMPLE_VERSION}" +export UPSTREAM_CONTAINER_IMAGE="gcr.io/${GOOGLE_CLOUD_PROJECT}/run-renderer:${UPSTREAM_SAMPLE_VERSION}" # Build the service set -x From bc1b999a77ca275c615f3e8f1556fdb65874186e Mon Sep 17 00:00:00 2001 From: kelsk Date: Wed, 10 Jun 2020 15:30:26 -0700 Subject: [PATCH 85/96] Move service.sh functions into runner.sh --- run/markdown-preview/editor/test/runner.sh | 17 +++++++++-- run/markdown-preview/editor/test/service.sh | 34 --------------------- 2 files changed, 15 insertions(+), 36 deletions(-) delete mode 100755 run/markdown-preview/editor/test/service.sh diff --git a/run/markdown-preview/editor/test/runner.sh b/run/markdown-preview/editor/test/runner.sh index 8c0e95b815..40bd2a2c5c 100755 --- a/run/markdown-preview/editor/test/runner.sh +++ b/run/markdown-preview/editor/test/runner.sh @@ -24,8 +24,21 @@ requireEnv SERVICE_NAME # The markdown-preview sample needs to be tested with both the editor and renderer services deployed. echo '---' -# Build the Renderer service. -export UPSTREAM_CONTAINER_IMAGE=$(test/service.sh) +pushd ../renderer + +# Version is in the format -. +# Ensures PR-based triggers of the same branch don't collide if Kokoro attempts +# to run them concurrently. +export UPSTREAM_SAMPLE_VERSION="${KOKORO_GIT_COMMIT:-latest}" +export UPSTREAM_CONTAINER_IMAGE="gcr.io/${GOOGLE_CLOUD_PROJECT}/run-renderer:${UPSTREAM_SAMPLE_VERSION}" + +# Build the Renderer service +set -x +gcloud builds submit --tag="${UPSTREAM_CONTAINER_IMAGE}" +set +x + +pushd ../editor + requireEnv UPSTREAM_CONTAINER_IMAGE # Assign the Renderer service container image. diff --git a/run/markdown-preview/editor/test/service.sh b/run/markdown-preview/editor/test/service.sh deleted file mode 100755 index f829fcb3f3..0000000000 --- a/run/markdown-preview/editor/test/service.sh +++ /dev/null @@ -1,34 +0,0 @@ -#!/usr/bin/env bash - -# Copyright 2020 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. - -# Navigate to the Renderer folder -pushd ../renderer - -# Install dependencies -npm install - -# Version is in the format -. -# Ensures PR-based triggers of the same branch don't collide if Kokoro attempts -# to run them concurrently. -export UPSTREAM_SAMPLE_VERSION="${KOKORO_GIT_COMMIT:-latest}" - -# Builds not triggered by a PR will fall back to the commit hash then "latest". -export UPSTREAM_CONTAINER_IMAGE="gcr.io/${GOOGLE_CLOUD_PROJECT}/run-renderer:${UPSTREAM_SAMPLE_VERSION}" - -# Build the service -set -x -gcloud builds submit --tag="${UPSTREAM_CONTAINER_IMAGE}" -echo "$UPSTREAM_CONTAINER_IMAGE" From 71eafedbfa2ac6f80a30cd2b0d968886b2e38218 Mon Sep 17 00:00:00 2001 From: kelsk Date: Wed, 10 Jun 2020 15:47:05 -0700 Subject: [PATCH 86/96] Added check for BASE_URL --- run/markdown-preview/editor/test/runner.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/run/markdown-preview/editor/test/runner.sh b/run/markdown-preview/editor/test/runner.sh index 40bd2a2c5c..279a7e1f79 100755 --- a/run/markdown-preview/editor/test/runner.sh +++ b/run/markdown-preview/editor/test/runner.sh @@ -57,6 +57,7 @@ FLAGS="--allow-unauthenticated --set-env-vars EDITOR_UPSTREAM_RENDER_URL=$EDITOR # Assign the Editor service url. export BASE_URL=$(SERVICE_NAME=${SERVICE_NAME} test/url.sh) +test -z "$BASE_URL" && echo "BASE_URL value is empty" && exit 1 echo echo '---' From 9ed8ef5d0d86090f57d4a55e128e22a08889e684 Mon Sep 17 00:00:00 2001 From: kelsk Date: Wed, 10 Jun 2020 16:01:37 -0700 Subject: [PATCH 87/96] Fixed system.test env --- run/markdown-preview/editor/test/system.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/run/markdown-preview/editor/test/system.test.js b/run/markdown-preview/editor/test/system.test.js index 5eb44ecba2..5478bba2ea 100644 --- a/run/markdown-preview/editor/test/system.test.js +++ b/run/markdown-preview/editor/test/system.test.js @@ -17,7 +17,7 @@ const got = require('got'); describe('End-to-End Tests', () => { describe('Service-to-service authenticated request', () => { - const {BASE_URL} = process.env.BASE_URL; + const {BASE_URL} = process.env; if (!BASE_URL) { throw Error( '"BASE_URL" environment variable is required. For example: https://service-x8xabcdefg-uc.a.run.app' From 44af0294ff95444014a5124005f32b4108f2e523 Mon Sep 17 00:00:00 2001 From: kelsk Date: Thu, 11 Jun 2020 11:24:28 -0700 Subject: [PATCH 88/96] Add check for BASE_URL var --- run/markdown-preview/editor/test/system.test.js | 1 + 1 file changed, 1 insertion(+) diff --git a/run/markdown-preview/editor/test/system.test.js b/run/markdown-preview/editor/test/system.test.js index 5478bba2ea..6c624edfe1 100644 --- a/run/markdown-preview/editor/test/system.test.js +++ b/run/markdown-preview/editor/test/system.test.js @@ -23,6 +23,7 @@ describe('End-to-End Tests', () => { '"BASE_URL" environment variable is required. For example: https://service-x8xabcdefg-uc.a.run.app' ); }; + console.log(`BASE_URL environment variable declared with value ${BASE_URL}`); it('Can successfully make a request', async () => { const options = { From c4a152f3f3f2b6994c1720eb2272e9c4b6d2a61c Mon Sep 17 00:00:00 2001 From: kelsk Date: Fri, 12 Jun 2020 10:16:04 -0700 Subject: [PATCH 89/96] Added id token --- run/markdown-preview/editor/test/runner.sh | 3 +++ run/markdown-preview/editor/test/system.test.js | 14 ++++++++++++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/run/markdown-preview/editor/test/runner.sh b/run/markdown-preview/editor/test/runner.sh index 279a7e1f79..70e21cd001 100755 --- a/run/markdown-preview/editor/test/runner.sh +++ b/run/markdown-preview/editor/test/runner.sh @@ -59,6 +59,9 @@ FLAGS="--allow-unauthenticated --set-env-vars EDITOR_UPSTREAM_RENDER_URL=$EDITOR export BASE_URL=$(SERVICE_NAME=${SERVICE_NAME} test/url.sh) test -z "$BASE_URL" && echo "BASE_URL value is empty" && exit 1 +# Assign an ID token for the Editor service. +export ID_TOKEN=$(gcloud auth print-identity-token) + echo echo '---' echo diff --git a/run/markdown-preview/editor/test/system.test.js b/run/markdown-preview/editor/test/system.test.js index 6c624edfe1..1ab906f3af 100644 --- a/run/markdown-preview/editor/test/system.test.js +++ b/run/markdown-preview/editor/test/system.test.js @@ -17,17 +17,24 @@ const got = require('got'); describe('End-to-End Tests', () => { describe('Service-to-service authenticated request', () => { + const {ID_TOKEN} = process.env; + if (!ID_TOKEN) { + throw Error('"ID_TOKEN" environment variable is required.'); + } + const {BASE_URL} = process.env; if (!BASE_URL) { throw Error( '"BASE_URL" environment variable is required. For example: https://service-x8xabcdefg-uc.a.run.app' ); }; - console.log(`BASE_URL environment variable declared with value ${BASE_URL}`); it('Can successfully make a request', async () => { const options = { - prefixUrl: BASE_URL.trim() + prefixUrl: BASE_URL.trim(), + headers: { + Authorization: `Bearer ${ID_TOKEN.trim()}` + } }; const response = await got('/', options); assert.strictEqual(response.statusCode, 200); @@ -36,6 +43,9 @@ describe('End-to-End Tests', () => { it('Can successfully make a request to the Renderer', async () => { const options = { prefixUrl: BASE_URL.trim(), + headers: { + Authorization: `Bearer ${ID_TOKEN.trim()}` + }, method: 'POST', json: { "data": "**markdown**" From 57aa5ee051e34dd9b115359ddb67e711525e96a7 Mon Sep 17 00:00:00 2001 From: kelsk Date: Fri, 12 Jun 2020 10:29:18 -0700 Subject: [PATCH 90/96] Fixed test; removed allow-auth flag --- run/markdown-preview/editor/test/runner.sh | 3 ++- run/markdown-preview/editor/test/system.test.js | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/run/markdown-preview/editor/test/runner.sh b/run/markdown-preview/editor/test/runner.sh index 70e21cd001..8d0a73fc05 100755 --- a/run/markdown-preview/editor/test/runner.sh +++ b/run/markdown-preview/editor/test/runner.sh @@ -53,7 +53,7 @@ FLAGS="--no-allow-unauthenticated" SERVICE_NAME=${UPSTREAM_SERVICE_NAME} CONTAIN export EDITOR_UPSTREAM_RENDER_URL=$(SERVICE_NAME=${UPSTREAM_SERVICE_NAME} test/url.sh) # Deploy the Editor service. -FLAGS="--allow-unauthenticated --set-env-vars EDITOR_UPSTREAM_RENDER_URL=$EDITOR_UPSTREAM_RENDER_URL" test/deploy.sh +FLAGS="--set-env-vars EDITOR_UPSTREAM_RENDER_URL=$EDITOR_UPSTREAM_RENDER_URL" test/deploy.sh # Assign the Editor service url. export BASE_URL=$(SERVICE_NAME=${SERVICE_NAME} test/url.sh) @@ -61,6 +61,7 @@ test -z "$BASE_URL" && echo "BASE_URL value is empty" && exit 1 # Assign an ID token for the Editor service. export ID_TOKEN=$(gcloud auth print-identity-token) +test -z "$ID_TOKEN" && echo "ID_TOKEN value is empty" && exit 1 echo echo '---' diff --git a/run/markdown-preview/editor/test/system.test.js b/run/markdown-preview/editor/test/system.test.js index 1ab906f3af..edcd8d4a10 100644 --- a/run/markdown-preview/editor/test/system.test.js +++ b/run/markdown-preview/editor/test/system.test.js @@ -53,7 +53,7 @@ describe('End-to-End Tests', () => { }; const response = await got('/render', options); assert.strictEqual(response.statusCode, 200); - assert.strictEqual(response.body, '

markdown

'); + assert.strictEqual(response.body, '

markdown

\n'); }); }); From a31c3206dcbac790ae55a19e93fd39579d3160b5 Mon Sep 17 00:00:00 2001 From: kelsk Date: Mon, 15 Jun 2020 16:51:12 -0700 Subject: [PATCH 91/96] Added retry to http requests --- run/markdown-preview/editor/render.js | 3 ++- run/markdown-preview/editor/test/system.test.js | 8 ++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/run/markdown-preview/editor/render.js b/run/markdown-preview/editor/render.js index 94d034528e..4b47aad527 100644 --- a/run/markdown-preview/editor/render.js +++ b/run/markdown-preview/editor/render.js @@ -32,7 +32,8 @@ const renderRequest = async (markdown) => { 'Content-Type': 'text/plain' }, body: markdown, - timeout: 3000 + timeout: 3000, + retry: 1 }; try { diff --git a/run/markdown-preview/editor/test/system.test.js b/run/markdown-preview/editor/test/system.test.js index edcd8d4a10..bbb85eb72d 100644 --- a/run/markdown-preview/editor/test/system.test.js +++ b/run/markdown-preview/editor/test/system.test.js @@ -34,7 +34,9 @@ describe('End-to-End Tests', () => { prefixUrl: BASE_URL.trim(), headers: { Authorization: `Bearer ${ID_TOKEN.trim()}` - } + }, + timeout: 3000, + retry: 1 }; const response = await got('/', options); assert.strictEqual(response.statusCode, 200); @@ -49,7 +51,9 @@ describe('End-to-End Tests', () => { method: 'POST', json: { "data": "**markdown**" - } + }, + timeout: 3000, + retry: 1 }; const response = await got('/render', options); assert.strictEqual(response.statusCode, 200); From 46b2110800a5b2c1c7b19a6c843afdd615819327 Mon Sep 17 00:00:00 2001 From: kelsk Date: Mon, 15 Jun 2020 17:52:58 -0700 Subject: [PATCH 92/96] Updated timeout on Renderer test --- run/markdown-preview/editor/test/system.test.js | 1 - 1 file changed, 1 deletion(-) diff --git a/run/markdown-preview/editor/test/system.test.js b/run/markdown-preview/editor/test/system.test.js index bbb85eb72d..9d6a030f66 100644 --- a/run/markdown-preview/editor/test/system.test.js +++ b/run/markdown-preview/editor/test/system.test.js @@ -52,7 +52,6 @@ describe('End-to-End Tests', () => { json: { "data": "**markdown**" }, - timeout: 3000, retry: 1 }; const response = await got('/render', options); From 84b51f89285220b98e9c5088c485ce46d3e94298 Mon Sep 17 00:00:00 2001 From: kelsk Date: Mon, 15 Jun 2020 17:53:32 -0700 Subject: [PATCH 93/96] Removed all timeouts from tests --- run/markdown-preview/editor/test/system.test.js | 1 - 1 file changed, 1 deletion(-) diff --git a/run/markdown-preview/editor/test/system.test.js b/run/markdown-preview/editor/test/system.test.js index b90ce9df12..c599f7aad8 100644 --- a/run/markdown-preview/editor/test/system.test.js +++ b/run/markdown-preview/editor/test/system.test.js @@ -35,7 +35,6 @@ describe('End-to-End Tests', () => { headers: { Authorization: `Bearer ${ID_TOKEN.trim()}` }, - timeout: 3000, retry: 1 }; const response = await got('/', options); From e42a9ae1d27f81cd5963e5a72c02222f53dab565 Mon Sep 17 00:00:00 2001 From: kelsk Date: Tue, 16 Jun 2020 09:57:27 -0700 Subject: [PATCH 94/96] Removed retry from render.js --- run/markdown-preview/editor/render.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/run/markdown-preview/editor/render.js b/run/markdown-preview/editor/render.js index 4b47aad527..94d034528e 100644 --- a/run/markdown-preview/editor/render.js +++ b/run/markdown-preview/editor/render.js @@ -32,8 +32,7 @@ const renderRequest = async (markdown) => { 'Content-Type': 'text/plain' }, body: markdown, - timeout: 3000, - retry: 1 + timeout: 3000 }; try { From 737e49599dc03fd6077b8235ce69bc54c5009417 Mon Sep 17 00:00:00 2001 From: kelsk Date: Wed, 17 Jun 2020 10:55:45 -0700 Subject: [PATCH 95/96] Add region tag 'run_secure_request_do' --- run/markdown-preview/editor/main.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/run/markdown-preview/editor/main.js b/run/markdown-preview/editor/main.js index 589dce123f..b36723e8d8 100644 --- a/run/markdown-preview/editor/main.js +++ b/run/markdown-preview/editor/main.js @@ -46,6 +46,7 @@ app.get('/', async (req, res) => { // The renderRequest makes a request to the Renderer service. // The request returns the Markdown text converted to HTML. +// [START run_secure_request_do] app.post('/render', async (req, res) => { try { const markdown = req.body.data; @@ -56,6 +57,7 @@ app.post('/render', async (req, res) => { res.status(500).send(err); } }); +// [END run_secure_request_do] const PORT = process.env.PORT || 8080; From 6e57917ddbbbb4c76f241f77b0d807b4ab531821 Mon Sep 17 00:00:00 2001 From: kelsk Date: Fri, 19 Jun 2020 10:54:19 -0700 Subject: [PATCH 96/96] Fix endpoint syntax --- run/markdown-preview/editor/test/system.test.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/run/markdown-preview/editor/test/system.test.js b/run/markdown-preview/editor/test/system.test.js index c599f7aad8..9af8256d51 100644 --- a/run/markdown-preview/editor/test/system.test.js +++ b/run/markdown-preview/editor/test/system.test.js @@ -37,7 +37,7 @@ describe('End-to-End Tests', () => { }, retry: 1 }; - const response = await got('/', options); + const response = await got('', options); assert.strictEqual(response.statusCode, 200); }); @@ -54,7 +54,7 @@ describe('End-to-End Tests', () => { }, retry: 1 }; - const response = await got('/render', options); + const response = await got('render', options); assert.strictEqual(response.statusCode, 200); assert.strictEqual(response.body, '

markdown

\n'); });