Skip to content
This repository has been archived by the owner on Apr 26, 2024. It is now read-only.

Commit

Permalink
Add the ability to pass multiple env keys (#159)
Browse files Browse the repository at this point in the history
* Add the ability to pass multiple env keys

* Fix linter errors
  • Loading branch information
sz-piotr authored Mar 19, 2024
1 parent 3ea4a9d commit c7ec600
Show file tree
Hide file tree
Showing 4 changed files with 70 additions and 20 deletions.
6 changes: 6 additions & 0 deletions packages/backend-tools/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# @l2beat/backend-tools

## 0.5.2

### Patch Changes

- Add the ability to pass multiple env variable keys

## 0.5.1

### Patch Changes
Expand Down
2 changes: 1 addition & 1 deletion packages/backend-tools/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@l2beat/backend-tools",
"description": "Common utilities for L2BEAT projects.",
"version": "0.5.1",
"version": "0.5.2",
"license": "MIT",
"repository": "https://github.com/l2beat/tools",
"bugs": {
Expand Down
18 changes: 18 additions & 0 deletions packages/backend-tools/src/env.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@ describe(Env.name, () => {
const env = new Env({})
expect(() => env.string('TEST_A')).toThrow()
})

it('supports array keys', () => {
const env = new Env({ TEST_A: 'foo', TEST_C: 'bar' })
expect(env.string(['TEST_B', 'TEST_A'])).toEqual('foo')
expect(env.string(['TEST_C', 'TEST_A'])).toEqual('bar')
})
})

describe(Env.prototype.integer.name, () => {
Expand All @@ -44,6 +50,12 @@ describe(Env.name, () => {
const env = new Env({ TEST_A: 'foo' })
expect(() => env.integer('TEST_A')).toThrow()
})

it('supports array keys', () => {
const env = new Env({ TEST_A: '69', TEST_C: '-420' })
expect(env.integer(['TEST_B', 'TEST_A'])).toEqual(69)
expect(env.integer(['TEST_C', 'TEST_A'])).toEqual(-420)
})
})

describe(Env.prototype.boolean.name, () => {
Expand All @@ -68,5 +80,11 @@ describe(Env.name, () => {
const env = new Env({ TEST_A: '69' })
expect(() => env.boolean('TEST_A')).toThrow()
})

it('supports array keys', () => {
const env = new Env({ TEST_A: 'true', TEST_C: 'false' })
expect(env.boolean(['TEST_B', 'TEST_A'])).toEqual(true)
expect(env.boolean(['TEST_C', 'TEST_A'])).toEqual(false)
})
})
})
64 changes: 45 additions & 19 deletions packages/backend-tools/src/env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,66 +8,92 @@ export function getEnv(): Env {
export class Env {
constructor(private readonly env: Record<string, string | undefined>) {}

string(key: string, fallback?: string): string {
private resolve(
key: string | string[],
): { value: string; key: string } | undefined {
if (Array.isArray(key)) {
for (const k of key) {
const value = this.env[k]
if (value !== undefined) {
return { value, key: k }
}
}
return undefined
}
const value = this.env[key]
return value !== undefined ? { value, key } : value
}

string(key: string | string[], fallback?: string): string {
const value = this.optionalString(key)
if (value !== undefined) {
return value
}
if (fallback !== undefined) {
return fallback
}
throw new Error(`Missing environment variable ${key}!`)
throwMissingEnvVar(key)
}

optionalString(key: string): string | undefined {
return this.env[key]
optionalString(key: string | string[]): string | undefined {
return this.resolve(key)?.value
}

integer(key: string, fallback?: number): number {
integer(key: string | string[], fallback?: number): number {
const value = this.optionalInteger(key)
if (value !== undefined) {
return value
}
if (fallback !== undefined) {
return fallback
}
throw new Error(`Missing environment variable ${key}!`)
throwMissingEnvVar(key)
}

optionalInteger(key: string): number | undefined {
const value = this.env[key]
if (value !== undefined) {
const result = parseInt(value)
if (result.toString() === value) {
optionalInteger(key: string | string[]): number | undefined {
const resolved = this.resolve(key)
if (resolved) {
const result = parseInt(resolved.value)
if (result.toString() === resolved.value) {
return result
}
throw new Error(`Environment variable ${key} is not an integer!`)
throw new Error(`Environment variable ${resolved.key} is not an integer!`)
}
}

boolean(key: string, fallback?: boolean): boolean {
boolean(key: string | string[], fallback?: boolean): boolean {
const value = this.optionalBoolean(key)
if (value !== undefined) {
return value
}
if (fallback !== undefined) {
return fallback
}
throw new Error(`Missing environment variable ${key}!`)
throwMissingEnvVar(key)
}

optionalBoolean(key: string): boolean | undefined {
const value = this.env[key]
if (value !== undefined) {
const lowerCased = value.toLowerCase()
optionalBoolean(key: string | string[]): boolean | undefined {
const resolved = this.resolve(key)
if (resolved) {
const lowerCased = resolved.value.toLowerCase()

const trueValues = ['true', 'yes', '1']
const falseValues = ['false', 'no', '0']

if (trueValues.includes(lowerCased)) return true
if (falseValues.includes(lowerCased)) return false

throw new Error(`Environment variable ${key} is not a boolean value!`)
throw new Error(
`Environment variable ${resolved.key} is not a boolean value!`,
)
}
}
}

function throwMissingEnvVar(keys: string | string[]): never {
if (Array.isArray(keys)) {
throw new Error(`Missing environment variables: ${keys.join(', ')}!`)
} else {
throw new Error(`Missing environment variable: ${keys}!`)
}
}

0 comments on commit c7ec600

Please sign in to comment.