Commit 8b7cdfe3 authored by mohoff's avatar mohoff Committed by Richard Crosby

chore: renamed Parameters to Options

parent d8d52899
jest.mock('./load')
jest.mock('./validate')
jest.mock('./params/validate')
jest.mock('./options/validate')
import { load } from './load'
import { validate } from './validate'
import { validate as validateParams } from './params/validate'
import { defaultParameters } from './params'
import { validate as validateOptions } from './options/validate'
import { defaultOptions } from './options'
const mockedLoad = load as jest.MockedFunction<typeof load>
const mockedValidate = validate as jest.MockedFunction<typeof validate>
const mockedValidateParams = validateParams as jest.MockedFunction<
typeof validateParams
const mockedValidateOptions = validateOptions as jest.MockedFunction<
typeof validateOptions
>
const runtimeEnv = process.env.NODE_ENV || 'test'
const mockedConfig = { some: 'config', runtimeEnv }
const mockedParameters = defaultParameters
const mockedOptions = defaultOptions
const mockedConfigPath = 'some/config/path'
const mockedValidationResult = true
mockedLoad.mockReturnValue(mockedConfig)
mockedValidate.mockReturnValue(mockedValidationResult)
mockedValidateParams.mockReturnValue(mockedParameters)
mockedValidateOptions.mockReturnValue(mockedOptions)
import StrongConfig from '.'
......@@ -34,20 +34,20 @@ describe('StrongConfig class', () => {
expect(new StrongConfig()).toBeDefined()
})
it('can be instantiated with a parameters object', () => {
expect(new StrongConfig(mockedParameters)).toBeDefined()
it('can be instantiated with a options object', () => {
expect(new StrongConfig(mockedOptions)).toBeDefined()
})
it('validates the parameters object', () => {
new StrongConfig(mockedParameters)
it('validates the options object', () => {
new StrongConfig(mockedOptions)
expect(mockedValidateParams).toHaveBeenCalledWith(mockedParameters)
expect(mockedValidateOptions).toHaveBeenCalledWith(mockedOptions)
})
it('stores the validated parameters object', () => {
const strongConfig = new StrongConfig(mockedParameters)
it('stores the validated options object', () => {
const strongConfig = new StrongConfig(mockedOptions)
expect(strongConfig.parameters).toStrictEqual(mockedParameters)
expect(strongConfig.options).toStrictEqual(mockedOptions)
})
})
......@@ -69,12 +69,12 @@ describe('StrongConfig class', () => {
expect(result).toStrictEqual(mockedConfig)
})
it('calls imported load() with initialized parameters', () => {
const strongConfig = new StrongConfig(mockedParameters)
it('calls imported load() with initialized options', () => {
const strongConfig = new StrongConfig(mockedOptions)
strongConfig.load()
expect(mockedLoad).toHaveBeenCalledWith(mockedParameters)
expect(mockedLoad).toHaveBeenCalledWith(mockedOptions)
})
it('memoizes previously loaded config', () => {
......@@ -100,25 +100,25 @@ describe('StrongConfig class', () => {
})
it('validates config paths that are passed', () => {
const strongConfig = new StrongConfig(mockedParameters)
const strongConfig = new StrongConfig(mockedOptions)
strongConfig.validate(mockedConfigPath, mockedConfigPath)
expect(mockedValidate).toHaveBeenCalledWith(
[mockedConfigPath, mockedConfigPath],
mockedParameters
mockedOptions
)
})
it('validates parameters.configPath by default', () => {
const strongConfig = new StrongConfig(mockedParameters)
it('validates options.configPath by default', () => {
const strongConfig = new StrongConfig(mockedOptions)
// Do not pass any configPaths to trigger default validation
strongConfig.validate()
expect(mockedValidate).toHaveBeenCalledWith(
[defaultParameters.configPath],
mockedParameters
[defaultOptions.configPath],
mockedOptions
)
})
})
......
......@@ -2,23 +2,23 @@ import R from 'ramda'
import { load } from './load'
import { validate } from './validate'
import { validate as validateParameters } from './params/validate'
import { validate as validateOptions } from './options/validate'
import { MemoizedConfig } from './types'
import { defaultParameters, Parameters } from './params'
import { defaultOptions, Options } from './options'
export = class StrongConfig {
public readonly parameters: Parameters
public readonly options: Options
private config: MemoizedConfig
constructor(parameters?: Partial<Parameters>) {
this.parameters = validateParameters(
parameters
constructor(options?: Partial<Options>) {
this.options = validateOptions(
options
? {
...defaultParameters,
...parameters,
...defaultOptions,
...options,
}
: defaultParameters
: defaultOptions
)
}
......@@ -27,15 +27,15 @@ export = class StrongConfig {
return this.config
}
this.config = load(this.parameters)
this.config = load(this.options)
return this.config
}
public validate(...configPaths: string[]): ReturnType<typeof validate> {
return validate(
R.isEmpty(configPaths) ? [this.parameters.configPath] : configPaths,
this.parameters
R.isEmpty(configPaths) ? [this.options.configPath] : configPaths,
this.options
)
}
}
......@@ -5,7 +5,7 @@ jest.mock('./utils/validate-json')
jest.mock('./utils/read-file')
jest.mock('./utils/sops')
import { defaultParameters } from './params'
import { defaultOptions } from './options'
import { generateTypeFromSchema } from './utils/generate-type-from-schema'
import { hydrateConfig, InnerHydrateFunction } from './utils/hydrate-config'
import { validateJson } from './utils/validate-json'
......@@ -14,7 +14,7 @@ import { decryptToObject } from './utils/sops'
import { HydratedConfig } from './types'
const mockedParameters = defaultParameters
const mockedOptions = defaultOptions
const runtimeEnv = process.env.NODE_ENV || 'test'
const mockedConfigFile = {
filePath: './config/test.yaml',
......@@ -65,7 +65,7 @@ describe('load()', () => {
jest.clearAllMocks()
jest.resetModules()
process.env = Object.assign(process.env, {
[defaultParameters.runtimeEnvName]: runtimeEnv,
[defaultOptions.runtimeEnvName]: runtimeEnv,
})
})
......@@ -74,17 +74,17 @@ describe('load()', () => {
})
it('throws if NODE_ENV is not set', () => {
delete process.env[defaultParameters.runtimeEnvName]
delete process.env[defaultOptions.runtimeEnvName]
expect(() => load(mockedParameters)).toThrow('runtimeEnv must be defined')
expect(() => load(mockedOptions)).toThrow('runtimeEnv must be defined')
process.env = Object.assign(process.env, {
[defaultParameters.runtimeEnvName]: runtimeEnv,
[defaultOptions.runtimeEnvName]: runtimeEnv,
})
})
it('reads the config based on process.env.NODE_ENV', () => {
load(mockedParameters)
load(mockedOptions)
expect(mockedReadConfigFile).toHaveBeenCalledWith(
expect.any(String),
......@@ -93,7 +93,7 @@ describe('load()', () => {
})
it('decrypts the config with SOPS', () => {
load(mockedParameters)
load(mockedOptions)
expect(mockedDecryptToObject).toHaveBeenCalledWith(
mockedConfigFile.filePath,
......@@ -102,25 +102,20 @@ describe('load()', () => {
})
it('hydrates the config', () => {
load(mockedParameters)
load(mockedOptions)
expect(mockedHydrateConfig).toHaveBeenCalledWith(
runtimeEnv,
mockedParameters
)
expect(mockedHydrateConfig).toHaveBeenCalledWith(runtimeEnv, mockedOptions)
expect(innerHydrateFunction).toHaveBeenCalledWith(mockedDecryptedConfigFile)
})
it('reads the schema file', () => {
load(mockedParameters)
load(mockedOptions)
expect(mockedReadSchemaFile).toHaveBeenCalledWith(
defaultParameters.schemaPath
)
expect(mockedReadSchemaFile).toHaveBeenCalledWith(defaultOptions.schemaPath)
})
it('validates config against schema if schema was found', () => {
load(mockedParameters)
load(mockedOptions)
expect(validateJson).toHaveBeenCalledWith(
mockedHydratedConfig,
......@@ -128,15 +123,15 @@ describe('load()', () => {
)
})
it('generates types if parameters.types is not false', () => {
load(mockedParameters)
it('generates types if options.types is not false', () => {
load(mockedOptions)
expect(generateTypeFromSchema).toHaveBeenCalledWith(mockedParameters)
expect(generateTypeFromSchema).toHaveBeenCalledWith(mockedOptions)
})
it('skips generating types if parameters.types is false', () => {
it('skips generating types if options.types is false', () => {
load({
...mockedParameters,
...mockedOptions,
types: false,
})
......@@ -146,7 +141,7 @@ describe('load()', () => {
it('skips validating config if schema was not found', () => {
mockedReadSchemaFile.mockReturnValueOnce(null)
load(mockedParameters)
load(mockedOptions)
expect(validateJson).toHaveBeenCalledTimes(0)
})
......@@ -154,13 +149,13 @@ describe('load()', () => {
it('skips generating types if schema was not found', () => {
mockedReadSchemaFile.mockReturnValueOnce(null)
load(mockedParameters)
load(mockedOptions)
expect(generateTypeFromSchema).toHaveBeenCalledTimes(0)
})
it('returns the config', () => {
const loadedConfig = load(mockedParameters)
const loadedConfig = load(mockedOptions)
expect(loadedConfig).toStrictEqual(mockedHydratedConfig)
})
......
......@@ -8,12 +8,12 @@ import { readConfigFile, readSchemaFile } from './utils/read-file'
import * as sops from './utils/sops'
import { HydratedConfig } from './types'
import { Parameters } from './params'
import { Options } from './options'
export const load = (parameters: Parameters): HydratedConfig => {
const normalizedConfigPath = path.normalize(parameters.configPath)
const normalizedSchemaPath = path.normalize(parameters.schemaPath)
const runtimeEnv = process.env[parameters.runtimeEnvName]
export const load = (options: Options): HydratedConfig => {
const normalizedConfigPath = path.normalize(options.configPath)
const normalizedSchemaPath = path.normalize(options.schemaPath)
const runtimeEnv = process.env[options.runtimeEnvName]
if (R.isNil(runtimeEnv)) {
throw new Error('runtimeEnv must be defined.')
......@@ -26,15 +26,15 @@ export const load = (parameters: Parameters): HydratedConfig => {
configFile.contents
)
const config = hydrateConfig(runtimeEnv, parameters)(decrypted)
const config = hydrateConfig(runtimeEnv, options)(decrypted)
const schemaFile = readSchemaFile(normalizedSchemaPath)
if (schemaFile !== null) {
validateJson(config, schemaFile.contents)
if (parameters.types !== false) {
generateTypeFromSchema(parameters)
if (options.types !== false) {
generateTypeFromSchema(options)
}
}
......
export interface TypesParameters {
export interface TypeOptions {
rootTypeName: string
filePath: string
}
export interface Parameters {
export interface Options {
runtimeEnvName: string
types: TypesParameters | false
types: TypeOptions | false
substitutionPattern: string
configPath: string
schemaPath: string
}
export const defaultParameters: Parameters = {
export const defaultOptions: Options = {
runtimeEnvName: 'NODE_ENV',
types: {
rootTypeName: 'Config',
......
export const parametersSchema = {
export const optionsSchema = {
type: 'object',
title: 'Schema for strong-config parameters',
title: 'Schema for strong-config options',
required: [
'runtimeEnvName',
'types',
......@@ -19,9 +19,9 @@ export const parametersSchema = {
pattern: '^[a-zA-Z]\\w*$',
},
types: {
title: 'Type-related parameters',
title: 'Type-related options',
description:
'Type-related parameters controlling the generation of Typescript types for the config',
'Type-related options controlling the generation of Typescript types for the config',
type: ['object'],
additionalProperties: false,
properties: {
......@@ -34,7 +34,7 @@ export const parametersSchema = {
},
filePath: {
title: 'Path to types file',
description: 'The file that the generated types should be stored to',
description: 'The file that the generated types should be stored in',
examples: ['strong-config.d.ts', './types/config.ts'],
type: 'string',
},
......
jest.mock('../utils/validate-json')
import { validateJson } from '../utils/validate-json'
import { defaultParameters } from '.'
import { defaultOptions } from '.'
const mockedValidateJson = validateJson as jest.MockedFunction<
typeof validateJson
>
const mockedParameters = { ...defaultParameters }
const mockedOptions = { ...defaultOptions }
mockedValidateJson.mockReturnValue(true)
......@@ -19,27 +19,25 @@ describe('validate()', () => {
})
it('validates json', () => {
validate(mockedParameters)
validate(mockedOptions)
expect(mockedValidateJson).toHaveBeenCalledWith(
mockedParameters,
mockedOptions,
expect.any(Object)
)
})
it('returns the params when they are valid', () => {
const validationResult = validate(mockedParameters)
it('returns the options when they are valid', () => {
const validationResult = validate(mockedOptions)
expect(validationResult).toStrictEqual(mockedParameters)
expect(validationResult).toStrictEqual(mockedOptions)
})
it('throws when parameter validation fails', () => {
it('throws when option validation fails', () => {
mockedValidateJson.mockImplementation(() => {
throw new Error('some validation error')
})
expect(() => validate(mockedParameters)).toThrowError(
'some validation error'
)
expect(() => validate(mockedOptions)).toThrowError('some validation error')
})
})
import { validateJson } from '../utils/validate-json'
import { optionsSchema } from './schema'
import { JSONObject } from '../types'
import { Options } from '.'
export const validate = (options: Options): Options =>
validateJson((options as unknown) as JSONObject, optionsSchema) && options
import { validateJson } from '../utils/validate-json'
import { parametersSchema } from './schema'
import { JSONObject } from '../types'
import { Parameters } from '.'
export const validate = (parameters: Parameters): Parameters =>
validateJson((parameters as unknown) as JSONObject, parametersSchema) &&
parameters
import { defaultParameters, TypesParameters } from '../params'
import { defaultOptions, TypeOptions } from '../options'
const mockedParameters = defaultParameters
const mockedOptions = defaultOptions
const mockedCompiledTypes = `
export interface TheTopLevelInterface {
name: string;
......@@ -64,29 +64,25 @@ describe('generateTypeFromSchema()', () => {
})
it('calls compileFromFile with a file path', async () => {
await generateTypeFromSchema(mockedParameters)
await generateTypeFromSchema(mockedOptions)
expect(mockedCompileFromFile).toHaveBeenCalledWith(
mockedParameters.schemaPath
)
expect(mockedCompileFromFile).toHaveBeenCalledWith(mockedOptions.schemaPath)
})
it('reads the file at filePath', async () => {
await generateTypeFromSchema(mockedParameters)
await generateTypeFromSchema(mockedOptions)
expect(mockedFs.readFileSync).toHaveBeenCalledWith(
mockedParameters.schemaPath
)
expect(mockedFs.readFileSync).toHaveBeenCalledWith(mockedOptions.schemaPath)
})
it('generates correct types', async () => {
const expectedTypes = `${mockedCompiledTypes}${expectedRootType}`
const typeParams = mockedParameters.types as TypesParameters
const typeOptions = mockedOptions.types as TypeOptions
await generateTypeFromSchema(mockedParameters)
await generateTypeFromSchema(mockedOptions)
expect(mockedFs.writeFileSync).toHaveBeenCalledWith(
typeParams.filePath,
typeOptions.filePath,
expectedTypes
)
})
......@@ -94,7 +90,7 @@ describe('generateTypeFromSchema()', () => {
it('throws when top-level schema definition does not have a title field', async () => {
mockedFs.readFileSync.mockReturnValueOnce(mockedSchemaStringWithoutTitle)
await expect(generateTypeFromSchema(mockedParameters)).rejects.toThrowError(
await expect(generateTypeFromSchema(mockedOptions)).rejects.toThrowError(
Error
)
})
......@@ -104,8 +100,6 @@ describe('generateTypeFromSchema()', () => {
mockedSchemaStringWithInvalidTitle
)
await expect(generateTypeFromSchema(mockedParameters)).rejects.toThrow(
Error
)
await expect(generateTypeFromSchema(mockedOptions)).rejects.toThrow(Error)
})
})
......@@ -2,7 +2,7 @@ import { compileFromFile } from 'json-schema-to-typescript'
import fs from 'fs'
import R from 'ramda'
import { Parameters, TypesParameters } from '../params'
import { Options, TypeOptions } from '../options'
// json-schema-to-typescript uses a `toSafeString(string)` function https://github.com/bcherny/json-schema-to-typescript/blob/f41945f19b68918e9c13885f345cb708e1d9898a/src/utils.ts#L163) to obtain a normalized string. This pascalCase mimics this functionality and should address most cases.
export const pascalCase = (input: string): string =>
......@@ -14,9 +14,9 @@ export const pascalCase = (input: string): string =>
export const generateTypeFromSchema = async ({
schemaPath,
types,
}: Parameters): Promise<void> => {
}: Options): Promise<void> => {
// When this function is called, we are sure that types !== false
types = types as TypesParameters
types = types as TypeOptions
const baseTypes = await compileFromFile(schemaPath)
......
jest.mock('./substitute-with-env')
import { substituteWithEnv } from './substitute-with-env'
import { defaultParameters } from '../params'
import { defaultOptions } from '../options'
const mockedSubstituteWithEnv = substituteWithEnv as jest.MockedFunction<
typeof substituteWithEnv
>
const mockedParameters = defaultParameters
const mockedOptions = defaultOptions
const runtimeEnv = 'test'
const mockedConfig = {
field: 'value',
......@@ -15,26 +15,26 @@ const mockedSubstitutedConfig = '{"field":"value","replaceMe":"PASTE"}'
const mockedHydratedConfig = {
...mockedConfig,
replaceMe: 'PASTE',
[mockedParameters.runtimeEnvName]: runtimeEnv,
[mockedOptions.runtimeEnvName]: runtimeEnv,
}
mockedSubstituteWithEnv.mockReturnValue(() => mockedSubstitutedConfig)
import { hydrateConfig } from './hydrate-config'
const hydrateConfigInited = hydrateConfig(runtimeEnv, mockedParameters)
const hydrateConfigInited = hydrateConfig(runtimeEnv, mockedOptions)
describe('hydrateConfig()', () => {
it('calls substituteWithEnv with substitutionPattern', () => {
hydrateConfigInited(mockedConfig)
expect(mockedSubstituteWithEnv).toHaveBeenCalledWith(
mockedParameters.substitutionPattern
mockedOptions.substitutionPattern
)
})
it('adds runtimeEnv as top-level field', () => {
expect(hydrateConfigInited(mockedConfig)).toEqual(
expect.objectContaining({
[mockedParameters.runtimeEnvName]: runtimeEnv,
[mockedOptions.runtimeEnvName]: runtimeEnv,
})
)
})
......
......@@ -2,7 +2,7 @@ import R from 'ramda'
import { substituteWithEnv } from './substitute-with-env'
import { DecryptedConfig, HydratedConfig } from '../types'
import { Parameters } from '../params'
import { Options } from '../options'
export type InnerHydrateFunction = (
decryptedConfig: DecryptedConfig
......@@ -10,7 +10,7 @@ export type InnerHydrateFunction = (
export const hydrateConfig = (
runtimeEnv: string,
{ runtimeEnvName, substitutionPattern }: Parameters
{ runtimeEnvName, substitutionPattern }: Options
): InnerHydrateFunction =>
R.compose(
R.assoc(runtimeEnvName, runtimeEnv),
......
import { defaultParameters } from '../params'
import { defaultOptions } from '../options'
const mockedProcessEnv = {
replace: 'REPLACED',
ABC: 'SOME_ENV_VAR_VALUE',
'0INVALID': 'INVALID KEY',
}
const mockedParameters = defaultParameters
const mockedSubstitutionPattern = mockedParameters.substitutionPattern
const mockedOptions = defaultOptions
const mockedSubstitutionPattern = mockedOptions.substitutionPattern
import { substituteWithEnv } from './substitute-with-env'
const substituteWithEnvInitialized = substituteWithEnv(
......
......@@ -6,7 +6,7 @@ import { getFileFromPath } from './utils/get-file-from-path'
import { validateJson } from './utils/validate-json'
import { Schema } from './types'
import { Parameters } from './params'
import { Options } from './options'
const validateConfigAgainstSchema = (schema: Schema) => (
configFilePath: string
......@@ -18,7 +18,7 @@ const validateConfigAgainstSchema = (schema: Schema) => (
export const validate = (
configPaths: string[],
{ schemaPath }: Parameters
{ schemaPath }: Options
): true => {
const normalizedSchemaPath = path.normalize(schemaPath)
const normalizedConfigPaths = configPaths.map(path.normalize)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment