From 5e1da82688afcacf4d765f7d76625f0402a595cb Mon Sep 17 00:00:00 2001 From: Spencer Alger Date: Tue, 2 Jun 2015 11:11:30 -0700 Subject: [PATCH] added filterPath support to master api and enhanced yaml testing --- docs/api_conventions.asciidoc | 4 + grunt/config/mochacov.js | 5 +- package.json | 4 +- scripts/generate/js_api.js | 8 +- scripts/generate/overrides.js | 21 ++++ scripts/generate/templates/api_file.tmpl | 8 +- .../templates/client_action_proxy.tmpl | 2 +- src/lib/client_action.js | 112 ++++++++++-------- test/integration/yaml_suite/yaml_doc.js | 43 +++++-- 9 files changed, 139 insertions(+), 68 deletions(-) diff --git a/docs/api_conventions.asciidoc b/docs/api_conventions.asciidoc index 842e6a14b..58a3bfc2a 100755 --- a/docs/api_conventions.asciidoc +++ b/docs/api_conventions.asciidoc @@ -17,6 +17,10 @@ NOTE: the https://github.com/fullscale/elastic.js[elastic.js] library can be use + `Number, Number[]` -- HTTP status codes which should not be considered errors for this request. +`filterPath`:: ++ +`String|String[]` -- Starting in **elasticsearch 2.0** the `filterPath` parameter can be passed to any API to filter it's reponse values. See the https://www.elastic.co/guide/en/elasticsearch/reference/master/common-options.html#_response_filtering[elasticsearch response filtering docs] for more information. + === Config values you can override per request * `requestTimeout` -- <> * `maxRetries` -- <> diff --git a/grunt/config/mochacov.js b/grunt/config/mochacov.js index 0cc1d5feb..4c50dfb7d 100644 --- a/grunt/config/mochacov.js +++ b/grunt/config/mochacov.js @@ -38,7 +38,10 @@ var config = { }, integration: { - src: null + src: null, + options: { + reporter: 'spec' + } }, jenkins_integration: { diff --git a/package.json b/package.json index 49bc614e0..cc9220738 100644 --- a/package.json +++ b/package.json @@ -64,7 +64,7 @@ "js-yaml": "~2.1.3", "load-grunt-config": "~0.7.0", "load-grunt-tasks": "~0.2.0", - "mocha": "~1.21.4", + "mocha": "^2.2.5", "mocha-lcov-reporter": "0.0.1", "mocha-screencast-reporter": "~0.1.4", "moment": "~2.4.0", @@ -98,4 +98,4 @@ "node": ">=0.8 <=0.12", "iojs": ">=1.5" } -} \ No newline at end of file +} diff --git a/scripts/generate/js_api.js b/scripts/generate/js_api.js index eff1a6a5e..e70a4eba1 100644 --- a/scripts/generate/js_api.js +++ b/scripts/generate/js_api.js @@ -29,7 +29,7 @@ module.exports = function (branch, done) { var m; // master === the highest version number - if (branch === 'master') return '999.999.999'; + if (branch === 'master') return '2.0.0'; // n.m -> n.m.0 if (m = branch.match(/^\d+\.\d+$/)) return branch + '.0'; // n.x -> n.(maxVersion + 1).0 @@ -48,7 +48,8 @@ module.exports = function (branch, done) { return _.merge(overrides, _.omit(rule, 'version')); }, { aliases: {}, - paramAsBody: {} + paramAsBody: {}, + clientActionModifier: false }); var steps = [ @@ -106,7 +107,8 @@ module.exports = function (branch, done) { apiSpec = { actions: groups.normal || [], proxies: groups.proxies || [], - namespaces: _.unique(namespaces.sort(), true) + namespaces: _.unique(namespaces.sort(), true), + clientActionModifier: overrides.clientActionModifier }; var create = _.assign({}, _.find(apiSpec.actions, { name: 'index' }), { diff --git a/scripts/generate/overrides.js b/scripts/generate/overrides.js index 73e79461e..c193565ae 100644 --- a/scripts/generate/overrides.js +++ b/scripts/generate/overrides.js @@ -177,5 +177,26 @@ module.exports = [ '/{index}/_aliases/{name}' ] } + }, + { + version: '>=2.0.0', + +/* jshint ignore:start */ + +// strange indentation makes pretty api files +clientActionModifier: +function (spec) { + return require('../utils').merge(spec, { + params: { + filterPath: { + type: 'list', + name: 'filter_path' + } + } + }); +} + +/* jshint ignore:end */ + } ]; \ No newline at end of file diff --git a/scripts/generate/templates/api_file.tmpl b/scripts/generate/templates/api_file.tmpl index 82cb11819..7b0a02895 100644 --- a/scripts/generate/templates/api_file.tmpl +++ b/scripts/generate/templates/api_file.tmpl @@ -1,7 +1,11 @@ /* jshint maxlen: false */ -var ca = require('../client_action').factory; -var proxy = require('../client_action').proxyFactory; +var ca = require('../client_action').<% + +if (!clientActionModifier) print('factory'); +else print('makeFactoryWithModifier(' + clientActionModifier.toString() + ')'); + +%>; var namespace = require('../client_action').namespaceFactory; var api = module.exports = {}; diff --git a/scripts/generate/templates/client_action_proxy.tmpl b/scripts/generate/templates/client_action_proxy.tmpl index c9d54cf29..f5dd98929 100644 --- a/scripts/generate/templates/client_action_proxy.tmpl +++ b/scripts/generate/templates/client_action_proxy.tmpl @@ -9,7 +9,7 @@ _.each(allParams, function(param, paramName) { %> } %><% }) %> */ -api<%= (location[0] === '[' ? '' : '.') + location %> = proxy(<%= 'api' + (proxy[0] === '[' ? '' : '.') + proxy %><% +api<%= (location[0] === '[' ? '' : '.') + location %> = ca.proxy(<%= 'api' + (proxy[0] === '[' ? '' : '.') + proxy %><% if (typeof transformBody === 'string') { %>, { transform: function (params) { <%= indent(transformBody, 4) %> diff --git a/src/lib/client_action.js b/src/lib/client_action.js index 576e82018..734934b57 100644 --- a/src/lib/client_action.js +++ b/src/lib/client_action.js @@ -1,15 +1,23 @@ +var _ = require('./utils'); + +/** + * Constructs a client action factory that uses specific defaults + * @type {Function} + */ +exports.makeFactoryWithModifier = makeFactoryWithModifier; + /** * Constructs a function that can be called to make a request to ES * @type {Function} */ -exports.factory = factory; +exports.factory = makeFactoryWithModifier(); /** * Constructs a proxy to another api method * @type {Function} */ -exports.proxyFactory = proxyFactory; +exports.proxyFactory = exports.factory.proxy; // export so that we can test this exports._resolveUrl = resolveUrl; @@ -25,60 +33,66 @@ exports.namespaceFactory = function () { return ClientNamespace; }; -var _ = require('./utils'); +function makeFactoryWithModifier(modifier) { + modifier = modifier || _.identity; -function factory(spec) { - if (!_.isPlainObject(spec.params)) { - spec.params = {}; - } + var factory = function (spec) { + spec = modifier(spec); - if (!spec.method) { - spec.method = 'GET'; - } - - function action(params, cb) { - if (typeof params === 'function') { - cb = params; - params = {}; - } else { - params = params || {}; - cb = typeof cb === 'function' ? cb : null; + if (!_.isPlainObject(spec.params)) { + spec.params = {}; } - try { - return exec(this.transport, spec, _.clone(params), cb); - } catch (e) { - if (typeof cb === 'function') { - _.nextTick(cb, e); + if (!spec.method) { + spec.method = 'GET'; + } + + function action(params, cb) { + if (typeof params === 'function') { + cb = params; + params = {}; } else { - var def = this.transport.defer(); - def.reject(e); - return def.promise; + params = params || {}; + cb = typeof cb === 'function' ? cb : null; + } + + try { + return exec(this.transport, spec, _.clone(params), cb); + } catch (e) { + if (typeof cb === 'function') { + _.nextTick(cb, e); + } else { + var def = this.transport.defer(); + def.reject(e); + return def.promise; + } } } - } - action.spec = spec; + action.spec = spec; - return action; -} - -function proxyFactory(fn, spec) { - return function (params, cb) { - if (typeof params === 'function') { - cb = params; - params = {}; - } else { - params = params || {}; - cb = typeof cb === 'function' ? cb : null; - } - - if (spec.transform) { - spec.transform(params); - } - - return fn.call(this, params, cb); + return action; }; + + factory.proxy = function (fn, spec) { + return function (params, cb) { + if (typeof params === 'function') { + cb = params; + params = {}; + } else { + params = params || {}; + cb = typeof cb === 'function' ? cb : null; + } + + if (spec.transform) { + spec.transform(params); + } + + return fn.call(this, params, cb); + }; + }; + + return factory; } var castType = { @@ -288,9 +302,7 @@ function exec(transport, spec, params, cb) { }, []); } - var key, paramSpec; - - for (key in params) { + for (var key in params) { if (params.hasOwnProperty(key) && params[key] != null) { switch (key) { case 'body': @@ -305,7 +317,7 @@ function exec(transport, spec, params, cb) { request.method = _.toUpperString(params[key]); break; default: - paramSpec = spec.params[key]; + var paramSpec = spec.params[key]; if (paramSpec) { // param keys don't always match the param name, in those cases it's stored in the param def as "name" paramSpec.name = paramSpec.name || key; diff --git a/test/integration/yaml_suite/yaml_doc.js b/test/integration/yaml_suite/yaml_doc.js index f3a3185bc..02da25074 100644 --- a/test/integration/yaml_suite/yaml_doc.js +++ b/test/integration/yaml_suite/yaml_doc.js @@ -12,7 +12,7 @@ var expect = require('expect.js'); var clientManager = require('./client_manager'); var inspect = require('util').inspect; -var implementedFeatures = ['gtelte', 'regex', 'benchmark']; +var implementedFeatures = ['gtelte', 'regex', 'benchmark', 'stash_in_path']; /** * The version that ES is running, in comparable string form XXX-XXX-XXX, fetched when needed @@ -239,21 +239,25 @@ YamlDoc.prototype = { * @return {*} - The value requested, or undefined if it was not found */ get: function (path, from) { - + var self = this; var log = process.env.LOG_GETS && !from ? console.log.bind(console) : function () {}; var i; if (path === '$body') { // shortcut, the test just wants the whole body - return this._last_requests_response; + return self._last_requests_response; + } else if (path) { + path = path.replace(/\.\$([a-zA-Z0-9_]+)/g, function (m, name) { + return '.' + self._stash[name]; + }); } if (!from) { if (path[0] === '$') { - from = this._stash; + from = self._stash; path = path.substring(1); } else { - from = this._last_requests_response; + from = self._last_requests_response; } } @@ -331,6 +335,15 @@ YamlDoc.prototype = { do_do: function (args, done) { var catcher; + if (process.env.LOG_DO) { + var __done = done; + done = function (err, resp) { + console.log('doing', clientActionName, 'with', params); + console.log('got', resp); + __done(err, resp); + }; + } + // resolve the catch arg to a value used for matching once the request is complete switch (args.catch) { case void 0: @@ -475,7 +488,12 @@ YamlDoc.prototype = { * @return {undefined} */ do_is_true: function (path) { - expect(Boolean(this.get(path))).to.be(true, 'path: ' + path); + var val = this.get(path); + try { + expect(Boolean(val)).to.be(true, 'path: ' + path); + } catch (e) { + throw new Error('expected path "' + path + '" to be true but got ' + val); + } }, /** @@ -486,7 +504,12 @@ YamlDoc.prototype = { * @return {undefined} */ do_is_false: function (path) { - expect(Boolean(this.get(path))).to.be(false, 'path: ' + path); + var val = this.get(path); + try { + expect(Boolean(val)).to.be(false, 'path: ' + path); + } catch (e) { + throw new Error('expected path "' + path + '" to be false but got ' + val); + } }, /** @@ -529,8 +552,10 @@ YamlDoc.prototype = { return _.each(val, recurse); } - if (_.isString(val) && val[0] === '$') { - lvl[key] = self.get(val); + if (_.isString(val)) { + lvl[key] = val.replace(/\$[a-zA-Z0-9_]+/g, function (name) { + return self.get(name); + }); } });