Skip to content

feat(markdown-preview): Migrate editor and renderer services to ESM #4065

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 5 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 13 additions & 12 deletions run/markdown-preview/editor/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@
// See the License for the specific language governing permissions and
// limitations under the License.

const express = require('express');
const handlebars = require('handlebars');
const {readFile} = require('fs').promises;
const renderRequest = require('./render.js');
import express from 'express';
import handlebars from 'handlebars';
import {readFile} from 'fs/promises';
import renderRequest from './render.js';

const app = express();
app.use(express.json());
Expand All @@ -25,14 +25,18 @@ let markdownDefault, compiledTemplate, renderedHtml;
// Load the template files and serve them with the Editor service.
const buildRenderedHtml = async () => {
try {
markdownDefault = await readFile(__dirname + '/templates/markdown.md');
compiledTemplate = handlebars.compile(
await readFile(__dirname + '/templates/index.html', 'utf8')
markdownDefault = await readFile(
new URL('./templates/markdown.md', import.meta.url)
);
const indexTemplate = await readFile(
new URL('./templates/index.html', import.meta.url),
'utf8'
);
compiledTemplate = handlebars.compile(indexTemplate);
renderedHtml = compiledTemplate({default: markdownDefault});
return renderedHtml;
} catch (err) {
throw Error('Error loading template: ', err);
throw Error('Error loading template: ' + err);
}
};

Expand Down Expand Up @@ -62,7 +66,4 @@ app.post('/render', async (req, res) => {
// [END cloudrun_secure_request_do]

// Exports for testing purposes.
module.exports = {
app,
buildRenderedHtml,
};
export {app, buildRenderedHtml};
6 changes: 3 additions & 3 deletions run/markdown-preview/editor/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.

const {app} = require('./app');
const pkg = require('./package.json');
const PORT = parseInt(process.env.PORT) || 8080;
import {app} from './app';
import pkg from './package.json' assert {type: 'json'};

const PORT = parseInt(process.env.PORT) || 8080;
app.listen(PORT, () => console.log(`${pkg.name} listening on port ${PORT}`));
3 changes: 2 additions & 1 deletion run/markdown-preview/editor/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"private": true,
"license": "Apache-2.0",
"author": "Google LLC",
"type": "module",
"repository": {
"type": "git",
"url": "https://github.com/GoogleCloudPlatform/nodejs-docs-samples.git"
Expand All @@ -23,7 +24,7 @@
"dependencies": {
"express": "^4.17.1",
"google-auth-library": "^9.0.0",
"got": "^11.5.0",
"got": "^12.6.1",
"handlebars": "^4.7.6"
},
"devDependencies": {
Expand Down
19 changes: 11 additions & 8 deletions run/markdown-preview/editor/render.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,18 @@
// limitations under the License.

// [START cloudrun_secure_request]
const {GoogleAuth} = require('google-auth-library');
const got = require('got');
const auth = new GoogleAuth();
import {GoogleAuth} from 'google-auth-library';
import got from 'got';

const auth = new GoogleAuth();
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 => {
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.
Expand All @@ -33,15 +34,17 @@ const renderRequest = async markdown => {
'Content-Type': 'text/plain',
},
body: markdown,
timeout: 3000,
timeout: {
request: 3000,
},
};

try {
// [END cloudrun_secure_request]
// If we're in the test environment, use the envvar instead
if (process.env.ID_TOKEN) {
serviceRequestOptions.headers['Authorization'] =
'Bearer ' + process.env.ID_TOKEN;
`Bearer ${process.env.ID_TOKEN}`;
} else {
// [START cloudrun_secure_request]
// Create a Google Auth client with the Renderer service url as the target audience.
Expand All @@ -60,13 +63,13 @@ const renderRequest = async markdown => {

try {
// serviceResponse converts the Markdown plaintext to HTML.
const serviceResponse = await got(serviceUrl, serviceRequestOptions);
return serviceResponse.body;
const {body} = await got(serviceUrl, serviceRequestOptions);
return body;
} catch (err) {
throw Error('request to rendering service failed: ' + err.message);
}
};

// [END cloudrun_secure_request]

module.exports = renderRequest;
export default renderRequest;
20 changes: 13 additions & 7 deletions run/markdown-preview/editor/test/app.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,24 +14,30 @@

'use strict';

const assert = require('assert');
const path = require('path');
const supertest = require('supertest');
import assert from 'assert';
import path from 'path';
import {fileURLToPath} from 'url';
import supertest from 'supertest';

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

describe('Editor unit tests', () => {
describe('Initialize app', () => {
it('should successfully load the index page', async () => {
const {app} = require(path.join(__dirname, '..', 'app'));
const {app} = await import(path.join(__dirname, '..', 'app.js'));
const request = supertest(app);
await request.get('/').retry(3).expect(200);
});
});

describe('Handlebars compiler', async () => {
describe('Handlebars compiler', () => {
let template;

before(async () => {
const {buildRenderedHtml} = require(path.join(__dirname, '..', 'app'));
const {buildRenderedHtml} = await import(
path.join(__dirname, '..', 'app.js')
);
template = await buildRenderedHtml();
});

Expand All @@ -48,7 +54,7 @@ describe('Integration tests', () => {

before(async () => {
process.env.EDITOR_UPSTREAM_RENDER_URL = 'https://www.example.com/';
const {app} = require(path.join(__dirname, '..', 'app'));
const {app} = await import(path.join(__dirname, '..', 'app.js'));
request = supertest(app);
});

Expand Down
16 changes: 8 additions & 8 deletions run/markdown-preview/editor/test/system.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.

const assert = require('assert');
const got = require('got');
const {execSync} = require('child_process');
import assert from 'assert';
import got from 'got';
import {execSync} from 'child_process';

describe('End-to-End Tests', () => {
// Retrieve Cloud Run service test config
Expand All @@ -27,13 +27,13 @@ describe('End-to-End Tests', () => {
console.log('"SERVICE_NAME" env var not found. Defaulting to "editor"');
SERVICE_NAME = 'editor';
}
const {ID_TOKEN} = process.env;
if (!ID_TOKEN) throw Error('ID token not in envvar');
const {SAMPLE_VERSION} = process.env;
const {SERVICE_ACCOUNT} = process.env;

const {ID_TOKEN, SAMPLE_VERSION, SERVICE_ACCOUNT} = process.env;
const REGION = 'us-central1';
if (!ID_TOKEN) throw Error('ID token not in envvar');

let BASE_URL;

before(async () => {
// Deploy Renderer service
let buildRendererCmd =
Expand Down Expand Up @@ -88,7 +88,7 @@ describe('End-to-End Tests', () => {
headers: {
Authorization: `Bearer ${ID_TOKEN.trim()}`,
},
retry: 3,
retry: {limit: 3},
};
const response = await got('', options);
assert.strictEqual(response.statusCode, 200);
Expand Down
6 changes: 3 additions & 3 deletions run/markdown-preview/renderer/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.

const express = require('express');
const MarkdownIt = require('markdown-it');
import express from 'express';
import MarkdownIt from 'markdown-it';

const app = express();
app.use(express.text());
Expand All @@ -40,4 +40,4 @@ app.post('/', (req, res) => {
});

// Export for testing purposes.
module.exports = app;
export default app;
6 changes: 3 additions & 3 deletions run/markdown-preview/renderer/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.

const app = require('./app');
const pkg = require('./package.json');
const PORT = parseInt(process.env.PORT) || 8080;
import app from './app';
import pkg from './package.json';

const PORT = parseInt(process.env.PORT) || 8080;
app.listen(PORT, () => console.log(`${pkg.name} listening on port ${PORT}`));
5 changes: 3 additions & 2 deletions run/markdown-preview/renderer/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"private": true,
"license": "Apache-2.0",
"author": "Google LLC",
"type": "module",
"repository": {
"type": "git",
"url": "https://github.com/GoogleCloudPlatform/nodejs-docs-samples.git"
Expand All @@ -21,14 +22,14 @@
},
"dependencies": {
"express": "^4.17.1",
"got": "^12.6.1",
"markdown-it": "^14.0.0"
},
"devDependencies": {
"c8": "^10.0.0",
"google-auth-library": "^9.0.0",
"got": "^11.5.0",
"mocha": "^10.0.0",
"sinon": "^18.0.0",
"supertest": "^7.0.0"
}
}
}
18 changes: 11 additions & 7 deletions run/markdown-preview/renderer/test/app.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,21 @@

'use strict';

const assert = require('assert');
const path = require('path');
const sinon = require('sinon');
const supertest = require('supertest');
import assert from 'assert';
import path from 'path';
import sinon from 'sinon';
import supertest from 'supertest';
import {fileURLToPath} from 'url';

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

let request;

describe('Unit Tests', () => {
before(() => {
const app = require(path.join(__dirname, '..', 'app'));
request = supertest(app);
before(async () => {
const appModule = await import(path.join(__dirname, '..', 'app.js'));
request = supertest(appModule.default);
});

it('should return Bad Request with an invalid type', async () => {
Expand Down
17 changes: 9 additions & 8 deletions run/markdown-preview/renderer/test/system.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,28 +12,29 @@
// See the License for the specific language governing permissions and
// limitations under the License.

const assert = require('assert');
const got = require('got');
const {execSync} = require('child_process');
import assert from 'assert';
import got from 'got';
import {execSync} from 'child_process';

describe('End-to-End Tests', () => {
// Retrieve Cloud Run service test config
const {GOOGLE_CLOUD_PROJECT} = process.env;
if (!GOOGLE_CLOUD_PROJECT) {
throw Error('"GOOGLE_CLOUD_PROJECT" env var not found.');
}

let {SERVICE_NAME} = process.env;
if (!SERVICE_NAME) {
console.log('"SERVICE_NAME" env var not found. Defaulting to "editor"');
SERVICE_NAME = 'renderer';
}
const {ID_TOKEN} = process.env;
if (!ID_TOKEN) throw Error('ID token not in envvar');
const {SAMPLE_VERSION} = process.env;
const {SERVICE_ACCOUNT} = process.env;

const {ID_TOKEN, SAMPLE_VERSION, SERVICE_ACCOUNT} = process.env;
const REGION = 'us-central1';
if (!ID_TOKEN) throw Error('ID token not in envvar');

let BASE_URL;

before(async () => {
// Deploy service using Cloud Build
let buildCmd =
Expand Down Expand Up @@ -74,7 +75,7 @@ describe('End-to-End Tests', () => {
Authorization: `Bearer ${ID_TOKEN.trim()}`,
},
method: 'POST',
retry: 3,
retry: {limit: 3},
throwHttpErrors: false,
};
const response = await got('', options);
Expand Down
Loading