feat: add env and config-file sources
This commit is contained in:
@@ -0,0 +1,47 @@
|
|||||||
|
import { existsSync, readFileSync } from 'node:fs'
|
||||||
|
import { parseConnections } from './store.js'
|
||||||
|
import type { ConnectionConfig } from './types.js'
|
||||||
|
|
||||||
|
export const readEnvConnections = (env: NodeJS.ProcessEnv = process.env): ConnectionConfig[] => {
|
||||||
|
const raw = env.DBMOLE_CONNECTIONS
|
||||||
|
if (!raw) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
let parsed: unknown
|
||||||
|
try {
|
||||||
|
parsed = JSON.parse(raw)
|
||||||
|
} catch {
|
||||||
|
console.error('dbmole: DBMOLE_CONNECTIONS is not valid JSON, ignoring')
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
if (!Array.isArray(parsed)) {
|
||||||
|
console.error('dbmole: DBMOLE_CONNECTIONS must be a JSON array, ignoring')
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
return parseConnections(parsed, 'env DBMOLE_CONNECTIONS')
|
||||||
|
}
|
||||||
|
|
||||||
|
export const readConfigFile = (path: string | undefined): ConnectionConfig[] => {
|
||||||
|
if (!path) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
if (!existsSync(path)) {
|
||||||
|
console.error('dbmole: config file ' + path + ' not found, ignoring')
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
let parsed: unknown
|
||||||
|
try {
|
||||||
|
parsed = JSON.parse(readFileSync(path, 'utf8'))
|
||||||
|
} catch {
|
||||||
|
console.error('dbmole: config file ' + path + ' is not valid JSON, ignoring')
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
const connections = (parsed as { connections?: unknown }).connections
|
||||||
|
if (!Array.isArray(connections)) {
|
||||||
|
console.error(
|
||||||
|
'dbmole: config file ' + path + ' must contain { "connections": [...] }, ignoring'
|
||||||
|
)
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
return parseConnections(connections, 'config ' + path)
|
||||||
|
}
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
import { mkdtempSync, rmSync, writeFileSync } from 'node:fs'
|
||||||
|
import { tmpdir } from 'node:os'
|
||||||
|
import { join } from 'node:path'
|
||||||
|
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||||
|
import { readConfigFile, readEnvConnections } from '../../../src/config/sources.js'
|
||||||
|
|
||||||
|
const valid = { name: 'env-pg', type: 'postgres', host: 'h', user: 'u' }
|
||||||
|
|
||||||
|
describe('readEnvConnections', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
vi.spyOn(console, 'error').mockImplementation(() => {})
|
||||||
|
})
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
vi.restoreAllMocks()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('returns empty when unset', () => {
|
||||||
|
expect(readEnvConnections({})).toEqual([])
|
||||||
|
})
|
||||||
|
|
||||||
|
it('parses a JSON array of connections', () => {
|
||||||
|
const env = { DBMOLE_CONNECTIONS: JSON.stringify([valid]) }
|
||||||
|
expect(readEnvConnections(env).map((c) => c.name)).toEqual(['env-pg'])
|
||||||
|
})
|
||||||
|
|
||||||
|
it('ignores invalid JSON with a warning', () => {
|
||||||
|
expect(readEnvConnections({ DBMOLE_CONNECTIONS: 'nope{' })).toEqual([])
|
||||||
|
expect(console.error).toHaveBeenCalled()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('ignores non-array JSON with a warning', () => {
|
||||||
|
expect(readEnvConnections({ DBMOLE_CONNECTIONS: '{}' })).toEqual([])
|
||||||
|
expect(console.error).toHaveBeenCalled()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('readConfigFile', () => {
|
||||||
|
let dir: string
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
dir = mkdtempSync(join(tmpdir(), 'dbmole-config-'))
|
||||||
|
vi.spyOn(console, 'error').mockImplementation(() => {})
|
||||||
|
})
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
rmSync(dir, { recursive: true, force: true })
|
||||||
|
vi.restoreAllMocks()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('returns empty when path is undefined', () => {
|
||||||
|
expect(readConfigFile(undefined)).toEqual([])
|
||||||
|
})
|
||||||
|
|
||||||
|
it('warns and returns empty for a missing file', () => {
|
||||||
|
expect(readConfigFile(join(dir, 'absent.json'))).toEqual([])
|
||||||
|
expect(console.error).toHaveBeenCalled()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('reads { connections: [...] } shape', () => {
|
||||||
|
const path = join(dir, 'config.json')
|
||||||
|
writeFileSync(path, JSON.stringify({ connections: [valid] }))
|
||||||
|
expect(readConfigFile(path).map((c) => c.name)).toEqual(['env-pg'])
|
||||||
|
})
|
||||||
|
|
||||||
|
it('warns on wrong shape', () => {
|
||||||
|
const path = join(dir, 'config.json')
|
||||||
|
writeFileSync(path, JSON.stringify([valid]))
|
||||||
|
expect(readConfigFile(path)).toEqual([])
|
||||||
|
expect(console.error).toHaveBeenCalled()
|
||||||
|
})
|
||||||
|
})
|
||||||
Reference in New Issue
Block a user