Skip to content

Commit

Permalink
feat: add goto option (#420)
Browse files Browse the repository at this point in the history
* feat: add goto option

* chore: update README.md

* style: apply lint suggestions

Co-authored-by: Ivan Vilanculo <ivan.vilanculo@vm.co.mz>
  • Loading branch information
isneezy and Ivan Vilanculo authored Jan 17, 2023
1 parent 4a7ff6a commit 8f476d4
Show file tree
Hide file tree
Showing 8 changed files with 46 additions and 7 deletions.
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,9 @@ The webserver started by express.js has one JSON endpoint to generate PDFs.
Will generate a PDF based on the given `payload` data and returns the pdf file as a stream
```json5
{
"content": "", // required - HTML string/handlebars template to be converted to PDF,
"context": {}, // object with the data to be passed to handlebars template engine
"goto": "", // optional - URL to the HTML content/handlebars template to be converted to PDF. This option overrides the content when present.
"content": "", // required when goto is not present - HTML string/handlebars template to be converted to PDF,
"context": {}, // object with the data to be passed to handlebars template engine
"orientation": "portrait", // optional - possible values ["portrait", "landscape"]
"format": "A4", // optional - possible values ["Letter", "Legal", "Tabloid", "Ledger", "A0", "A1", "A2", "A3", "A4", "A5", "A6"]
"header": "", // optional - HTML template for the print header. See https://github.com/puppeteer/puppeteer/blob/main/docs/api.md#pagepdfoptions
Expand Down
10 changes: 10 additions & 0 deletions src/__test__/payloadValidator.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { validatePayload } from '../payloadValidator'

describe('src/payloadValidator.ts', () => {
it('should only allow payload with one of content or goto options exclusively', () => {
let errors = validatePayload({})
expect(errors.content[0]).toMatch(/html filed is required /)
errors = validatePayload({ content: '<htm></htm>>', goto: 'https://example.com' })
expect(Object.keys(errors).length).toBe(0)
})
})
4 changes: 2 additions & 2 deletions src/payloadValidator.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
export function validatePayload(body: Record<string, string> = {}): Record<string, string[]> {
const errors: Record<string, string[]> = {}
if (!body.content) {
errors.content = ['html filed is required.']
if (!body.content && !body.goto) {
errors.content = ['html filed is required when goto is not present.']
}
if (body.content && body.content.includes('<script')) {
errors.content = errors.content || []
Expand Down
3 changes: 2 additions & 1 deletion src/services/Pdf.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ export class Pdf {
}

private static async generateContent(options: PdfOptions, page: Page): Promise<Buffer> {
await page.setContent(options.content, { waitUntil: 'networkidle0' })
if (options.goto) await page.goto(options.goto, { waitUntil: 'networkidle0' })
else await page.setContent(options.content, { waitUntil: 'networkidle0' })
const pdfOptions = Pdf.buildPdfArguments(options, false)
return await page.pdf(pdfOptions)
}
Expand Down
5 changes: 4 additions & 1 deletion src/services/PdfOptions.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import defaults from 'lodash.defaults'
import { PDFMargin, PaperFormat } from 'puppeteer'
import { isValidURL } from '../util'

export type PDFOrientation = 'landscape' | 'portrait'
export type TocEntry = {
Expand All @@ -10,6 +11,7 @@ export type TocEntry = {
page?: number
}
export interface PdfOptions {
goto?: string
orientation?: PDFOrientation
format?: PaperFormat
content: string
Expand All @@ -25,7 +27,8 @@ export interface PdfOptions {
}

export function pdfOptionsFactory(options: Partial<PdfOptions>): PdfOptions {
if (!options.content || !options.content.length) {
if (options.goto && !isValidURL(options.goto)) throw new Error('invalid value passed to goto option')
if ((!options.content || !options.content.length) && !options.goto) {
throw new Error('content should not be empty')
}

Expand Down
6 changes: 6 additions & 0 deletions src/services/__test__/PdfOptions.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@ import { throws } from 'assert'
import { PaperFormat } from 'puppeteer'

describe('PdfOptions', () => {
it('must not throw any errors when content is not present and got is valid url', () => {
pdfOptionsFactory({ goto: 'https://www.example.com' })
})
it('must throw errors when goto is not valid URL', () => {
throws(() => pdfOptionsFactory({ goto: 'www.example.com' }), /invalid value passed to goto option/)
})
it('must trow error with message `content should not be empty` when content is empty', () => {
throws(() => pdfOptionsFactory({ content: '' }), /content should not be empty/)
})
Expand Down
11 changes: 10 additions & 1 deletion src/util/__test__/index.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { pdfOptionsFactory } from '../../services/PdfOptions'
import { compileHeaderOrFooterTemplate, enhanceContent, prepareToc } from '..'
import { compileHeaderOrFooterTemplate, enhanceContent, isValidURL, prepareToc } from '..'

describe('utils.ts', () => {
it('should wrap the template with a div with margin and font size', () => {
Expand Down Expand Up @@ -83,4 +83,13 @@ describe('prepareToc from src/utils.ts', () => {
expect(options.content).toContain(`class="removeAfterTocExtraction">${data.id}<`)
)
})

it('should validate urls', () => {
expect(isValidURL('some random string')).toBe(false)
expect(isValidURL('www.example.com')).toBe(false)
expect(isValidURL('example.com')).toBe(false)
expect(isValidURL('http://example.com')).toBe(true)
expect(isValidURL('http://example.com:80')).toBe(true)
expect(isValidURL('https://example.com')).toBe(true)
})
})
9 changes: 9 additions & 0 deletions src/util/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,3 +85,12 @@ export async function enhanceContent(options: PdfOptions): Promise<void> {

prepareToc(options)
}

export const isValidURL = (url: string): boolean => {
try {
new URL(url)
return true
} catch (e) {
return false
}
}

0 comments on commit 8f476d4

Please sign in to comment.