Compare commits

...

9 Commits

Author SHA1 Message Date
yumauri 38a379afe7 v0.7.0 2021-02-07 20:43:33 +03:00
yumauri 65abf280a8 Introduce adjust function (issue #26) 2021-02-07 20:38:49 +03:00
yumauri 70885ee8d0 Fix headers merging (issue #26) 2021-02-07 19:20:11 +03:00
yumauri cd08c5e860 Update dependencies 2021-02-07 15:07:55 +03:00
yumauri 6cc5e292ad v0.6.3 2021-01-26 19:53:29 +03:00
yumauri b863239514 Update dependencies 2021-01-26 19:49:48 +03:00
yumauri 3be519955b Extend url typings (fix issue #25) 2021-01-26 19:44:28 +03:00
yumauri 51dcdc83a9 Update dependencies 2020-12-12 14:48:49 +03:00
yumauri 5cbd477051 Add manual ping tests 2020-12-12 12:27:25 +03:00
16 changed files with 1871 additions and 1988 deletions
+25
View File
@@ -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
View File
@@ -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
View File
@@ -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
+29
View File
@@ -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
View File
@@ -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)
+1
View File
@@ -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'
+5 -15
View File
@@ -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
View File
@@ -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
+59
View File
@@ -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' } })
})
+37 -1
View File
@@ -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.
+15
View File
@@ -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)
+8
View File
@@ -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))
+31
View File
@@ -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
View File
@@ -13,6 +13,6 @@
"declaration": true,
"baseUrl": "./src"
},
"include": ["src/**/*"],
"exclude": ["**/*.spec.ts"]
"include": ["src"],
"exclude": ["**/node_modules", "**/.*/", "**/*.spec.ts"]
}
+1639 -1951
View File
File diff suppressed because it is too large Load Diff