diff --git a/README.md b/README.md index 3e3bfec3..73e48d0d 100644 --- a/README.md +++ b/README.md @@ -45,6 +45,7 @@ The API recognizes the following properties under the top-level `api` key in you |`relativeScores`|*no*|true|if set to true, confidence scores will be normalized, realistically at this point setting this to false is not tested or desirable |`accessLog`|*no*||name of the format to use for access logs; may be any one of the [predefined values](https://github.com/expressjs/morgan#predefined-formats) in the `morgan` package. Defaults to `"common"`; if set to `false`, or an otherwise falsy value, disables access-logging entirely.| |`services`|*no*||service definitions for [point-in-polygon](https://github.com/pelias/pip-service) and [placholder](https://github.com/pelias/placeholder) services. If missing (which is not recommended), the point-in-polygon and placeholder services will not be called.| +|`defaultParameters.focus.point.lon`
`defaultParameters.focus.point.lat`|no | |default coordinates for focus point Example configuration file would look something like this: @@ -74,6 +75,10 @@ Example configuration file would look something like this: "url": "http://myplaceholderservice.com:5000" } } + "defaultParameters": { + "focus.point.lat": 12.121212, + "focus.point.lon": 21.212121 + } }, "interpolation": { "client": { diff --git a/routes/v1.js b/routes/v1.js index 1449887f..83d2fb78 100644 --- a/routes/v1.js +++ b/routes/v1.js @@ -129,7 +129,7 @@ function addRoutes(app, peliasConfig) { controllers.mdToHTML(peliasConfig.api, './public/attribution.md') ]), search: createRouter([ - sanitizers.search.middleware, + sanitizers.search.middleware(peliasConfig.api), middleware.requestLanguage, middleware.calcSize(), controllers.placeholder(placeholderService, placeholderShouldExecute), @@ -156,7 +156,7 @@ function addRoutes(app, peliasConfig) { postProc.sendJSON ]), structured: createRouter([ - sanitizers.structured_geocoding.middleware, + sanitizers.structured_geocoding.middleware(peliasConfig.api), middleware.requestLanguage, middleware.calcSize(), controllers.search(peliasConfig.api, esclient, queries.structured_geocoding, not(hasResponseDataOrRequestErrors)), @@ -177,7 +177,7 @@ function addRoutes(app, peliasConfig) { postProc.sendJSON ]), autocomplete: createRouter([ - sanitizers.autocomplete.middleware, + sanitizers.autocomplete.middleware(peliasConfig.api), middleware.requestLanguage, controllers.search(peliasConfig.api, esclient, queries.autocomplete, not(hasResponseDataOrRequestErrors)), postProc.distances('focus.point.'), diff --git a/sanitizer/_location_bias.js b/sanitizer/_location_bias.js new file mode 100644 index 00000000..4c9c7f8b --- /dev/null +++ b/sanitizer/_location_bias.js @@ -0,0 +1,31 @@ +const _ = require('lodash'); +/** +Set a focus.lat and focus.lon if specified in pelias config +* @param {object} defaultParameters property of pelias config +*/ + +function setup(defaultParameters){ + + return function setLocationBias(raw, clean){ +/* +check that: +1. {object} raw exists +2. pelias-config included the properties focus.point.lat and focus.point.lon +3. raw.focus.point.lon and raw.focus.point.lat have not been set +*/ + if (!_.isUndefined(raw) && + !_.isUndefined(defaultParameters['focus.point.lat']) && + !_.isUndefined(defaultParameters['focus.point.lon']) && + !_.has(raw, 'focus.point.lon') && + !_.has(raw, 'focus.point.lat') ) { + + raw['focus.point.lat'] = defaultParameters['focus.point.lat']; + raw['focus.point.lon'] = defaultParameters['focus.point.lon']; + } + + return { errors: [], warnings: [] }; + }; +} + +// if focus.point.lat and focus.point.lon already exists, don't change +module.exports = setup; diff --git a/sanitizer/autocomplete.js b/sanitizer/autocomplete.js index 86f85447..ed875bf5 100644 --- a/sanitizer/autocomplete.js +++ b/sanitizer/autocomplete.js @@ -1,7 +1,9 @@ var type_mapping = require('../helper/type_mapping'); +var sanitizeAll = require('../sanitizer/sanitizeAll'); -var sanitizeAll = require('../sanitizer/sanitizeAll'), - sanitizers = { +// middleware +module.exports.middleware = (_api_pelias_config) => { + var sanitizers = { singleScalarParameters: require('../sanitizer/_single_scalar_parameters'), text: require('../sanitizer/_text_addressit'), tokenizer: require('../sanitizer/_tokenizer'), @@ -11,24 +13,21 @@ var sanitizeAll = require('../sanitizer/sanitizeAll'), // depends on the layers and sources sanitizers, must be run after them sources_and_layers: require('../sanitizer/_sources_and_layers'), private: require('../sanitizer/_flag_bool')('private', false), + location_bias: require('../sanitizer/_location_bias')(_api_pelias_config.defaultParameters), geo_autocomplete: require('../sanitizer/_geo_autocomplete'), boundary_country: require('../sanitizer/_boundary_country'), categories: require('../sanitizer/_categories') }; -var sanitize = function(req, cb) { sanitizeAll(req, sanitizers, cb); }; - -// export sanitize for testing -module.exports.sanitize = sanitize; -module.exports.sanitizer_list = sanitizers; + var sanitize = function(req, cb) { sanitizeAll(req, sanitizers, cb); }; -// middleware -module.exports.middleware = function( req, res, next ){ - sanitize( req, function( err, clean ){ - if( err ){ - res.status(400); // 400 Bad Request - return next(err); - } + return function( req, res, next ){ + sanitize( req, function( err, clean ){ + if( err ){ + res.status(400); // 400 Bad Request + return next(err); + } next(); - }); + }); + }; }; diff --git a/sanitizer/search.js b/sanitizer/search.js index 1d2507c2..99baa5a7 100644 --- a/sanitizer/search.js +++ b/sanitizer/search.js @@ -1,30 +1,33 @@ var type_mapping = require('../helper/type_mapping'); +var sanitizeAll = require('../sanitizer/sanitizeAll'); +// middleware +module.exports.middleware = (_api_pelias_config) => { + var sanitizers = { + singleScalarParameters: require('../sanitizer/_single_scalar_parameters'), + quattroshapes_deprecation: require('../sanitizer/_deprecate_quattroshapes'), + text: require('../sanitizer/_text'), + iso2_to_iso3: require('../sanitizer/_iso2_to_iso3'), + city_name_standardizer: require('../sanitizer/_city_name_standardizer'), + size: require('../sanitizer/_size')(/* use defaults*/), + layers: require('../sanitizer/_targets')('layers', type_mapping.layer_mapping), + sources: require('../sanitizer/_targets')('sources', type_mapping.source_mapping), + // depends on the layers and sources sanitizers, must be run after them + sources_and_layers: require('../sanitizer/_sources_and_layers'), + private: require('../sanitizer/_flag_bool')('private', false), + location_bias: require('../sanitizer/_location_bias')(_api_pelias_config.defaultParameters), + geo_search: require('../sanitizer/_geo_search'), + boundary_country: require('../sanitizer/_boundary_country'), + categories: require('../sanitizer/_categories'), + // this can go away once geonames has been abrogated + geonames_warnings: require('../sanitizer/_geonames_warnings') + }; -var sanitizeAll = require('../sanitizer/sanitizeAll'), - sanitizers = { - singleScalarParameters: require('../sanitizer/_single_scalar_parameters'), - quattroshapes_deprecation: require('../sanitizer/_deprecate_quattroshapes'), - text: require('../sanitizer/_text'), - iso2_to_iso3: require('../sanitizer/_iso2_to_iso3'), - city_name_standardizer: require('../sanitizer/_city_name_standardizer'), - size: require('../sanitizer/_size')(/* use defaults*/), - layers: require('../sanitizer/_targets')('layers', type_mapping.layer_mapping), - sources: require('../sanitizer/_targets')('sources', type_mapping.source_mapping), - // depends on the layers and sources sanitizers, must be run after them - sources_and_layers: require('../sanitizer/_sources_and_layers'), - private: require('../sanitizer/_flag_bool')('private', false), - geo_search: require('../sanitizer/_geo_search'), - boundary_country: require('../sanitizer/_boundary_country'), - categories: require('../sanitizer/_categories'), - // this can go away once geonames has been abrogated - geonames_warnings: require('../sanitizer/_geonames_warnings') - }; + var sanitize = function(req, cb) { sanitizeAll(req, sanitizers, cb); }; -var sanitize = function(req, cb) { sanitizeAll(req, sanitizers, cb); }; + return function( req, res, next ){ + sanitize( req, function( err, clean ){ + next(); + }); + }; -// middleware -module.exports.middleware = function( req, res, next ){ - sanitize( req, function( err, clean ){ - next(); - }); }; diff --git a/sanitizer/structured_geocoding.js b/sanitizer/structured_geocoding.js index 29edce41..e19a2837 100644 --- a/sanitizer/structured_geocoding.js +++ b/sanitizer/structured_geocoding.js @@ -1,28 +1,30 @@ var type_mapping = require('../helper/type_mapping'); - -var sanitizeAll = require('../sanitizer/sanitizeAll'), - sanitizers = { - singleScalarParameters: require('../sanitizer/_single_scalar_parameters'), - quattroshapes_deprecation: require('../sanitizer/_deprecate_quattroshapes'), - synthesize_analysis: require('../sanitizer/_synthesize_analysis'), - iso2_to_iso3: require('../sanitizer/_iso2_to_iso3'), - city_name_standardizer: require('../sanitizer/_city_name_standardizer'), - size: require('../sanitizer/_size')(/* use defaults*/), - layers: require('../sanitizer/_targets')('layers', type_mapping.layer_mapping), - sources: require('../sanitizer/_targets')('sources', type_mapping.source_mapping), - // depends on the layers and sources sanitizers, must be run after them - sources_and_layers: require('../sanitizer/_sources_and_layers'), - private: require('../sanitizer/_flag_bool')('private', false), - geo_search: require('../sanitizer/_geo_search'), - boundary_country: require('../sanitizer/_boundary_country'), - categories: require('../sanitizer/_categories') - }; - -var sanitize = function(req, cb) { sanitizeAll(req, sanitizers, cb); }; +var sanitizeAll = require('../sanitizer/sanitizeAll'); // middleware -module.exports.middleware = function( req, res, next ){ - sanitize( req, function( err, clean ){ - next(); - }); +module.exports.middleware = (_api_pelias_config) => { + var sanitizers = { + singleScalarParameters: require('../sanitizer/_single_scalar_parameters'), + quattroshapes_deprecation: require('../sanitizer/_deprecate_quattroshapes'), + synthesize_analysis: require('../sanitizer/_synthesize_analysis'), + iso2_to_iso3: require('../sanitizer/_iso2_to_iso3'), + city_name_standardizer: require('../sanitizer/_city_name_standardizer'), + size: require('../sanitizer/_size')(/* use defaults*/), + layers: require('../sanitizer/_targets')('layers', type_mapping.layer_mapping), + sources: require('../sanitizer/_targets')('sources', type_mapping.source_mapping), + // depends on the layers and sources sanitizers, must be run after them + sources_and_layers: require('../sanitizer/_sources_and_layers'), + private: require('../sanitizer/_flag_bool')('private', false), + location_bias: require('../sanitizer/_location_bias')(_api_pelias_config.defaultParameters), + geo_search: require('../sanitizer/_geo_search'), + boundary_country: require('../sanitizer/_boundary_country'), + categories: require('../sanitizer/_categories') + }; + var sanitize = function(req, cb) { sanitizeAll(req, sanitizers, cb); }; + + return function( req, res, next ){ + sanitize( req, function( err, clean ){ + next(); + }); + }; }; diff --git a/schema.js b/schema.js index ca69ccfb..ecb66cc5 100644 --- a/schema.js +++ b/schema.js @@ -37,7 +37,11 @@ module.exports = Joi.object().keys({ timeout: Joi.number().integer().optional().default(250).min(0), retries: Joi.number().integer().optional().default(3).min(0), }).unknown(false).requiredKeys('url') - }).unknown(false).default({}) // default api.services to an empty object + }).unknown(false).default({}), // default api.services to an empty object + defaultParameters: Joi.object().keys({ + 'focus.point.lat': Joi.number(), + 'focus.point.lon': Joi.number(), + }).unknown(true).default({}) }).requiredKeys('version', 'indexName', 'host').unknown(true), esclient: Joi.object().keys({ diff --git a/test/unit/run.js b/test/unit/run.js index 92d258fa..b60f06c5 100644 --- a/test/unit/run.js +++ b/test/unit/run.js @@ -64,6 +64,7 @@ var tests = [ require('./sanitizer/_ids'), require('./sanitizer/_iso2_to_iso3'), require('./sanitizer/_layers'), + require('./sanitizer/_location_bias'), require('./sanitizer/_city_name_standardizer'), require('./sanitizer/_single_scalar_parameters'), require('./sanitizer/_size'), diff --git a/test/unit/sanitizer/_location_bias.js b/test/unit/sanitizer/_location_bias.js new file mode 100644 index 00000000..6778f045 --- /dev/null +++ b/test/unit/sanitizer/_location_bias.js @@ -0,0 +1,109 @@ +const setup = require('../../../sanitizer/_location_bias'); + +module.exports.tests = {}; + +module.exports.tests.setLocationBias = function(test, common) { + test('set focus point', t => { + const defaultParameters = { // specify focus point latitude and longitude + 'focus.point.lat': 12.12121212, + 'focus.point.lon': 21.21212121 + }; + const locationBias = setup(defaultParameters); + const raw = {}; + const expected = { + 'focus.point.lat': 12.12121212, + 'focus.point.lon': 21.21212121 + }; + + locationBias(raw, undefined); + t.deepEqual(raw, expected, 'focus point should be set'); + t.end(); + + }); + + test('undefined raw', t => { + const defaultParameters = { + 'focus.point.lat': 12.12121212, + 'focus.point.lon': 21.21212121 + }; + const locationBias = setup(defaultParameters); + + locationBias(undefined, undefined); + t.deepEqual(undefined, undefined, 'should be unmodified' ); + t.end(); + }); + + test('focusPointLat is undefined', t => { + const defaultParameters = { + 'focus.point.lon': 12.2121212 + }; + const locationBias = setup(defaultParameters); + const raw = {}; + const expected = {}; + + locationBias(raw, undefined); + t.deepEqual(raw, expected, 'should be unmodified' ); + t.end(); + }); + + test('focusPointLon is undefined', t => { + const defaultParameters = { + 'focus.point.lat': 12.2121212 + }; + const locationBias = setup(defaultParameters); + const raw = {}; + const expected = {}; + + locationBias(raw, undefined); + t.deepEqual(raw, expected, 'should be unmodified' ); + t.end(); + }); + + test('raw has focus.point.lon already', t => { + const defaultParameters = { + 'focus.point.lon': 12.2121212, + 'focus.point.lat': 12.2121212 + }; + const locationBias = setup(defaultParameters); + const raw = { + 'focus.point.lon': 43.4343434 + }; + const expected = { + 'focus.point.lon': 43.4343434 + }; + + locationBias(raw, undefined); + t.deepEqual(raw, expected, 'should be unmodified' ); + t.end(); + }); + + test('raw has focus.point.lat already', t => { + const defaultParameters = { + 'focus.point.lon': 12.2121212, + 'focus.point.lat': 12.2121212 + }; + const locationBias = setup(defaultParameters); + const raw = { + 'focus.point.lat': 34.3434343 + }; + const expected = { + 'focus.point.lat': 34.3434343 + }; + + locationBias(raw, undefined); + t.deepEqual(raw, expected, 'should be unmodified' ); + t.end(); + }); + +}; + +module.exports.all = (tape, common) => { + + function test(name, testFunction) { + return tape(`SANITIZE _location_bias: ${name}`, testFunction); + } + + for( var testCase in module.exports.tests ){ + module.exports.tests[testCase](test, common); + } +}; diff --git a/test/unit/sanitizer/autocomplete.js b/test/unit/sanitizer/autocomplete.js index 29db50ee..9f0b4a3a 100644 --- a/test/unit/sanitizer/autocomplete.js +++ b/test/unit/sanitizer/autocomplete.js @@ -1,21 +1,126 @@ -var autocomplete = require('../../../sanitizer/autocomplete'); +const proxyquire = require('proxyquire').noCallThru(); +const _ = require('lodash'); module.exports.tests = {}; module.exports.tests.sanitizers = function(test, common) { - test('check sanitizer list', function (t) { - var expected = [ - 'singleScalarParameters', 'text', 'tokenizer', 'size', 'layers', 'sources', - 'sources_and_layers', 'private', 'geo_autocomplete', 'boundary_country', 'categories' + test('verify that all sanitizers were called as expected', function(t) { + var called_sanitizers = []; + + var autocomplete = proxyquire('../../../sanitizer/autocomplete', { + '../sanitizer/_single_scalar_parameters': () => { + called_sanitizers.push('_single_scalar_parameters'); + return { errors: [], warnings: [] }; + }, + '../sanitizer/_text_addressit': () => { + called_sanitizers.push('_text_addressit'); + return { errors: [], warnings: [] }; + }, + '../sanitizer/_tokenizer': () => { + called_sanitizers.push('_tokenizer'); + return { errors: [], warnings: [] }; + }, + '../sanitizer/_size': function() { + if (_.isEqual(_.values(arguments), [10, 10, 10])) { + return () => { + called_sanitizers.push('_size'); + return { errors: [], warnings: [] }; + }; + + } else { + throw new Error('incorrect parameters passed to _size'); + } + + }, + '../sanitizer/_targets': (type) => { + if (['layers', 'sources'].indexOf(type) !== -1) { + return () => { + called_sanitizers.push(`_targets/${type}`); + return { errors: [], warnings: [] }; + }; + + } + else { + throw new Error('incorrect parameters passed to _targets'); + } + + }, + '../sanitizer/_sources_and_layers': () => { + called_sanitizers.push('_sources_and_layers'); + return { errors: [], warnings: [] }; + }, + '../sanitizer/_flag_bool': function() { + if (arguments[0] === 'private' && arguments[1] === false) { + return () => { + called_sanitizers.push('_flag_bool'); + return { errors: [], warnings: [] }; + }; + + } + else { + throw new Error('incorrect parameters passed to _flag_bool'); + } + + }, + '../sanitizer/_location_bias': (defaultParameters) => { + if (defaultParameters.key === 'value'){ + return () => { + called_sanitizers.push('_location_bias'); + return { errors: [], warnings: [] }; + }; + } else { + throw new Error('incorrect parameter passed to _location_bias'); + } + }, + '../sanitizer/_geo_autocomplete': () => { + called_sanitizers.push('_geo_autocomplete'); + return { errors: [], warnings: [] }; + }, + '../sanitizer/_boundary_country': () => { + called_sanitizers.push('_boundary_country'); + return { errors: [], warnings: [] }; + }, + '../sanitizer/_categories': () => { + called_sanitizers.push('_categories'); + return { errors: [], warnings: [] }; + }, + }); + + const expected_sanitizers = [ + '_single_scalar_parameters', + '_text_addressit', + '_tokenizer', + '_size', + '_targets/layers', + '_targets/sources', + '_sources_and_layers', + '_flag_bool', + '_location_bias', + '_geo_autocomplete', + '_boundary_country', + '_categories' ]; - t.deepEqual(Object.keys(autocomplete.sanitizer_list), expected); - t.end(); + + const req = {}; + const res = {}; + + const middleware = autocomplete.middleware({ + defaultParameters: { + key: 'value' + } + }); + + middleware(req, res, () => { + t.deepEquals(called_sanitizers, expected_sanitizers); + t.end(); + }); + }); }; module.exports.all = function (tape, common) { function test(name, testFunction) { - return tape('SANTIZE /autocomplete ' + name, testFunction); + return tape('SANITIZE /autocomplete ' + name, testFunction); } for( var testCase in module.exports.tests ){ diff --git a/test/unit/sanitizer/search.js b/test/unit/sanitizer/search.js index e7a8e3fe..214c9cbc 100644 --- a/test/unit/sanitizer/search.js +++ b/test/unit/sanitizer/search.js @@ -87,7 +87,18 @@ module.exports.tests.sanitize = (test, common) => { '../sanitizer/_geonames_warnings': () => { called_sanitizers.push('_geonames_warnings'); return { errors: [], warnings: [] }; + }, + '../sanitizer/_location_bias': (defaultParameters) => { + if (defaultParameters.key === 'value'){ + return () => { + called_sanitizers.push('_location_bias'); + return { errors: [], warnings: [] }; + }; + } else { + throw new Error('incorrect parameter passed to _location_bias'); + } } + }); const expected_sanitizers = [ @@ -101,6 +112,7 @@ module.exports.tests.sanitize = (test, common) => { '_targets/sources', '_sources_and_layers', '_flag_bool', + '_location_bias', '_geo_search', '_boundary_country', '_categories', @@ -110,17 +122,24 @@ module.exports.tests.sanitize = (test, common) => { const req = {}; const res = {}; - search.middleware(req, res, () => { + const middleware = search.middleware({ + defaultParameters: { + key: 'value' + } + }); + + middleware(req, res, () => { t.deepEquals(called_sanitizers, expected_sanitizers); t.end(); }); + }); }; module.exports.all = (tape, common) => { function test(name, testFunction) { - return tape(`SANTIZE /search ${name}`, testFunction); + return tape(`SANITIZE /search ${name}`, testFunction); } for( const testCase in module.exports.tests ){ diff --git a/test/unit/sanitizer/structured_geocoding.js b/test/unit/sanitizer/structured_geocoding.js index 133be462..3cc10432 100644 --- a/test/unit/sanitizer/structured_geocoding.js +++ b/test/unit/sanitizer/structured_geocoding.js @@ -83,6 +83,16 @@ module.exports.tests.sanitize = function(test, common) { called_sanitizers.push('_categories'); return { errors: [], warnings: [] }; }, + '../sanitizer/_location_bias': function (defaultParameters) { + if (defaultParameters.key === 'value'){ + return () => { + called_sanitizers.push('_location_bias'); + return { errors: [], warnings: [] }; + }; + } else { + throw new Error('incorrect parameter passed to _location_bias'); + } + } }); var expected_sanitizers = [ @@ -96,6 +106,7 @@ module.exports.tests.sanitize = function(test, common) { '_targets/sources', '_sources_and_layers', '_flag_bool', + '_location_bias', '_geo_search', '_boundary_country', '_categories' @@ -104,17 +115,24 @@ module.exports.tests.sanitize = function(test, common) { var req = {}; var res = {}; - search.middleware(req, res, function(){ + const middleware = search.middleware({ + defaultParameters: { + key: 'value' + } + }); + + middleware(req, res, function(){ t.deepEquals(called_sanitizers, expected_sanitizers); t.end(); }); + }); }; module.exports.all = function (tape, common) { function test(name, testFunction) { - return tape('SANTIZE /structured ' + name, testFunction); + return tape('SANITIZE /structured ' + name, testFunction); } for( var testCase in module.exports.tests ){ diff --git a/test/unit/schema.js b/test/unit/schema.js index c3cf25de..d4273207 100644 --- a/test/unit/schema.js +++ b/test/unit/schema.js @@ -25,6 +25,10 @@ module.exports.tests.completely_valid = (test, common) => { placeholder: { url: 'http://locahost' } + }, + defaultParameters: { + 'focus.point.lat': 19, + 'focus.point.lon': 91 } }, esclient: { @@ -433,6 +437,80 @@ module.exports.tests.api_validation = (test, common) => { }); + test('non-number defaultParameters.focus.point.lat should throw error', (t) => { + [null, 'string', {}, [], false].forEach((value) => { + const config = { + api: { + version: 'version value', + indexName: 'index name value', + host: 'host value', + defaultParameters: { + 'focus.point.lat': value + } + }, + esclient: {} + }; + + const result = Joi.validate(config, schema); + + t.equals(result.error.details.length, 1); + t.equals(result.error.details[0].message, '"focus.point.lat" must be a number'); + + }); + + t.end(); + + }); + + test('non-number defaultParameters.focus.point.lon should throw error', (t) => { + [null, 'string', {}, [], false].forEach((value) => { + const config = { + api: { + version: 'version value', + indexName: 'index name value', + host: 'host value', + defaultParameters: { + 'focus.point.lon': value + } + }, + esclient: {} + }; + + const result = Joi.validate(config, schema); + + t.equals(result.error.details.length, 1); + t.equals(result.error.details[0].message, '"focus.point.lon" must be a number'); + + }); + + t.end(); + + }); + + test('non-object api.defaultParameters should throw error', (t) => { + [null, 17, false, [], 'string'].forEach((value) => { + var config = { + api: { + version: 'version value', + indexName: 'index name value', + host: 'host value', + defaultParameters: value + }, + esclient: {} + }; + + const result = Joi.validate(config, schema); + + t.equals(result.error.details.length, 1); + t.equals(result.error.details[0].message, '"defaultParameters" must be an object'); + + }); + + t.end(); + + }); + + }; module.exports.tests.api_services_validation = (test, common) => {