Skip to content

Commit

Permalink
Merge branch 'master' into kenny-#903
Browse files Browse the repository at this point in the history
  • Loading branch information
kennylavender authored Dec 14, 2018
2 parents 9147886 + 3b989a8 commit 8f9a4c9
Show file tree
Hide file tree
Showing 8 changed files with 261 additions and 134 deletions.
8 changes: 4 additions & 4 deletions src/API/HealthController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,8 +117,8 @@ export class HealthController {
}

private async getTransactionRetryInfo(): Promise<TransactionAnchorRetryInfo> {
this.logger.child({ message: 'getTransactionRetryInfo' })
this.logger.trace('retrieving TransactionAnchorRetryInfo')
const logger = this.logger.child({ method: 'getTransactionRetryInfo' })
logger.trace('retrieving TransactionAnchorRetryInfo')
try {
const transactionAnchorRetryResults = await this.collection.findOne(
{
Expand All @@ -132,10 +132,10 @@ export class HealthController {
},
},
) || emptyTransactionAnchorRetryInfo
this.logger.trace({ transactionAnchorRetryResults }, 'getTransactionRetryInfo results')
logger.trace({ transactionAnchorRetryResults }, 'getTransactionRetryInfo results')
return transactionAnchorRetryResults.transactionAnchorRetryInfo
} catch (error) {
this.logger.error({ error }, 'error retrieving TransactionAnchorRetryInfo')
logger.error({ error }, 'error retrieving TransactionAnchorRetryInfo')
return []
}
}
Expand Down
123 changes: 2 additions & 121 deletions src/Configuration.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,3 @@
/* tslint:disable:no-relative-imports */
import * as assert from 'assert'
import { readFileSync, existsSync } from 'fs'
import { homedir } from 'os'
import * as path from 'path'
import { keys, pipe } from 'ramda'

import { createEnvToConfigurationKeyMap } from 'Helpers/Configuration'

const defaultMongodbUrl = 'mongodb://localhost:27017/poet'

