const Optional = require('optional-js'); const { validateMetric } = require('measured-core').metricValidators; /** * This module contains various validators to validate publicly exposed input. * * @module inputValidators */ module.exports = { /** * Validates @{link Gauge} options. * * @param {string} name The metric name * @param {function} callback The callback for the Gauge * @param {Dimensions} dimensions The optional custom dimensions * @param {number} publishingIntervalInSeconds the optional publishing interval */ validateGaugeOptions: (name, callback, dimensions, publishingIntervalInSeconds) => { module.exports.validateCommonMetricParameters(name, dimensions, publishingIntervalInSeconds); module.exports.validateNumberReturningCallback(callback); }, /** * Validates @{link Gauge} options. * * @param {string} name The metric name * @param {function} callback The callback for the CachedGauge * @param {Dimensions} dimensions The optional custom dimensions * @param {number} publishingIntervalInSeconds the optional publishing interval */ validateCachedGaugeOptions: (name, callback, dimensions, publishingIntervalInSeconds) => { module.exports.validateCommonMetricParameters(name, dimensions, publishingIntervalInSeconds); // Should we validate the promise call back, it may be expensive or produce a race condition in some use-cases. }, /** * Validates the create histogram Options. * * @param {string} name The metric name * @param {Dimensions} dimensions The optional custom dimensions * @param {number} publishingIntervalInSeconds the optional publishing interval */ validateHistogramOptions: (name, dimensions, publishingIntervalInSeconds) => { module.exports.validateCommonMetricParameters(name, dimensions, publishingIntervalInSeconds); }, /** * Validates the create counter Options. * * @param {string} name The metric name * @param {Dimensions} dimensions The optional custom dimensions * @param {number} publishingIntervalInSeconds the optional publishing interval */ validateCounterOptions: (name, dimensions, publishingIntervalInSeconds) => { module.exports.validateCommonMetricParameters(name, dimensions, publishingIntervalInSeconds); }, /** * Validates the create timer Options. * * @param {string} name The metric name * @param {Dimensions} dimensions The optional custom dimensions * @param {number} publishingIntervalInSeconds the optional publishing interval */ validateTimerOptions: (name, dimensions, publishingIntervalInSeconds) => { module.exports.validateCommonMetricParameters(name, dimensions, publishingIntervalInSeconds); }, /** * Validates the create timer Options. * * @param {string} name The metric name * @param {Metric} metric The metric instance * @param {Dimensions} dimensions The optional custom dimensions * @param {number} publishingIntervalInSeconds the optional publishing interval */ validateRegisterOptions: (name, metric, dimensions, publishingIntervalInSeconds) => { module.exports.validateMetric(metric); module.exports.validateCommonMetricParameters(name, dimensions, publishingIntervalInSeconds); }, /** * Validates the create settable gauge Options. * * @param {string} name The metric name * @param {Dimensions} dimensions The optional custom dimensions * @param {number} publishingIntervalInSeconds the optional publishing interval */ validateSettableGaugeOptions: (name, dimensions, publishingIntervalInSeconds) => { module.exports.validateCommonMetricParameters(name, dimensions, publishingIntervalInSeconds); }, /** * Validates the options that are common amoung all create metric methods * * @param {string} name The metric name * @param {Dimensions} dimensions The optional custom dimensions * @param {number} publishingIntervalInSeconds the optional publishing interval */ validateCommonMetricParameters: (name, dimensions, publishingIntervalInSeconds) => { module.exports.validateMetricName(name); module.exports.validateOptionalDimensions(dimensions); module.exports.validateOptionalPublishingInterval(publishingIntervalInSeconds); }, /** * Validates the metric name. * * @param name The metric name. */ validateMetricName: name => { const type = typeof name; if (type !== 'string') { throw new TypeError(`options.name is a required option and must be of type string, actual type: ${type}`); } }, /** * Validates that a metric implements the metric interface. * * @function * @name validateMetric * @param {Metric} metric The object that is supposed to be a metric. */ validateMetric, /** * Validates the provided callback. * * @param callback The provided callback for a gauge. */ validateNumberReturningCallback: callback => { const type = typeof callback; if (type !== 'function') { throw new TypeError(`options.callback is a required option and must be function, actual type: ${type}`); } const callbackType = typeof callback(); if (callbackType !== 'number') { throw new TypeError(`options.callback must return a number, actual return type: ${callbackType}`); } }, /** * Validates a set of optional dimensions * @param dimensionsOptional */ validateOptionalDimensions: dimensionsOptional => { Optional.ofNullable(dimensionsOptional).ifPresent(dimensions => { const type = typeof dimensions; if (type !== 'object') { throw new TypeError(`options.dimensions should be an object, actual type: ${type}`); } if (Array.isArray(dimensions)) { throw new TypeError('dimensions where detected to be an array, expected Object<string, string>'); } Object.keys(dimensions).forEach(key => { const valueType = typeof dimensions[key]; if (valueType !== 'string') { throw new TypeError(`options.dimensions.${key} should be of type string, actual type: ${type}`); } }); }); }, /** * Validates that an optional logger instance at least has the methods we expect. * @param loggerOptional */ validateOptionalLogger: loggerOptional => { Optional.ofNullable(loggerOptional).ifPresent(logger => { if ( typeof logger.debug !== 'function' || typeof logger.info !== 'function' || typeof logger.warn !== 'function' || typeof logger.error !== 'function' ) { throw new TypeError( 'The logger that was passed in does not support all required ' + 'logging methods, expected object to have functions debug, info, warn, and error with ' + 'method signatures (...msgs) => {}' ); } }); }, /** * Validates the optional publishing interval. * * @param publishingIntervalInSecondsOptional The optional publishing interval. */ validateOptionalPublishingInterval: publishingIntervalInSecondsOptional => { Optional.ofNullable(publishingIntervalInSecondsOptional).ifPresent(publishingIntervalInSeconds => { const type = typeof publishingIntervalInSeconds; if (type !== 'number') { throw new TypeError(`options.publishingIntervalInSeconds must be of type number, actual type: ${type}`); } }); }, /** * Validates optional params for a Reporter * @param {ReporterOptions} options The optional params */ validateReporterParameters: options => { if (options) { module.exports.validateOptionalDimensions(options.defaultDimensions); module.exports.validateOptionalLogger(options.logger); const type = typeof options.unrefTimers; if (type !== 'boolean' && type !== 'undefined') { throw new TypeError(`options.unrefTimers should be a boolean or undefined, actual type: ${type}`); } } }, /** * Validates that a valid Reporter object has been supplied * * @param {Reporter} reporter */ validateReporterInstance: reporter => { if (!reporter) { throw new TypeError('The reporter was undefined, when it was required'); } if (typeof reporter.setRegistry !== 'function') { throw new TypeError( 'A reporter must implement setRegistry(registry), see the abstract Reporter class in the docs.' ); } if (typeof reporter.reportMetricOnInterval !== 'function') { throw new TypeError( 'A reporter must implement reportMetricOnInterval(metricKey, intervalInSeconds), see the abstract Reporter class in the docs.' ); } }, /** * Validates the input parameters for a {@link SelfReportingMetricsRegistry} * @param {Reporter[]} reporters * @param {SelfReportingMetricsRegistryOptions} [options] */ validateSelfReportingMetricsRegistryParameters: (reporters, options) => { reporters.forEach(reporter => module.exports.validateReporterInstance(reporter)); if (options) { module.exports.validateOptionalLogger(options.logger); } } };