-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement generate-document lambda (WIP)
Implement lambda to generate documents (e.g., DOW) that returns a base64 encoded string of the pdf binary. Template files are contained in a layer and are under the /templates folder. Juice is a third-party package to combine the html and css files. Still troubleshooting trying to get puppeteer to run inside of lambda using puppeteer-core and having a compressed chromium binary added as a dependency or some alternative. The chrome-aws-lambda package does not appear to be maintained anymore, but someone has forked the repo and has been updating recent versions of node (v16) and puppeteer (v14). - https://acloudguru.com/blog/engineering/serverless-browser-automation-with-aws-lambda-and-puppeteer - https://github.com/alixaxel/chrome-aws-lambda - https://github.com/Sparticuz/chrome-aws-lambda - alixaxel/chrome-aws-lambda#264 - alixaxel/chrome-aws-lambda#275 Tikcet: AT-7345
- Loading branch information
1 parent
9f371e6
commit 74c237d
Showing
5 changed files
with
1,990 additions
and
50 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import { handler } from "./generate-document"; | ||
|
||
describe("Success", () => { | ||
it("should return successful", async () => { | ||
// GIVEN / ARRANGE | ||
// WHEN / ACT | ||
// THEN / ASSERT | ||
}); | ||
}); | ||
|
||
describe("Failure", () => { | ||
it("should return errors", async () => { | ||
// GIVEN / ARRANGE | ||
// WHEN / ACT | ||
// THEN / ASSERT | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
import { APIGatewayProxyEvent, APIGatewayProxyResult } from "aws-lambda"; | ||
import { injectLambdaContext } from "@aws-lambda-powertools/logger"; | ||
import middy from "@middy/core"; | ||
import httpJsonBodyParser from "@middy/http-json-body-parser"; | ||
import inputOutputLogger from "@middy/input-output-logger"; | ||
import errorLogger from "@middy/error-logger"; | ||
import { logger } from "../utils/logging"; | ||
import { ApiBase64SuccessResponse, SuccessStatusCode } from "../utils/response"; | ||
import { IpCheckerMiddleware } from "../utils/middleware/ip-logging"; | ||
import { errorHandlingMiddleware } from "../utils/middleware/error-handling-middleware"; | ||
import JSONErrorHandlerMiddleware from "middy-middleware-json-error-handler"; | ||
import validator from "@middy/validator"; | ||
import { wrapSchema } from "../utils/middleware/schema-wrapper"; | ||
import { | ||
generateDocumentSchema, | ||
RequestEvent, | ||
GenerateDocumentRequest, | ||
DocumentType, | ||
} from "../models/document-generation"; | ||
import * as fs from "fs"; | ||
import handlebars from "handlebars"; | ||
// import puppeteer from "puppeteer"; | ||
import * as puppeteer from "puppeteer-core"; | ||
import { PDFOptions } from "puppeteer"; | ||
import chromium from "chrome-aws-lambda"; | ||
import juice from "juice"; | ||
|
||
async function baseHandler( | ||
event: RequestEvent<GenerateDocumentRequest> | ||
): Promise<ApiBase64SuccessResponse<APIGatewayProxyResult>> { | ||
const lookingAtFiles = fs.readdirSync("/var/task/", { withFileTypes: true }); | ||
// logger.debug not work ? | ||
console.debug("FILES: " + JSON.stringify(lookingAtFiles)); | ||
|
||
// small sample to ensure data populated in template | ||
const { documentType, templatePayload } = event.body; | ||
|
||
// get files to generate documents | ||
let html, css, htmlWithCss; | ||
if (documentType === DocumentType.DESCRIPTION_OF_WORK) { | ||
html = fs.readFileSync("/opt/dow-template.html", "utf-8"); | ||
css = fs.readFileSync("/opt/dow-style.css", "utf-8"); | ||
htmlWithCss = juice.inlineContent(html, css); | ||
} | ||
|
||
// use handlebars to populate data into template | ||
const template = handlebars.compile(htmlWithCss); | ||
const templateWithData = template(templatePayload); | ||
|
||
// use puppeteer to generate pdf | ||
let browser, pdf; | ||
|
||
try { | ||
// ! chromium module does not seem to be added, troubleshoot | ||
console.debug("PUPPET: " + JSON.stringify(chromium.defaultViewport)); | ||
console.debug("PUPPET: " + JSON.stringify(chromium.defaultViewport)); | ||
console.debug("PUPPET: " + JSON.stringify(chromium.executablePath)); // ! empty object | ||
console.debug("PUPPET: " + JSON.stringify(chromium.headless)); // true | ||
console.debug("PUPPET: " + JSON.stringify(chromium.puppeteer)); // ! undefined | ||
|
||
browser = await chromium.puppeteer.launch({ | ||
args: chromium.args, | ||
defaultViewport: chromium.defaultViewport, | ||
executablePath: await chromium.executablePath, | ||
headless: chromium.headless, | ||
ignoreHTTPSErrors: true, | ||
}); | ||
logger.debug("BROWSER: " + JSON.stringify(browser)); | ||
const page = await browser.newPage(); | ||
logger.debug("PAGE: " + JSON.stringify(page)); | ||
|
||
const options: any = { format: "A4" }; // PDFOptions still gives typescript error | ||
|
||
await page.setContent(templateWithData); | ||
await page.emulateMediaType("screen"); | ||
pdf = await page.pdf(options); | ||
|
||
logger.info("Document generation complete"); | ||
logger.debug("PDF: " + pdf); | ||
} catch (error) { | ||
logger.error(error as any); | ||
} finally { | ||
if (browser !== null) { | ||
await browser?.close(); | ||
} | ||
} | ||
|
||
const headers = { "Content-type": "application/pdf" }; | ||
return new ApiBase64SuccessResponse<string | undefined>(pdf?.toString("base64"), SuccessStatusCode.OK, headers); | ||
} | ||
|
||
export const handler = middy(baseHandler) | ||
.use(injectLambdaContext(logger)) | ||
.use(inputOutputLogger({ logger: (message) => logger.info("Event/Result", message) })) | ||
.use(errorLogger({ logger: (err) => logger.error("An error occurred during the request", err as Error) })) | ||
.use(httpJsonBodyParser()) | ||
.use(validator({ eventSchema: wrapSchema(generateDocumentSchema) })); | ||
// TODO: fix middleware inputs/outputs | ||
// .use(IpCheckerMiddleware()) | ||
// .use(errorHandlingMiddleware); |
Oops, something went wrong.