// Provide default value in defaultConfiguration for any new configuration options
export interface Configuration extends LoggingConfiguration, BitcoinRPCConfiguration, ExchangeConfiguration {
readonly rabbitmqUrl: string
readonly exchangePrefix: string
Expand Down Expand Up @@ -85,15 +73,15 @@ export interface ExchangeConfiguration {
readonly exchangeForkDetected: string
}

const defaultConfiguration: Configuration = {
export const DefaultConfiguration: Configuration = {
rabbitmqUrl: 'amqp://admin:adminPass@localhost',
exchangePrefix: '',
mongodbUser: '',
mongodbPassword: '',
mongodbHost: 'localhost',
mongodbPort: 27017,
mongodbDatabase: 'poet',
mongodbUrl: defaultMongodbUrl,
mongodbUrl: 'mongodb://localhost:27017/poet',
ipfsUrl: 'http://localhost:5001',
ipfsArchiveUrlPrefix: 'https://ipfs.io/ipfs',
bitcoinUrl: '127.0.0.1',
Expand Down Expand Up @@ -151,110 +139,3 @@ const defaultConfiguration: Configuration = {
exchangePurgeStaleTransactions: 'BLOCK_WRITER::PURGE_STALE_TRANSACTIONS',
exchangeForkDetected: 'FORK_DETECTED',
}

export const configurationPath = () => path.join(homedir(), '/.po.et/configuration.json')

export const mergeConfigs = (localVars: any = {}) => {
const config = {
...defaultConfiguration,
...loadConfigurationFromEnv(localVars),
...loadConfigurationFromFile(configurationPath()),
}

// Support setting MONGODB_URL all at once or via separate variables.
// Especially needed for production since the schema is different (mongodb+srv) and
// there's currently no override for that.
if (config.mongodbUrl === defaultMongodbUrl) {
const mongoAuth = config.mongodbUser !== '' ? `${config.mongodbUser}:${config.mongodbPassword}@` : ''
config.mongodbUrl = `mongodb://${mongoAuth}${config.mongodbHost}:${config.mongodbPort}/${config.mongodbDatabase}`
}

return config
}

const prependPrefix = (prefix: string, configVars: any) => (acc: any, k: string) => ({
...acc,
[k]: `${prefix}.${configVars[k]}`,
})

const applyExchangePrefix = (configVars: any) => {
if (configVars.exchangePrefix === '') return configVars

const exchangeNames = [
'exchangeAnchorNextHashRequest',
'exchangeBatchReaderReadNextDirectoryRequest',
'exchangeBatchReaderReadNextDirectorySuccess',
'exchangeBatchWriterCreateNextBatchRequest',
'exchangeBatchWriterCreateNextBatchSuccess',
'exchangeNewClaim',
'exchangeClaimIpfsHash',
'exchangeIpfsHashTxId',
'exchangePoetAnchorDownloaded',
'exchangeClaimsDownloaded',
'exchangeClaimsNotDownloaded',
'exchangeStorageWriterStoreNextClaim',
'exchangeGetHealth',
'exchangePurgeStaleTransactions',
'exchangeForkDetected',
]

return {
...configVars,
...exchangeNames.reduce(prependPrefix(configVars.exchangePrefix, configVars), {}),
}
}

export const loadConfigurationWithDefaults = (localVars: any = {}) =>
pipe(
mergeConfigs,
applyExchangePrefix,
)({ ...process.env, ...localVars })

function loadConfigurationFromFile(configPath: string): Configuration | {} {
if (!existsSync(configPath)) return {}

const configuration = JSON.parse(readFileSync(configPath, 'utf8'))

if (configuration.poetNetwork) validatePoetNetwork(configuration.poetNetwork)
if (configuration.poetVersion) validatePoetVersion(configuration.poetVersion)

return configuration
}

const extractValue = (value: any) => {
const coercedValue = value === 'true' ? true : value === 'false' ? false : value

return isNaN(coercedValue)
? coercedValue
: typeof coercedValue === 'boolean'
? coercedValue
: parseInt(coercedValue, 10)
}

function loadConfigurationFromEnv(env: any): Partial<Configuration> {
const map = createEnvToConfigurationKeyMap(keys(defaultConfiguration))

const configurationFromEnv = Object.entries(env)
.filter(([key, value]) => map[key])
.reduce(
(previousValue, [key, value]: [string, any]) => ({
...previousValue,
[map[key]]: extractValue(value),
}),
{},
)

return configurationFromEnv
}

function validatePoetVersion(poetVersion: number) {
assert(
Number.isInteger(poetVersion) && 0 <= poetVersion && poetVersion <= 0xFFFF,
'poetVersion must be an integer between 0 and 65535',
)
}

function validatePoetNetwork(poetNetwork: any) {
assert(typeof poetNetwork === 'string', 'Field poetNetwork must be a string')
assert(poetNetwork.length === 4, 'Field poetNetwork must have a length of 4')
}
138 changes: 133 additions & 5 deletions src/Configuration.test.ts → src/LoadConfiguration.test.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
/* tslint:disable:no-relative-imports */
import { pick } from 'ramda'
import { describe } from 'riteway'
import { loadConfigurationWithDefaults, mergeConfigs } from './Configuration'
import { describe, Try } from 'riteway'
import {
loadConfigurationWithDefaults,
mergeConfigs,
applyExchangePrefix,
extractValue,
validatePoetVersion,
validatePoetNetwork,
} from './LoadConfiguration'

const defaultConfig = mergeConfigs()

describe('src/Configuration', async (assert: any) => {
describe('src/LoadConfiguration', async assert => {
assert({
given: 'no arguments',
should: 'return the default config',
Expand Down Expand Up @@ -81,7 +88,7 @@ describe('src/Configuration', async (assert: any) => {
}
})

describe('loadConfigurationWithDefaults', async (assert: any) => {
describe('loadConfigurationWithDefaults', async assert => {
const mongodbOverrides = {
API_PORT: '4321',
ENABLE_ANCHORING: 'true',
Expand Down Expand Up @@ -133,7 +140,7 @@ describe('loadConfigurationWithDefaults', async (assert: any) => {
}
})

describe('src/Configuration RabbitmqExchangeMessages', async (assert: any) => {
describe('src/LoadConfiguration RabbitmqExchangeMessages', async assert => {
{
const defaultValues = {
exchangeBatchReaderReadNextDirectoryRequest: 'BATCH_READER::READ_NEXT_DIRECTORY_REQUEST',
Expand Down Expand Up @@ -201,3 +208,124 @@ describe('src/Configuration RabbitmqExchangeMessages', async (assert: any) => {
})
}
})

describe('src/LoadConfiguration applyExchangePrefix', async assert => {
{
const actual = { exchangePrefix: '' }

const expected = applyExchangePrefix(actual)

assert({
given: 'configVars with the property exchangePrefix with empty string',
should: 'return the same object configVars',
actual,
expected,
})
}

{
const configVars = {
exchangePrefix: 'prefix',
exchangeAnchorNextHashRequest: 'exchangeAnchorNextHashRequest',
exchangeBatchReaderReadNextDirectoryRequest: 'exchangeBatchReaderReadNextDirectoryRequest',
exchangeBatchReaderReadNextDirectorySuccess: 'exchangeBatchReaderReadNextDirectorySuccess',
exchangeBatchWriterCreateNextBatchRequest: 'exchangeBatchWriterCreateNextBatchRequest',
exchangeBatchWriterCreateNextBatchSuccess: 'exchangeBatchWriterCreateNextBatchSuccess',
exchangeNewClaim: 'exchangeNewClaim',
exchangeClaimIpfsHash: 'exchangeClaimIpfsHash',
exchangeIpfsHashTxId: 'exchangeIpfsHashTxId',
exchangePoetAnchorDownloaded: 'exchangePoetAnchorDownloaded',
exchangeClaimsDownloaded: 'exchangeClaimsDownloaded',
exchangeClaimsNotDownloaded: 'exchangeClaimsNotDownloaded',
exchangeStorageWriterStoreNextClaim: 'exchangeStorageWriterStoreNextClaim',
exchangeGetHealth: 'exchangeGetHealth',
exchangePurgeStaleTransactions: 'exchangePurgeStaleTransactions',
exchangeForkDetected: 'exchangeForkDetected',
}

const actual = applyExchangePrefix(configVars)

const expected = {
exchangePrefix: 'prefix',
exchangeAnchorNextHashRequest: 'prefix.exchangeAnchorNextHashRequest',
exchangeBatchReaderReadNextDirectoryRequest: 'prefix.exchangeBatchReaderReadNextDirectoryRequest',
exchangeBatchReaderReadNextDirectorySuccess: 'prefix.exchangeBatchReaderReadNextDirectorySuccess',
exchangeBatchWriterCreateNextBatchRequest: 'prefix.exchangeBatchWriterCreateNextBatchRequest',
exchangeBatchWriterCreateNextBatchSuccess: 'prefix.exchangeBatchWriterCreateNextBatchSuccess',
exchangeNewClaim: 'prefix.exchangeNewClaim',
exchangeClaimIpfsHash: 'prefix.exchangeClaimIpfsHash',
exchangeIpfsHashTxId: 'prefix.exchangeIpfsHashTxId',
exchangePoetAnchorDownloaded: 'prefix.exchangePoetAnchorDownloaded',
exchangeClaimsDownloaded: 'prefix.exchangeClaimsDownloaded',
exchangeClaimsNotDownloaded: 'prefix.exchangeClaimsNotDownloaded',
exchangeStorageWriterStoreNextClaim: 'prefix.exchangeStorageWriterStoreNextClaim',
exchangeGetHealth: 'prefix.exchangeGetHealth',
exchangePurgeStaleTransactions: 'prefix.exchangePurgeStaleTransactions',
exchangeForkDetected: 'prefix.exchangeForkDetected',
}

assert({
given: 'configVars with the property exchangePrefix with a custom prefix',
should: 'return the same object configVars with the custom prefix over the name of exchanges',
actual,
expected,
})
}
})

describe('src/LoadConfiguration extractValue', async assert => {
{
const actual = extractValue('false')
const expected = false

assert({
given: 'false like as string',
should: 'return false as boolean',
actual,
expected,
})
}

{
const actual = extractValue('true')
const expected = true

assert({
given: 'true like as string',
should: 'return true as boolean',
actual,
expected,
})
}
})

describe('src/LoadConfiguration validatePoetVersion', async assert => {
{
assert({
given: 'an invalid Poet version -1',
should: `return the message 'poetVersion must be an integer between 0 and 65535'`,
actual: Try(validatePoetVersion, -1).message,
expected: 'poetVersion must be an integer between 0 and 65535',
})
}

{
assert({
given: 'an invalid Poet version 65536',
should: `return the message 'poetVersion must be an integer between 0 and 65535'`,
actual: Try(validatePoetVersion, 65536).message,
expected: 'poetVersion must be an integer between 0 and 65535',
})
}
})

describe('src/LoadConfiguration validatePoetNetwork', async assert => {
{
assert({
given: 'a string with more 4 letters as Poet Network',
should: `return the message 'Field poetNetwork must have a length of 4'`,
actual: Try(validatePoetNetwork, '12345').message,
expected: 'Field poetNetwork must have a length of 4',
})
}
})
Loading

0 comments on commit 8f9a4c9

Please sign in to comment.