mirror of
https://github.com/brianc/node-postgres.git
synced 2026-01-18 15:55:05 +00:00
feat(pg-connection-string): ClientConfig helper functions
Two new functions are introduced to make it easy for TypeScript users to use a PostgresSQL connection string with pg Client. Fixes #2280
This commit is contained in:
parent
a9fd34fb42
commit
5a8b1a7d24
@ -35,6 +35,27 @@ The resulting config contains a subset of the following properties:
|
||||
* `ca`
|
||||
* any other query parameters (for example, `application_name`) are preserved intact.
|
||||
|
||||
### ClientConfig Compatibility for TypeScript
|
||||
|
||||
The pg-connection-string `ConnectionOptions` interface is not compatible with the `ClientConfig` interface that [pg.Client](https://node-postgres.com/apis/client) expects. To remedy this, use the `parseIntoClientConfig` function instead of `parse`:
|
||||
|
||||
```ts
|
||||
import { ClientConfig } from 'pg';
|
||||
import { parseIntoClientConfig } from 'pg-connection-string';
|
||||
|
||||
const config: ClientConfig = parseIntoClientConfig('postgres://someuser:somepassword@somehost:381/somedatabase')
|
||||
```
|
||||
|
||||
You can also use `toClientConfig` to convert an existing `ConnectionOptions` interface into a `ClientConfig` interface:
|
||||
|
||||
```ts
|
||||
import { ClientConfig } from 'pg';
|
||||
import { parse, toClientConfig } from 'pg-connection-string';
|
||||
|
||||
const config = parse('postgres://someuser:somepassword@somehost:381/somedatabase')
|
||||
const clientConfig: ClientConfig = toClientConfig(config)
|
||||
```
|
||||
|
||||
## Connection Strings
|
||||
|
||||
The short summary of acceptable URLs is:
|
||||
|
||||
5
packages/pg-connection-string/index.d.ts
vendored
5
packages/pg-connection-string/index.d.ts
vendored
@ -1,3 +1,5 @@
|
||||
import { ClientConfig } from 'pg'
|
||||
|
||||
export function parse(connectionString: string): ConnectionOptions
|
||||
|
||||
export interface ConnectionOptions {
|
||||
@ -13,3 +15,6 @@ export interface ConnectionOptions {
|
||||
fallback_application_name?: string
|
||||
options?: string
|
||||
}
|
||||
|
||||
export function toClientConfig(config: ConnectionOptions): ClientConfig
|
||||
export function parseIntoClientConfig(connectionString: string): ClientConfig
|
||||
|
||||
@ -107,6 +107,68 @@ function parse(str) {
|
||||
return config
|
||||
}
|
||||
|
||||
// convert pg-connection-string ssl config to a ClientConfig.ConnectionOptions
|
||||
function toConnectionOptions(sslConfig) {
|
||||
const connectionOptions = Object.entries(sslConfig).reduce((c, [key, value]) => {
|
||||
// we explicitly check for undefined and null instead of `if (value)` because some
|
||||
// options accept falsy values. Example: `ssl.rejectUnauthorized = false`
|
||||
if (value !== undefined && value !== null) {
|
||||
c[key] = value
|
||||
}
|
||||
|
||||
return c
|
||||
}, {})
|
||||
|
||||
return connectionOptions
|
||||
}
|
||||
|
||||
// convert pg-connection-string config to a ClientConfig
|
||||
function toClientConfig(config) {
|
||||
const poolConfig = Object.entries(config).reduce((c, [key, value]) => {
|
||||
if (key === 'ssl') {
|
||||
const sslConfig = value
|
||||
|
||||
if (typeof sslConfig === 'boolean') {
|
||||
c[key] = sslConfig
|
||||
}
|
||||
// else path is taken. multiple tests produce a sslConfig that is an object
|
||||
// and we can console.log to see that we take this path
|
||||
//
|
||||
// see https://github.com/istanbuljs/babel-plugin-istanbul/issues/186#issuecomment-1137765139
|
||||
// istanbul ignore else
|
||||
else if (typeof sslConfig === 'object') {
|
||||
c[key] = toConnectionOptions(sslConfig)
|
||||
}
|
||||
} else if (value !== undefined && value !== null) {
|
||||
if (key === 'port') {
|
||||
// when port is not specified, it is converted into an empty string
|
||||
// we want to avoid NaN or empty string as a values in ClientConfig
|
||||
if (value !== '') {
|
||||
const v = parseInt(value, 10)
|
||||
if (isNaN(v)) {
|
||||
throw new Error(`Invalid ${key}: ${value}`)
|
||||
}
|
||||
|
||||
c[key] = v
|
||||
}
|
||||
} else {
|
||||
c[key] = value
|
||||
}
|
||||
}
|
||||
|
||||
return c
|
||||
}, {})
|
||||
|
||||
return poolConfig
|
||||
}
|
||||
|
||||
// parses a connection string into ClientConfig
|
||||
function parseIntoClientConfig(str) {
|
||||
return toClientConfig(parse(str))
|
||||
}
|
||||
|
||||
module.exports = parse
|
||||
|
||||
parse.parse = parse
|
||||
parse.toClientConfig = toClientConfig
|
||||
parse.parseIntoClientConfig = parseIntoClientConfig
|
||||
|
||||
125
packages/pg-connection-string/test/clientConfig.js
Normal file
125
packages/pg-connection-string/test/clientConfig.js
Normal file
@ -0,0 +1,125 @@
|
||||
'use strict'
|
||||
|
||||
const chai = require('chai')
|
||||
const expect = chai.expect
|
||||
chai.should()
|
||||
|
||||
const { parse, toClientConfig, parseIntoClientConfig } = require('../')
|
||||
|
||||
describe('toClientConfig', function () {
|
||||
it('converts connection info', function () {
|
||||
const config = parse('postgres://brian:pw@boom:381/lala')
|
||||
const clientConfig = toClientConfig(config)
|
||||
|
||||
clientConfig.user.should.equal('brian')
|
||||
clientConfig.password.should.equal('pw')
|
||||
clientConfig.host.should.equal('boom')
|
||||
clientConfig.port.should.equal(381)
|
||||
clientConfig.database.should.equal('lala')
|
||||
})
|
||||
|
||||
it('converts query params', function () {
|
||||
const config = parse(
|
||||
'postgres:///?application_name=TheApp&fallback_application_name=TheAppFallback&client_encoding=utf8&options=-c geqo=off'
|
||||
)
|
||||
const clientConfig = toClientConfig(config)
|
||||
|
||||
clientConfig.application_name.should.equal('TheApp')
|
||||
clientConfig.fallback_application_name.should.equal('TheAppFallback')
|
||||
clientConfig.client_encoding.should.equal('utf8')
|
||||
clientConfig.options.should.equal('-c geqo=off')
|
||||
})
|
||||
|
||||
it('converts SSL boolean', function () {
|
||||
const config = parse('pg:///?ssl=true')
|
||||
const clientConfig = toClientConfig(config)
|
||||
|
||||
clientConfig.ssl.should.equal(true)
|
||||
})
|
||||
|
||||
it('converts sslmode=disable', function () {
|
||||
const config = parse('pg:///?sslmode=disable')
|
||||
const clientConfig = toClientConfig(config)
|
||||
|
||||
clientConfig.ssl.should.equal(false)
|
||||
})
|
||||
|
||||
it('converts sslmode=noverify', function () {
|
||||
const config = parse('pg:///?sslmode=no-verify')
|
||||
const clientConfig = toClientConfig(config)
|
||||
|
||||
clientConfig.ssl.rejectUnauthorized.should.equal(false)
|
||||
})
|
||||
|
||||
it('converts other sslmode options', function () {
|
||||
const config = parse('pg:///?sslmode=verify-ca')
|
||||
const clientConfig = toClientConfig(config)
|
||||
|
||||
clientConfig.ssl.should.deep.equal({})
|
||||
})
|
||||
|
||||
it('converts other sslmode options', function () {
|
||||
const config = parse('pg:///?sslmode=verify-ca')
|
||||
const clientConfig = toClientConfig(config)
|
||||
|
||||
clientConfig.ssl.should.deep.equal({})
|
||||
})
|
||||
|
||||
it('converts ssl cert options', function () {
|
||||
const connectionString =
|
||||
'pg:///?sslcert=' +
|
||||
__dirname +
|
||||
'/example.cert&sslkey=' +
|
||||
__dirname +
|
||||
'/example.key&sslrootcert=' +
|
||||
__dirname +
|
||||
'/example.ca'
|
||||
const config = parse(connectionString)
|
||||
const clientConfig = toClientConfig(config)
|
||||
|
||||
clientConfig.ssl.should.deep.equal({
|
||||
ca: 'example ca\n',
|
||||
cert: 'example cert\n',
|
||||
key: 'example key\n',
|
||||
})
|
||||
})
|
||||
|
||||
it('converts unix domain sockets', function () {
|
||||
const config = parse('socket:/some path/?db=my[db]&encoding=utf8&client_encoding=bogus')
|
||||
const clientConfig = toClientConfig(config)
|
||||
clientConfig.host.should.equal('/some path/')
|
||||
clientConfig.database.should.equal('my[db]', 'must to be escaped and unescaped through "my%5Bdb%5D"')
|
||||
clientConfig.client_encoding.should.equal('utf8')
|
||||
})
|
||||
|
||||
it('handles invalid port', function () {
|
||||
const config = parse('postgres://@boom:381/lala')
|
||||
config.port = 'bogus'
|
||||
expect(() => toClientConfig(config)).to.throw()
|
||||
})
|
||||
|
||||
it('handles invalid sslconfig values', function () {
|
||||
const config = parse('postgres://@boom/lala')
|
||||
config.ssl = {}
|
||||
config.ssl.cert = null
|
||||
config.ssl.key = undefined
|
||||
|
||||
const clientConfig = toClientConfig(config)
|
||||
|
||||
clientConfig.host.should.equal('boom')
|
||||
clientConfig.database.should.equal('lala')
|
||||
clientConfig.ssl.should.deep.equal({})
|
||||
})
|
||||
})
|
||||
|
||||
describe('parseIntoClientConfig', function () {
|
||||
it('converts url', function () {
|
||||
const clientConfig = parseIntoClientConfig('postgres://brian:pw@boom:381/lala')
|
||||
|
||||
clientConfig.user.should.equal('brian')
|
||||
clientConfig.password.should.equal('pw')
|
||||
clientConfig.host.should.equal('boom')
|
||||
clientConfig.port.should.equal(381)
|
||||
clientConfig.database.should.equal('lala')
|
||||
})
|
||||
})
|
||||
Loading…
x
Reference in New Issue
Block a user