Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 38a379afe7 | |||
| 65abf280a8 | |||
| 70885ee8d0 | |||
| cd08c5e860 | |||
| 6cc5e292ad | |||
| b863239514 | |||
| 3be519955b | |||
| 51dcdc83a9 | |||
| 5cbd477051 |
@@ -330,6 +330,31 @@ const toMergedPDF = pipe(
|
||||
)
|
||||
```
|
||||
|
||||
## Advanced fine adjustment
|
||||
|
||||
There is special function `adjust`, which you can use to modify _any_ field in prepared internal `Request` object. You can check internal `Request` object structure in types. Any object, passed to `adjust`, will be merged with prepared `Request`.
|
||||
|
||||
For example, you can modify `url`, if your Gotenberg instance is working behind reverse proxy with some weird url replacement rules:
|
||||
|
||||
```typescript
|
||||
import { pipe, gotenberg, convert, html, adjust, please } from 'gotenberg-js-client'
|
||||
|
||||
// Original Gotenberg HTML conversion endpoint is
|
||||
// -> /convert/html
|
||||
// But your reverse proxy uses location
|
||||
// -> /hidden/html/conversion
|
||||
const toPDF = pipe(
|
||||
gotenberg('http://localhost:3000'),
|
||||
convert,
|
||||
html,
|
||||
adjust({ url: '/hidden/html/conversion' }),
|
||||
please
|
||||
)
|
||||
```
|
||||
|
||||
But, using that function, remember about Peter Parker principle:
|
||||
> "With great power comes great responsibility"
|
||||
|
||||
## Bonus
|
||||
|
||||
If you happen to use this package from JavaScript, you will, obviously, lost type safety, but in return, you can use [proposed pipe operator](https://github.com/tc39/proposal-pipeline-operator) (with [Babel plugin](https://babeljs.io/docs/en/babel-plugin-proposal-pipeline-operator)), to get beauty like this:
|
||||
|
||||
+13
-13
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "gotenberg-js-client",
|
||||
"version": "0.6.2",
|
||||
"version": "0.7.0",
|
||||
"description": "A simple JS/TS for interacting with a Gotenberg API",
|
||||
"author": "Victor Didenko <yumaa.verdin@gmail.com> (https://yumaa.name)",
|
||||
"license": "MIT",
|
||||
@@ -25,7 +25,7 @@
|
||||
{
|
||||
"path": "pkg/dist-node/index.js",
|
||||
"webpack": false,
|
||||
"limit": "4106 B"
|
||||
"limit": "4216 B"
|
||||
}
|
||||
],
|
||||
"@pika/pack": {
|
||||
@@ -69,23 +69,23 @@
|
||||
"@pika/pack": "^0.5.0",
|
||||
"@pika/plugin-build-node": "^0.9.2",
|
||||
"@pika/plugin-ts-standard-pkg": "^0.9.2",
|
||||
"@size-limit/preset-small-lib": "^4.5.7",
|
||||
"@types/jest": "^26.0.13",
|
||||
"@types/node": "^14.6.4",
|
||||
"flowgen": "^1.11.0",
|
||||
"jest": "^26.4.2",
|
||||
"nock": "^13.0.4",
|
||||
"@size-limit/preset-small-lib": "^4.9.2",
|
||||
"@types/jest": "^26.0.20",
|
||||
"@types/node": "^14.14.25",
|
||||
"flowgen": "^1.13.0",
|
||||
"jest": "^26.6.3",
|
||||
"nock": "^13.0.7",
|
||||
"pika-plugin-package.json": "^1.0.2",
|
||||
"pika-plugin-typedefs-to-flow": "^0.0.2",
|
||||
"prettier": "^2.1.1",
|
||||
"size-limit": "^4.5.7",
|
||||
"ts-jest": "^26.3.0",
|
||||
"ts-node": "^9.0.0",
|
||||
"prettier": "^2.2.1",
|
||||
"size-limit": "^4.9.2",
|
||||
"ts-jest": "^26.5.0",
|
||||
"ts-node": "^9.1.1",
|
||||
"tslint": "^6.1.3",
|
||||
"tslint-config-prettier": "^1.18.0",
|
||||
"tslint-config-security": "^1.16.0",
|
||||
"tslint-config-standard-plus": "^2.3.0",
|
||||
"typescript": "^4.0.2",
|
||||
"typescript": "^4.1.3",
|
||||
"yaspeller": "^7.0.0"
|
||||
},
|
||||
"engines": {
|
||||
|
||||
+1
-4
@@ -110,11 +110,8 @@ export type OfficeRequestFields = {
|
||||
// Attention: when converting a website to PDF, you should remove all margins
|
||||
// If not, some of the content of the page might be hidden
|
||||
// https://thecodingmachine.github.io/gotenberg/#url
|
||||
export type UrlRequestFields = {
|
||||
export type UrlRequestFields = HtmlRequestFields & {
|
||||
remoteURL?: string
|
||||
|
||||
// https://thecodingmachine.github.io/gotenberg/#html.paper_size_margins_orientation_scaling
|
||||
scale?: number
|
||||
}
|
||||
|
||||
// merge conversion doesn't have any form fields
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
import { Request } from './_types'
|
||||
|
||||
/**
|
||||
* Recursively merge requests
|
||||
*/
|
||||
const merge = <RequestEx extends Request>(request: RequestEx, modify: Partial<Request>) => {
|
||||
const result = { ...request, ...modify }
|
||||
|
||||
for (const key in modify) {
|
||||
if (
|
||||
modify[key] &&
|
||||
request[key] &&
|
||||
typeof modify[key] === 'object' &&
|
||||
typeof request[key] === 'object'
|
||||
) {
|
||||
result[key] = merge(request[key], modify[key])
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* Adjust any Request *object* fields, for any request
|
||||
* @return new typed Request, doesn't modify original Request
|
||||
*/
|
||||
export const adjust: {
|
||||
<RequestEx extends Request>(modify: Partial<Request>): (request: RequestEx) => RequestEx
|
||||
} = (modify) => (request) => merge(request, modify)
|
||||
+5
-1
@@ -26,8 +26,12 @@ export function post(
|
||||
return new Promise((resolve, reject) => {
|
||||
const req = request(_url, {
|
||||
method: 'POST',
|
||||
headers: { ...data.getHeaders(), ...headers },
|
||||
...this, // extends with config options
|
||||
headers: {
|
||||
...data.getHeaders(),
|
||||
...headers,
|
||||
...(this ? (this as any).headers : null), // extends with config headers
|
||||
},
|
||||
})
|
||||
|
||||
req.on('error', reject)
|
||||
|
||||
@@ -12,6 +12,7 @@ export { html } from './html'
|
||||
export { url } from './url'
|
||||
|
||||
// export modifiers functions and constants
|
||||
export { adjust } from './adjust'
|
||||
export { add } from './add'
|
||||
export { set } from './set'
|
||||
export { to } from './to'
|
||||
|
||||
@@ -1,24 +1,14 @@
|
||||
import {
|
||||
ConversionOptions,
|
||||
HtmlRequest,
|
||||
MarginOptions,
|
||||
MarkdownRequest,
|
||||
OfficeRequest,
|
||||
PaperOptions,
|
||||
RequestFields,
|
||||
} from './_types'
|
||||
import { ConversionOptions, MarginOptions, PaperOptions, Request, RequestFields } from './_types'
|
||||
import { fields } from './internal/fields'
|
||||
import { marginSizes, paperSize } from './to-helpers'
|
||||
|
||||
/**
|
||||
* Adjust Request fields, for html, markdown or office requests
|
||||
* @return new Request (Html|Markdown|Office), doesn't modify original Request
|
||||
* Adjust Request fields, for any request
|
||||
* @return new typed Request, doesn't modify original Request
|
||||
*/
|
||||
export const to: {
|
||||
(...opts: ConversionOptions[]): (request: HtmlRequest) => HtmlRequest
|
||||
(...opts: ConversionOptions[]): (request: OfficeRequest) => OfficeRequest
|
||||
(...opts: ConversionOptions[]): (request: MarkdownRequest) => MarkdownRequest
|
||||
} = (...opts: ConversionOptions[]): any => {
|
||||
<RequestEx extends Request>(...opts: ConversionOptions[]): (request: RequestEx) => RequestEx
|
||||
} = (...opts): any => {
|
||||
const options: RequestFields = {}
|
||||
|
||||
// page size and margins options
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@ import { type } from './internal/type'
|
||||
|
||||
/**
|
||||
* Adjust Request url, by adding `/url` to it; Set `remoteURL` from source
|
||||
* @return new HtmlRequest, doesn't modify original Request
|
||||
* @return new UrlRequest, doesn't modify original Request
|
||||
*/
|
||||
export const url: {
|
||||
(request: Request): UrlRequest
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
import { adjust, Request, RequestType } from '../src'
|
||||
|
||||
// dumb object to test purity
|
||||
const dumb: Request = {
|
||||
type: RequestType.Undefined,
|
||||
url: 'test',
|
||||
fields: {
|
||||
scale: 1,
|
||||
landscape: true,
|
||||
pageRanges: '1-2',
|
||||
},
|
||||
client: {
|
||||
post: () => {
|
||||
throw new Error('not implemented')
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
test('Should adjust flat fields', () => {
|
||||
expect(adjust({})(dumb)).toEqual({ ...dumb })
|
||||
expect(adjust({ url: 'changed' })(dumb)).toEqual({ ...dumb, url: 'changed' })
|
||||
})
|
||||
|
||||
test('Should adjust deep fields', () => {
|
||||
expect(adjust({ fields: { landscape: false } })(dumb)).toEqual({
|
||||
...dumb,
|
||||
fields: {
|
||||
...dumb.fields,
|
||||
landscape: false,
|
||||
},
|
||||
})
|
||||
expect(adjust({ headers: { Authorization: 'Bearer token' } })(dumb)).toEqual({
|
||||
...dumb,
|
||||
headers: {
|
||||
Authorization: 'Bearer token',
|
||||
},
|
||||
})
|
||||
expect(
|
||||
adjust({ headers: { Authorization: 'Bearer token' } })({
|
||||
...dumb,
|
||||
headers: { 'X-Header': 'test' },
|
||||
})
|
||||
).toEqual({
|
||||
...dumb,
|
||||
headers: {
|
||||
Authorization: 'Bearer token',
|
||||
'X-Header': 'test',
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
test('Should replace deep fields', () => {
|
||||
expect(
|
||||
adjust({ headers: { Authorization: 'Bearer token' } })({
|
||||
...dumb,
|
||||
headers: { Authorization: 'Basic dXNlcjpwYXNzd29yZA==' },
|
||||
})
|
||||
).toEqual({ ...dumb, headers: { Authorization: 'Bearer token' } })
|
||||
})
|
||||
@@ -2,7 +2,7 @@ import nock from 'nock'
|
||||
import FormData from 'form-data'
|
||||
import { client } from '../../src/client/node'
|
||||
|
||||
// Helper function to get response JOSN body
|
||||
// Helper function to get response JSON body
|
||||
async function toJSON(response: any) {
|
||||
const chunks: any[] = []
|
||||
const text = await new Promise<string>((resolve, reject) => {
|
||||
@@ -78,3 +78,39 @@ test('Should handle http', async () => {
|
||||
const response = await clnt.get!('http://127.0.0.1:3000/ping')
|
||||
expect(await toJSON(response)).toEqual({ status: 'OK' })
|
||||
})
|
||||
|
||||
test('Should merge http.request options with config', async () => {
|
||||
let basicAuthHeader: string | null = null
|
||||
|
||||
nock('https://127.0.0.1:3000') //
|
||||
.post('/convert/html')
|
||||
.reply(200, function () {
|
||||
if (this.req.headers && this.req.headers.authorization) {
|
||||
basicAuthHeader = this.req.headers.authorization
|
||||
}
|
||||
return { status: 'OK' }
|
||||
})
|
||||
|
||||
const clnt = client({ auth: 'user:password' })
|
||||
const response = await clnt.post('https://127.0.0.1:3000/convert/html', new FormData())
|
||||
expect(await toJSON(response)).toEqual({ status: 'OK' })
|
||||
expect(basicAuthHeader).toEqual('Basic dXNlcjpwYXNzd29yZA==')
|
||||
})
|
||||
|
||||
test('Should merge headers with config', async () => {
|
||||
let tokenAuthHeader: string | null = null
|
||||
|
||||
nock('https://127.0.0.1:3000') //
|
||||
.post('/convert/html')
|
||||
.reply(200, function () {
|
||||
if (this.req.headers && this.req.headers.authorization) {
|
||||
tokenAuthHeader = this.req.headers.authorization
|
||||
}
|
||||
return { status: 'OK' }
|
||||
})
|
||||
|
||||
const clnt = client({ headers: { Authorization: 'Bearer token' } })
|
||||
const response = await clnt.post('https://127.0.0.1:3000/convert/html', new FormData())
|
||||
expect(await toJSON(response)).toEqual({ status: 'OK' })
|
||||
expect(tokenAuthHeader).toEqual('Bearer token')
|
||||
})
|
||||
|
||||
Binary file not shown.
@@ -0,0 +1,15 @@
|
||||
import { createWriteStream } from 'fs'
|
||||
import { convert, gotenberg, landscape, pipe, please, to, url } from '../../src'
|
||||
|
||||
// need to run Gotenberg like this
|
||||
// docker run --rm -p 3500:3000 thecodingmachine/gotenberg:6
|
||||
|
||||
pipe(
|
||||
gotenberg('http://localhost:3500'),
|
||||
convert,
|
||||
url,
|
||||
to(landscape),
|
||||
please
|
||||
)('https://google.com')
|
||||
.then((pdf) => pdf.pipe(createWriteStream(`${__dirname}/google.pdf`)))
|
||||
.catch(console.error)
|
||||
@@ -0,0 +1,8 @@
|
||||
const { gotenberg, pipe, ping, please } = require('../../pkg')
|
||||
|
||||
// need to run Gotenberg like this
|
||||
// docker run --rm -p 3500:3000 thecodingmachine/gotenberg:6
|
||||
|
||||
pipe(gotenberg('http://localhost:3500'), ping, please)()
|
||||
.then(() => console.log('Gotenberg is up'))
|
||||
.catch((error) => console.error('Gotenberg is down:', error))
|
||||
@@ -0,0 +1,31 @@
|
||||
import { gotenberg, pipe, ping, please } from '../../src'
|
||||
|
||||
// need to run Gotenberg like this
|
||||
// docker run --rm -p 3500:3000 thecodingmachine/gotenberg:6
|
||||
|
||||
// pipe(
|
||||
// gotenberg('http://localhost:3000'),
|
||||
// ping,
|
||||
// please
|
||||
// )({}) // <- empty source object to satisfy typings
|
||||
// .then(() => console.log('Gotenberg is up'))
|
||||
// .catch((error) => console.error('Gotenberg is down:', error))
|
||||
|
||||
// ;(async () => {
|
||||
// try {
|
||||
// await pipe(gotenberg('http://localhost:3500'), ping, please)({})
|
||||
// console.log('Gotenberg is up')
|
||||
// } catch (error) {
|
||||
// console.error('Gotenberg is down:', error)
|
||||
// }
|
||||
// })()
|
||||
|
||||
//
|
||||
;(async () => {
|
||||
try {
|
||||
await please(ping(gotenberg('http://localhost:3500')({})))
|
||||
console.log('Gotenberg is up')
|
||||
} catch (error) {
|
||||
console.error('Gotenberg is down:', error)
|
||||
}
|
||||
})()
|
||||
+2
-2
@@ -13,6 +13,6 @@
|
||||
"declaration": true,
|
||||
"baseUrl": "./src"
|
||||
},
|
||||
"include": ["src/**/*"],
|
||||
"exclude": ["**/*.spec.ts"]
|
||||
"include": ["src"],
|
||||
"exclude": ["**/node_modules", "**/.*/", "**/*.spec.ts"]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user