Added the browser based test suite, have it running automatically via PhantomJS with grunt, all tests are passing except one, which requires PhantomJS send a body with a DELETE request

This commit is contained in:
Spencer Alger
2013-11-05 09:57:56 -07:00
parent 4273ffc2c7
commit 7e6fa479ad
34 changed files with 58054 additions and 1008 deletions

View File

@ -21,7 +21,8 @@ module.exports = function (grunt) {
'<%= grunt.template.today("yyyy-mm-dd") %>\n' +
'<%= pkg.homepage ? " * " + pkg.homepage + "\\n" : "" %>' +
' * Copyright (c) <%= grunt.template.today("yyyy") %> <%= pkg.author.name %>;' +
' Licensed <%= pkg.license %> */\n\n'
' Licensed <%= pkg.license %> */\n' +
' // built using browserify\n\n'
},
clean: {
dist: {
@ -48,18 +49,11 @@ module.exports = function (grunt) {
src: [
'src/**/*.js',
'scripts/**/*.js',
'test/**/*.js -test/browser_integration/yaml_tests.js',
'Gruntfile.js'
],
options: {
jshintrc: '.jshintrc'
}
},
tests: {
src: [
'test/**/*.js'
],
options: {
jshintrc: 'test/.jshintrc'
jshintrc: true
}
}
},
@ -85,37 +79,66 @@ module.exports = function (grunt) {
'scripts/generate/js_api'
]
},
yaml_suite: {
yaml_tests: {
cmd: 'node',
args: [
'scripts/generate/yaml_tests'
]
}
},
start: {
integration_server: {
cmd: 'node',
args: [
'test/browser_integration/server.js'
]
}
},
browserify: {
generic: {
client: {
files: {
'<%= distDir %>/elasticsearch.js': 'src/elasticsearch.js'
},
options: {
standalone: 'true',
standalone: 'elasticsearch',
ignore: _.union(sharedBrowserfyExclusions, [
'src/lib/connectors/jquery.js',
'src/lib/connectors/angular.js'
])
}
},
angular: {
angular_client: {
files: {
'<%= distDir %>/elasticsearch.angular.js': ['src/elasticsearch.angular.js']
},
options: {
standalone: 'true',
standalone: 'elasticsearch',
ignore: _.union(sharedBrowserfyExclusions, [
'src/lib/connectors/jquery.js',
'src/lib/connectors/xhr.js'
])
}
},
yaml_suite: {
files: {
'test/browser_integration/yaml_tests.js': ['test/integration/yaml_suite/index.js']
},
options: {
external: [
'optimist'
]
}
}
},
concat: {
dist_banners: {
files: {
'<%= distDir %>/elasticsearch.js': ['<%= distDir %>/elasticsearch.js'],
'<%= distDir %>/elasticsearch.angular.js': ['<%= distDir %>/elasticsearch.angular.js']
},
options: {
banner: '<%= meta.banner %>'
}
}
},
uglify: {
@ -134,68 +157,48 @@ module.exports = function (grunt) {
}
}
}
}//,
// docular: {
// groups: [
// {
// groupTitle: 'Node',
// groupId: 'example',
// groupIcon: 'icon-beer',
// sections: [
// {
// id: "client",
// title: "Client",
// scripts: [
// "src/lib/client.js"
// ],
// docs: [],
// rank : {}
// }
// ]
// }
// ],
// }
// ,
// yuidoc: {
// compile: {
// name: '<%= pkg.name %>',
// description: '<%= pkg.description %>',
// version: '<%= pkg.version %>',
// url: '<%= pkg.homepage %>',
// logo: '<%= pkg.logo %>',
// options: {
// paths: 'src',
// themedir: '../yuidoc-bootstrap-theme',
// helpers: [
// '../yuidoc-bootstrap-theme/helpers/helpers.js'
// ],
// outdir: 'docs'
// }
// }
// }
},
mocha: {
yaml_suite: {
options: {
// log: true,
run: true,
urls: [ 'http://localhost:8888' ],
timeout: 10e3,
'--web-security': false
}
}
}
});
// load plugins
// grunt.loadNpmTasks('grunt-docular');
grunt.loadNpmTasks('grunt-mocha');
grunt.loadNpmTasks('grunt-browserify');
grunt.loadNpmTasks('grunt-mocha-test');
grunt.loadNpmTasks('grunt-contrib-clean');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-jshint');
// Default task.
grunt.registerTask('default', [
/*jshint scripturl:true*/
'jshint',
'mochaTest:unit',
'generate:yaml_suite',
'mochaTest:yaml_suite'
'build',
'mochaTest:yaml_suite',
'start:integration_server',
// 'mocha:yaml_suite' -- this will fail because of the way that PhantomJS handle's DELETE requests with body's
]);
grunt.registerTask('build', [
'clean:dist',
'browserify',
'uglify:dist'
'uglify:dist',
'concat:dist_banners',
'generate:yaml_tests',
'generate:js_api'
]);
grunt.task.registerMultiTask('generate', 'used to generate things', function () {
@ -216,4 +219,51 @@ module.exports = function (grunt) {
});
});
var runningProcs = {};
process.on('exit', function () {
_.each(runningProcs, function (proc) {
proc.kill();
});
});
grunt.task.registerMultiTask('start', 'used to start external processes (like servers)', function () {
var self = this;
var proc = require('child_process').spawn(
self.data.cmd,
self.data.args,
{
stdio: ['ignore', 'pipe', 'pipe']
}
);
proc.stdout.on('data', grunt.log.write);
proc.stderr.on('data', function (chunk) {
grunt.log.error(chunk);
proc.kill();
self.ansyc()(new Error('Error output received'));
clearTimeout(timeoutId);
});
runningProcs[self.nameArgs] = proc;
proc.on('close', function (exitCode) {
delete runningProcs[self.nameArgs];
});
// operates asyncronously to give the processes a moment to start up, not sure if there is a signal for "I'm ready"
var timeoutId = setTimeout(self.async(), 1000);
});
grunt.task.registerMultiTask('stop', 'used to stop external processes (like servers)', function () {
var proc = runningProcs[this.nameArgs.replace(/^start:/, 'stop:')];
if (proc) {
proc.kill();
} else {
grunt.log.error(this.nameArgs + ' failed to find active process');
}
});
};

View File

@ -1,4 +1,9 @@
!function(e){"object"==typeof exports?module.exports=e():"function"==typeof define&&define.amd?define(e):"undefined"!=typeof window?window.true=e():"undefined"!=typeof global?global.true=e():"undefined"!=typeof self&&(self.true=e())}(function(){var define,module,exports;
/*! elasticsearch-js - v0.0.1 - 2013-11-05
* https://github.com/elasticsearch/elasticsearch-js
* Copyright (c) 2013 Spencer Alger; Licensed Apache License */
// built using browserify
!function(e){"object"==typeof exports?module.exports=e():"function"==typeof define&&define.amd?define(e):"undefined"!=typeof window?window.elasticsearch=e():"undefined"!=typeof global?global.elasticsearch=e():"undefined"!=typeof self&&(self.elasticsearch=e())}(function(){var define,module,exports;
return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
},{}],2:[function(require,module,exports){
@ -11215,10 +11220,9 @@ api.exists = ca({
* @param {String} params.preference - Specify the node or shard the operation should be performed on (default: random)
* @param {String} params.q - Query in the Lucene query string syntax
* @param {String} params.routing - Specific routing value
* @param {String} params.source - The URL-encoded query definition (instead of using the request body)
* @param {String|ArrayOfStrings|Boolean} params._source - True or false to return the _source field or not, or a list of fields to return
* @param {String|ArrayOfStrings|Boolean} params._sourceExclude - A list of fields to exclude from the returned _source field
* @param {String|ArrayOfStrings|Boolean} params._sourceInclude - A list of fields to extract and return from the _source field
* @param {String|ArrayOfStrings|Boolean} params.source - True or false to return the _source field or not, or a list of fields to return
* @param {String|ArrayOfStrings|Boolean} params.sourceExclude - A list of fields to exclude from the returned _source field
* @param {String|ArrayOfStrings|Boolean} params.sourceInclude - A list of fields to extract and return from the _source field
* @param {String} params.id - The document ID
* @param {String} params.index - The name of the index
* @param {String} params.type - The type of the document
@ -11271,17 +11275,14 @@ api.explain = ca({
type: 'string'
},
source: {
type: 'string'
},
_source: {
type: 'list',
name: '_source'
},
_sourceExclude: {
sourceExclude: {
type: 'list',
name: '_source_exclude'
},
_sourceInclude: {
sourceInclude: {
type: 'list',
name: '_source_include'
}
@ -11315,9 +11316,9 @@ api.explain = ca({
* @param {Boolean} params.realtime - Specify whether to perform the operation in realtime or search mode
* @param {Boolean} params.refresh - Refresh the shard containing the document before performing the operation
* @param {String} params.routing - Specific routing value
* @param {String|ArrayOfStrings|Boolean} params._source - True or false to return the _source field or not, or a list of fields to return
* @param {String|ArrayOfStrings|Boolean} params._sourceExclude - A list of fields to exclude from the returned _source field
* @param {String|ArrayOfStrings|Boolean} params._sourceInclude - A list of fields to extract and return from the _source field
* @param {String|ArrayOfStrings|Boolean} params.source - True or false to return the _source field or not, or a list of fields to return
* @param {String|ArrayOfStrings|Boolean} params.sourceExclude - A list of fields to exclude from the returned _source field
* @param {String|ArrayOfStrings|Boolean} params.sourceInclude - A list of fields to extract and return from the _source field
* @param {String} params.id - The document ID
* @param {String} params.index - The name of the index
* @param {String} [params.type=_all] - The type of the document (use `_all` to fetch the first document matching the ID across all types)
@ -11345,15 +11346,15 @@ api.get = ca({
routing: {
type: 'string'
},
_source: {
source: {
type: 'list',
name: '_source'
},
_sourceExclude: {
sourceExclude: {
type: 'list',
name: '_source_exclude'
},
_sourceInclude: {
sourceInclude: {
type: 'list',
name: '_source_include'
}
@ -12226,6 +12227,63 @@ api.indices.prototype.getAliases = ca({
});
/**
* Perform a [indices.getFieldMapping](http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/indices-get-field-mapping.html) request
*
* @param {Object} params - An object with parameters used to carry out this action
* @param {Boolean} params.includeDefaults - Whether the default mapping values should be returned as well
* @param {String|ArrayOfStrings|Boolean} params.index - A comma-separated list of index names
* @param {String|ArrayOfStrings|Boolean} params.type - A comma-separated list of document types
* @param {String|ArrayOfStrings|Boolean} params.field - A comma-separated list of fields
*/
api.indices.prototype.getFieldMapping = ca({
methods: [
'GET'
],
params: {
includeDefaults: {
type: 'boolean',
name: 'include_defaults'
}
},
urls: [
{
fmt: '/<%=index%>/<%=type%>/_mapping/field/<%=field%>',
req: {
index: {
type: 'list'
},
type: {
type: 'list'
},
field: {
type: 'list'
}
}
},
{
fmt: '/<%=index%>/_mapping/field/<%=field%>',
req: {
index: {
type: 'list'
},
field: {
type: 'list'
}
}
},
{
fmt: '/_mapping/field/<%=field%>',
req: {
field: {
type: 'list'
}
}
}
]
});
/**
* Perform a [indices.getMapping](http://www.elasticsearch.org/guide/reference/api/admin-indices-get-mapping/) request
*
@ -13109,9 +13167,9 @@ api.info = ca({
* @param {String} params.preference - Specify the node or shard the operation should be performed on (default: random)
* @param {Boolean} params.realtime - Specify whether to perform the operation in realtime or search mode
* @param {Boolean} params.refresh - Refresh the shard containing the document before performing the operation
* @param {String|ArrayOfStrings|Boolean} params._source - True or false to return the _source field or not, or a list of fields to return
* @param {String|ArrayOfStrings|Boolean} params._sourceExclude - A list of fields to exclude from the returned _source field
* @param {String|ArrayOfStrings|Boolean} params._sourceInclude - A list of fields to extract and return from the _source field
* @param {String|ArrayOfStrings|Boolean} params.source - True or false to return the _source field or not, or a list of fields to return
* @param {String|ArrayOfStrings|Boolean} params.sourceExclude - A list of fields to exclude from the returned _source field
* @param {String|ArrayOfStrings|Boolean} params.sourceInclude - A list of fields to extract and return from the _source field
* @param {String} params.index - The name of the index
* @param {String} params.type - The type of the document
*/
@ -13133,15 +13191,15 @@ api.mget = ca({
refresh: {
type: 'boolean'
},
_source: {
source: {
type: 'list',
name: '_source'
},
_sourceExclude: {
sourceExclude: {
type: 'list',
name: '_source_exclude'
},
_sourceInclude: {
sourceInclude: {
type: 'list',
name: '_source_include'
}
@ -13450,10 +13508,9 @@ api.scroll = ca({
* @param {String} params.searchType - Search operation type
* @param {Number} params.size - Number of hits to return (default: 10)
* @param {String|ArrayOfStrings|Boolean} params.sort - A comma-separated list of <field>:<direction> pairs
* @param {String} params.source - The URL-encoded request definition using the Query DSL (instead of using request body)
* @param {String|ArrayOfStrings|Boolean} params._source - True or false to return the _source field or not, or a list of fields to return
* @param {String|ArrayOfStrings|Boolean} params._sourceExclude - A list of fields to exclude from the returned _source field
* @param {String|ArrayOfStrings|Boolean} params._sourceInclude - A list of fields to extract and return from the _source field
* @param {String|ArrayOfStrings|Boolean} params.source - True or false to return the _source field or not, or a list of fields to return
* @param {String|ArrayOfStrings|Boolean} params.sourceExclude - A list of fields to exclude from the returned _source field
* @param {String|ArrayOfStrings|Boolean} params.sourceInclude - A list of fields to extract and return from the _source field
* @param {String|ArrayOfStrings|Boolean} params.stats - Specific 'tag' of the request for logging and statistical purposes
* @param {String} params.suggestField - Specify which field to use for suggestions
* @param {String} [params.suggestMode=missing] - Specify suggest mode
@ -13549,17 +13606,14 @@ api.search = ca({
type: 'list'
},
source: {
type: 'string'
},
_source: {
type: 'list',
name: '_source'
},
_sourceExclude: {
sourceExclude: {
type: 'list',
name: '_source_exclude'
},
_sourceInclude: {
sourceInclude: {
type: 'list',
name: '_source_include'
},
@ -13692,7 +13746,7 @@ api.suggest = ca({
* @param {Date|Number} params.timestamp - Explicit timestamp for the document
* @param {Duration} params.ttl - Expiration time for the document
* @param {Number} params.version - Explicit version number for concurrency control
* @param {Number} params.versionType - Explicit version number for concurrency control
* @param {String} params.versionType - Specific version type
* @param {String} params.id - Document ID
* @param {String} params.index - The name of the index
* @param {String} params.type - The type of the document
@ -13754,7 +13808,11 @@ api.update = ca({
type: 'number'
},
versionType: {
type: 'number',
type: 'enum',
options: [
'internal',
'external'
],
name: 'version_type'
}
},
@ -13923,10 +13981,10 @@ var castType = {
}
},
time: function (param, val, name) {
if (val instanceof Date) {
return val.getTime();
} else if (_.isNumeric(val)) {
if (typeof val === 'string' || _.isNumeric(val)) {
return val;
} else if (val instanceof Date) {
return val.getTime();
} else {
throw new TypeError('Invalid ' + name + ': expected some sort of time.');
}
@ -14070,15 +14128,17 @@ function exec(transport, spec, params, cb) {
// build a key list on demand
spec.paramKeys = _.keys(spec.params);
}
var key, param;
var key, param, name;
for (i = 0; i < spec.paramKeys.length; i++) {
key = spec.paramKeys[i];
param = spec.params[key];
// param keys don't always match the param name, in those cases it's stored in the param def as "name"
name = param.name || key;
try {
if (params[key] != null) {
query[key] = castType[param.type] ? castType[param.type](param, params[key], key) : params[key];
if (param['default'] && query[key] === param['default']) {
delete query[key];
query[name] = castType[param.type] ? castType[param.type](param, params[key], key) : params[key];
if (param['default'] && query[name] === param['default']) {
delete query[name];
}
} else if (param.required) {
throw new TypeError('Missing required parameter ' + key);
@ -14137,6 +14197,11 @@ var defaultClasses = {
};
var defaultConfig = {
loggers: [
{
level: 'warning'
}
],
hosts: [
{
host: 'localhost',
@ -14182,24 +14247,28 @@ connectors = _.transform(connectors, function (note, connector, name) {
function ClientConfig(config) {
_.extend(this, defaultConfig, config);
if (this.log) {
// treat log as an alias for loggers in the config.
this.loggers = this.log;
delete this.log;
}
// validate connectionClass
if (typeof this.connectionClass === 'string') {
this.connectionClass = connectors[_.studlyCase(this.connectionClass)];
}
if (typeof this.connectionClass !== 'function') {
if (typeof connectors[this.connectionClass] === 'function') {
this.connectionClass = connectors[this.connectionClass];
} else {
throw new TypeError('Invalid connectionClass "' + this.connectionClass + '". ' +
throw new TypeError('Invalid connectionClass "' + this.connectionClass + '". ' +
'Expected a constructor or one of ' + _.keys(connectors).join(', '));
}
}
// validate selector
if (typeof this.selector === 'string') {
this.selector = selectors[_.camelCase(this.selector)];
}
if (typeof this.selector !== 'function') {
if (_.has(selectors, this.selector)) {
this.selector = selectors[this.selector];
} else {
throw new TypeError('Invalid Selector "' + this.selector + '". ' +
throw new TypeError('Invalid Selector "' + this.selector + '". ' +
'Expected a function or one of ' + _.keys(selectors).join(', '));
}
}
_.each(defaultClasses, function (DefaultClass, prop) {
@ -14452,7 +14521,7 @@ ConnectionPool.prototype.empty = ConnectionPool.prototype.close;
*
* @class connections.Angular
*/
module.exports = AngularConnection;
module.exports = AngularConnector;
var _ = require('../utils');
var ConnectionAbstract = require('../connection');
@ -14460,12 +14529,12 @@ var ConnectionFault = require('../errors').ConnectionFault;
/* global angular */
function AngularConnection(host, config) {
function AngularConnector(host, config) {
ConnectionAbstract.call(this, host, config);
}
_.inherits(AngularConnection, ConnectionAbstract);
_.inherits(AngularConnector, ConnectionAbstract);
AngularConnection.prototype.request = function (params, cb) {
AngularConnector.prototype.request = function (params, cb) {
var timeoutId;
this.$http({
@ -14483,17 +14552,21 @@ AngularConnection.prototype.request = function (params, cb) {
};
// must be overwritten before this connection can be used
AngularConnection.prototype.$http = null;
AngularConnector.prototype.$http = null;
},{"../connection":19,"../errors":22,"../utils":33}],22:[function(require,module,exports){
var _ = require('./utils'),
var process=require("__browserify_process");var _ = require('./utils'),
errors = module.exports;
function ErrorAbstract(msg, constructor) {
this.message = msg;
Error.call(this, this.message);
Error.captureStackTrace(this, constructor);
if (process.browser) {
this.stack = '';
} else {
Error.captureStackTrace(this, constructor);
}
}
_.inherits(ErrorAbstract, Error);
@ -14598,7 +14671,7 @@ _.each(statusCodes, function (name, status) {
errors[status] = StatusCodeError;
});
},{"./utils":33}],23:[function(require,module,exports){
},{"./utils":33,"__browserify_process":12}],23:[function(require,module,exports){
/**
* Class to wrap URLS, formatting them and maintaining their seperate details
* @type {[type]}
@ -14688,12 +14761,8 @@ Host.prototype.makeUrl = function (params) {
// just stringify the hosts query
query = qs.stringify(this.query);
}
// prepend the ? if there is actually a valid query string
if (query) {
query = '?' + query;
}
return this.protocol + '://' + this.host + port + path + query;
return this.protocol + '://' + this.host + port + path + (query ? '?' + query : '');
};
},{"./utils":33,"querystring":6,"url":7}],24:[function(require,module,exports){
@ -14730,7 +14799,7 @@ function Log(config) {
this.config = config || {};
var i;
var output = _.isPlainObject(config.log) ? config.log : 'warning';
var output = config.loggers ? config.loggers : 'warning';
if (_.isString(output) || _.isFinite(output)) {
output = [
@ -14887,7 +14956,7 @@ Log.prototype.addOutput = function (config) {
delete config.level;
config.levels = levels;
var Logger = loggers[config.type];
var Logger = loggers[_.studlyCase(config.type)];
if (Logger) {
return new Logger(config, this);
} else {
@ -14945,7 +15014,7 @@ Log.prototype.info = function (/* ...msg */) {
*/
Log.prototype.debug = function (/* ...msg */) {
if (EventEmitter.listenerCount(this, 'debug')) {
return this.emit('debug', Log.join(arguments) + _.getStackTrace(Log.prototype.debug));
return this.emit('debug', Log.join(arguments) /*+ _.getStackTrace(Log.prototype.debug)*/);
}
};
@ -14963,10 +15032,17 @@ Log.prototype.debug = function (/* ...msg */) {
*/
Log.prototype.trace = function (method, requestUrl, body, responseBody, responseStatus) {
if (EventEmitter.listenerCount(this, 'trace')) {
if (typeof requestUrl === 'object') {
requestUrl = url.format(requestUrl);
if (typeof requestUrl === 'string') {
requestUrl = url.parse(requestUrl, true, true);
}
return this.emit('trace', method, requestUrl, body, responseBody, responseStatus);
requestUrl = _.defaults({
host: 'localhost:9200',
query: _.defaults({
pretty: true
}, requestUrl.query)
}, requestUrl);
delete requestUrl.auth;
return this.emit('trace', method, url.format(requestUrl), body, responseBody, responseStatus);
}
};
@ -15184,10 +15260,9 @@ Console.prototype.setupListeners = function (levels) {
*/
Console.prototype.onError = _.handler(function (e) {
if (console.error && console.trace) {
console.error(e.name === 'Error' ? 'ERROR' : e.name);
console.trace();
console.error(e.name === 'Error' ? 'ERROR' : e.name, e.stack || e.message);
} else {
console.log(e.name === 'Error' ? 'ERROR' : e.name, e.stack);
console.log(e.name === 'Error' ? 'ERROR' : e.name, e.stack || e.message);
}
});
@ -15199,7 +15274,7 @@ Console.prototype.onError = _.handler(function (e) {
* @param {String} msg - The message to be logged
* @return {undefined}
*/
Console.prototype.onWarning = console[console.warn ? 'warn' : 'log'].bind(console, 'WARNING');
Console.prototype.onWarning = _.bindKey(console, console.warn ? 'warn' : 'log', 'WARNING');
/**
* Handler for the bridges "info" event
@ -15209,7 +15284,7 @@ Console.prototype.onWarning = console[console.warn ? 'warn' : 'log'].bind(consol
* @param {String} msg - The message to be logged
* @return {undefined}
*/
Console.prototype.onInfo = console[console.info ? 'info' : 'log'].bind(console, 'INFO');
Console.prototype.onInfo = _.bindKey(console, console.info ? 'info' : 'log', 'INFO');
/**
* Handler for the bridges "debug" event
@ -15219,7 +15294,7 @@ Console.prototype.onInfo = console[console.info ? 'info' : 'log'].bind(console,
* @param {String} msg - The message to be logged
* @return {undefined}
*/
Console.prototype.onDebug = console[console.debug ? 'debug' : 'log'].bind(console, 'DEBUG');
Console.prototype.onDebug = _.bindKey(console, console.debug ? 'debug' : 'log', 'DEBUG');
/**
* Handler for the bridges "trace" event
@ -15234,7 +15309,7 @@ Console.prototype.onTrace = _.handler(function (method, url, body, responseBody,
message += ' -d "' + body.replace(/"/g, '\\"') + '"';
}
message += '\n<- ' + responseStatus + '\n' + responseBody;
console.log('TRACE', message);
console.log('TRACE:\n' + message + '\n\n');
});
},{"../logger":25,"../utils":33}],27:[function(require,module,exports){
@ -15449,7 +15524,7 @@ TransportRequest.prototype._sendReqWithCon = _.handler(function (err, con) {
TransportRequest.prototype._checkRespForFail = _.handler(function (err, body, status) {
if (err && this._remainingRetries) {
this._remainingRetries--;
this._log.info('Connection error, retrying');
this._log.error(err.message, '-- retrying');
this._connectionPool.select(this.bound._sendReqWithCon);
} else {
this._log.info('Request complete');
@ -15684,6 +15759,10 @@ function adjustWordCase(firstWordCap, otherWordsCap, sep) {
if (word.length) {
words.push(word);
}
// add the leading underscore back to strings the had it originally
if (words.lenth && string.charAt(0) === '_') {
words[0] = '_' + words[0];
}
return words.join(sep);
};
}
@ -15937,11 +16016,16 @@ _.makeBoundMethods = function (obj, methods) {
_.noop = function () {};
_.getStackTrace = function (callee) {
var e = {};
Error.captureStackTrace(e, callee || _.getStackTrace);
return '\n' + e.stack.split('\n').slice(1).join('\n');
};
// _.getStackTrace = function (callee) {
// var e = {};
// if (typeof Error.captureStackTrace === 'function') {
// Error.captureStackTrace(e, callee || _.getStackTrace);
// } else {
// e.stack = (new Error()).stack;
// console.log(e.stack);
// }
// return '\n' + e.stack.split('\n').slice(1).join('\n');
// };
module.exports = utils;

File diff suppressed because one or more lines are too long

265
dist/elasticsearch.js vendored
View File

@ -1,4 +1,9 @@
!function(e){"object"==typeof exports?module.exports=e():"function"==typeof define&&define.amd?define(e):"undefined"!=typeof window?window.true=e():"undefined"!=typeof global?global.true=e():"undefined"!=typeof self&&(self.true=e())}(function(){var define,module,exports;
/*! elasticsearch-js - v0.0.1 - 2013-11-05
* https://github.com/elasticsearch/elasticsearch-js
* Copyright (c) 2013 Spencer Alger; Licensed Apache License */
// built using browserify
!function(e){"object"==typeof exports?module.exports=e():"function"==typeof define&&define.amd?define(e):"undefined"!=typeof window?window.elasticsearch=e():"undefined"!=typeof global?global.elasticsearch=e():"undefined"!=typeof self&&(self.elasticsearch=e())}(function(){var define,module,exports;
return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
},{}],2:[function(require,module,exports){
@ -11175,10 +11180,9 @@ api.exists = ca({
* @param {String} params.preference - Specify the node or shard the operation should be performed on (default: random)
* @param {String} params.q - Query in the Lucene query string syntax
* @param {String} params.routing - Specific routing value
* @param {String} params.source - The URL-encoded query definition (instead of using the request body)
* @param {String|ArrayOfStrings|Boolean} params._source - True or false to return the _source field or not, or a list of fields to return
* @param {String|ArrayOfStrings|Boolean} params._sourceExclude - A list of fields to exclude from the returned _source field
* @param {String|ArrayOfStrings|Boolean} params._sourceInclude - A list of fields to extract and return from the _source field
* @param {String|ArrayOfStrings|Boolean} params.source - True or false to return the _source field or not, or a list of fields to return
* @param {String|ArrayOfStrings|Boolean} params.sourceExclude - A list of fields to exclude from the returned _source field
* @param {String|ArrayOfStrings|Boolean} params.sourceInclude - A list of fields to extract and return from the _source field
* @param {String} params.id - The document ID
* @param {String} params.index - The name of the index
* @param {String} params.type - The type of the document
@ -11231,17 +11235,14 @@ api.explain = ca({
type: 'string'
},
source: {
type: 'string'
},
_source: {
type: 'list',
name: '_source'
},
_sourceExclude: {
sourceExclude: {
type: 'list',
name: '_source_exclude'
},
_sourceInclude: {
sourceInclude: {
type: 'list',
name: '_source_include'
}
@ -11275,9 +11276,9 @@ api.explain = ca({
* @param {Boolean} params.realtime - Specify whether to perform the operation in realtime or search mode
* @param {Boolean} params.refresh - Refresh the shard containing the document before performing the operation
* @param {String} params.routing - Specific routing value
* @param {String|ArrayOfStrings|Boolean} params._source - True or false to return the _source field or not, or a list of fields to return
* @param {String|ArrayOfStrings|Boolean} params._sourceExclude - A list of fields to exclude from the returned _source field
* @param {String|ArrayOfStrings|Boolean} params._sourceInclude - A list of fields to extract and return from the _source field
* @param {String|ArrayOfStrings|Boolean} params.source - True or false to return the _source field or not, or a list of fields to return
* @param {String|ArrayOfStrings|Boolean} params.sourceExclude - A list of fields to exclude from the returned _source field
* @param {String|ArrayOfStrings|Boolean} params.sourceInclude - A list of fields to extract and return from the _source field
* @param {String} params.id - The document ID
* @param {String} params.index - The name of the index
* @param {String} [params.type=_all] - The type of the document (use `_all` to fetch the first document matching the ID across all types)
@ -11305,15 +11306,15 @@ api.get = ca({
routing: {
type: 'string'
},
_source: {
source: {
type: 'list',
name: '_source'
},
_sourceExclude: {
sourceExclude: {
type: 'list',
name: '_source_exclude'
},
_sourceInclude: {
sourceInclude: {
type: 'list',
name: '_source_include'
}
@ -12186,6 +12187,63 @@ api.indices.prototype.getAliases = ca({
});
/**
* Perform a [indices.getFieldMapping](http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/indices-get-field-mapping.html) request
*
* @param {Object} params - An object with parameters used to carry out this action
* @param {Boolean} params.includeDefaults - Whether the default mapping values should be returned as well
* @param {String|ArrayOfStrings|Boolean} params.index - A comma-separated list of index names
* @param {String|ArrayOfStrings|Boolean} params.type - A comma-separated list of document types
* @param {String|ArrayOfStrings|Boolean} params.field - A comma-separated list of fields
*/
api.indices.prototype.getFieldMapping = ca({
methods: [
'GET'
],
params: {
includeDefaults: {
type: 'boolean',
name: 'include_defaults'
}
},
urls: [
{
fmt: '/<%=index%>/<%=type%>/_mapping/field/<%=field%>',
req: {
index: {
type: 'list'
},
type: {
type: 'list'
},
field: {
type: 'list'
}
}
},
{
fmt: '/<%=index%>/_mapping/field/<%=field%>',
req: {
index: {
type: 'list'
},
field: {
type: 'list'
}
}
},
{
fmt: '/_mapping/field/<%=field%>',
req: {
field: {
type: 'list'
}
}
}
]
});
/**
* Perform a [indices.getMapping](http://www.elasticsearch.org/guide/reference/api/admin-indices-get-mapping/) request
*
@ -13069,9 +13127,9 @@ api.info = ca({
* @param {String} params.preference - Specify the node or shard the operation should be performed on (default: random)
* @param {Boolean} params.realtime - Specify whether to perform the operation in realtime or search mode
* @param {Boolean} params.refresh - Refresh the shard containing the document before performing the operation
* @param {String|ArrayOfStrings|Boolean} params._source - True or false to return the _source field or not, or a list of fields to return
* @param {String|ArrayOfStrings|Boolean} params._sourceExclude - A list of fields to exclude from the returned _source field
* @param {String|ArrayOfStrings|Boolean} params._sourceInclude - A list of fields to extract and return from the _source field
* @param {String|ArrayOfStrings|Boolean} params.source - True or false to return the _source field or not, or a list of fields to return
* @param {String|ArrayOfStrings|Boolean} params.sourceExclude - A list of fields to exclude from the returned _source field
* @param {String|ArrayOfStrings|Boolean} params.sourceInclude - A list of fields to extract and return from the _source field
* @param {String} params.index - The name of the index
* @param {String} params.type - The type of the document
*/
@ -13093,15 +13151,15 @@ api.mget = ca({
refresh: {
type: 'boolean'
},
_source: {
source: {
type: 'list',
name: '_source'
},
_sourceExclude: {
sourceExclude: {
type: 'list',
name: '_source_exclude'
},
_sourceInclude: {
sourceInclude: {
type: 'list',
name: '_source_include'
}
@ -13410,10 +13468,9 @@ api.scroll = ca({
* @param {String} params.searchType - Search operation type
* @param {Number} params.size - Number of hits to return (default: 10)
* @param {String|ArrayOfStrings|Boolean} params.sort - A comma-separated list of <field>:<direction> pairs
* @param {String} params.source - The URL-encoded request definition using the Query DSL (instead of using request body)
* @param {String|ArrayOfStrings|Boolean} params._source - True or false to return the _source field or not, or a list of fields to return
* @param {String|ArrayOfStrings|Boolean} params._sourceExclude - A list of fields to exclude from the returned _source field
* @param {String|ArrayOfStrings|Boolean} params._sourceInclude - A list of fields to extract and return from the _source field
* @param {String|ArrayOfStrings|Boolean} params.source - True or false to return the _source field or not, or a list of fields to return
* @param {String|ArrayOfStrings|Boolean} params.sourceExclude - A list of fields to exclude from the returned _source field
* @param {String|ArrayOfStrings|Boolean} params.sourceInclude - A list of fields to extract and return from the _source field
* @param {String|ArrayOfStrings|Boolean} params.stats - Specific 'tag' of the request for logging and statistical purposes
* @param {String} params.suggestField - Specify which field to use for suggestions
* @param {String} [params.suggestMode=missing] - Specify suggest mode
@ -13509,17 +13566,14 @@ api.search = ca({
type: 'list'
},
source: {
type: 'string'
},
_source: {
type: 'list',
name: '_source'
},
_sourceExclude: {
sourceExclude: {
type: 'list',
name: '_source_exclude'
},
_sourceInclude: {
sourceInclude: {
type: 'list',
name: '_source_include'
},
@ -13652,7 +13706,7 @@ api.suggest = ca({
* @param {Date|Number} params.timestamp - Explicit timestamp for the document
* @param {Duration} params.ttl - Expiration time for the document
* @param {Number} params.version - Explicit version number for concurrency control
* @param {Number} params.versionType - Explicit version number for concurrency control
* @param {String} params.versionType - Specific version type
* @param {String} params.id - Document ID
* @param {String} params.index - The name of the index
* @param {String} params.type - The type of the document
@ -13714,7 +13768,11 @@ api.update = ca({
type: 'number'
},
versionType: {
type: 'number',
type: 'enum',
options: [
'internal',
'external'
],
name: 'version_type'
}
},
@ -13883,10 +13941,10 @@ var castType = {
}
},
time: function (param, val, name) {
if (val instanceof Date) {
return val.getTime();
} else if (_.isNumeric(val)) {
if (typeof val === 'string' || _.isNumeric(val)) {
return val;
} else if (val instanceof Date) {
return val.getTime();
} else {
throw new TypeError('Invalid ' + name + ': expected some sort of time.');
}
@ -14030,15 +14088,17 @@ function exec(transport, spec, params, cb) {
// build a key list on demand
spec.paramKeys = _.keys(spec.params);
}
var key, param;
var key, param, name;
for (i = 0; i < spec.paramKeys.length; i++) {
key = spec.paramKeys[i];
param = spec.params[key];
// param keys don't always match the param name, in those cases it's stored in the param def as "name"
name = param.name || key;
try {
if (params[key] != null) {
query[key] = castType[param.type] ? castType[param.type](param, params[key], key) : params[key];
if (param['default'] && query[key] === param['default']) {
delete query[key];
query[name] = castType[param.type] ? castType[param.type](param, params[key], key) : params[key];
if (param['default'] && query[name] === param['default']) {
delete query[name];
}
} else if (param.required) {
throw new TypeError('Missing required parameter ' + key);
@ -14097,6 +14157,11 @@ var defaultClasses = {
};
var defaultConfig = {
loggers: [
{
level: 'warning'
}
],
hosts: [
{
host: 'localhost',
@ -14142,24 +14207,28 @@ connectors = _.transform(connectors, function (note, connector, name) {
function ClientConfig(config) {
_.extend(this, defaultConfig, config);
if (this.log) {
// treat log as an alias for loggers in the config.
this.loggers = this.log;
delete this.log;
}
// validate connectionClass
if (typeof this.connectionClass === 'string') {
this.connectionClass = connectors[_.studlyCase(this.connectionClass)];
}
if (typeof this.connectionClass !== 'function') {
if (typeof connectors[this.connectionClass] === 'function') {
this.connectionClass = connectors[this.connectionClass];
} else {
throw new TypeError('Invalid connectionClass "' + this.connectionClass + '". ' +
throw new TypeError('Invalid connectionClass "' + this.connectionClass + '". ' +
'Expected a constructor or one of ' + _.keys(connectors).join(', '));
}
}
// validate selector
if (typeof this.selector === 'string') {
this.selector = selectors[_.camelCase(this.selector)];
}
if (typeof this.selector !== 'function') {
if (_.has(selectors, this.selector)) {
this.selector = selectors[this.selector];
} else {
throw new TypeError('Invalid Selector "' + this.selector + '". ' +
throw new TypeError('Invalid Selector "' + this.selector + '". ' +
'Expected a function or one of ' + _.keys(selectors).join(', '));
}
}
_.each(defaultClasses, function (DefaultClass, prop) {
@ -14411,7 +14480,7 @@ ConnectionPool.prototype.empty = ConnectionPool.prototype.close;
*
* @class connections.Xhr
*/
module.exports = XhrConnection;
module.exports = XhrConnector;
/* jshint browser:true */
@ -14419,11 +14488,12 @@ var _ = require('../utils');
var ConnectionAbstract = require('../connection');
var ConnectionFault = require('../errors').ConnectionFault;
var TimeoutError = require('../errors').RequestTimeout;
var asyncDefault = !(navigator && /PhantomJS/i.test(navigator.userAgent));
function XhrConnection(host, config) {
function XhrConnector(host, config) {
ConnectionAbstract.call(this, host, config);
}
_.inherits(XhrConnection, ConnectionAbstract);
_.inherits(XhrConnector, ConnectionAbstract);
/**
* Simply returns an XHR object cross browser
@ -14454,43 +14524,53 @@ if (!getXhr) {
throw new Error('getXhr(): XMLHttpRequest not available');
}
XhrConnection.prototype.request = function (params, cb) {
XhrConnector.prototype.request = function (params, cb) {
var xhr = getXhr();
var timeout = params.timeout ? params.timeout : 10000;
var timeoutId;
var url = this.host.makeUrl(params);
var log = this.config.log;
var async = params.async === false ? false : asyncDefault;
if (params.auth) {
xhr.open(params.method, url, true, params.auth.user, params.auth.pass);
xhr.open(params.method, url, async, params.auth.user, params.auth.pass);
} else {
xhr.open(params.method, url, true);
xhr.open(params.method, url, async);
}
xhr.onreadystatechange = function (e) {
if (xhr.readyState === 4) {
clearTimeout(timeoutId);
cb(xhr.status ? null : new ConnectionFault(), xhr.responseText, xhr.status);
log.trace(params.method, url, params.body, xhr.responseText, xhr.status);
var err = xhr.status ? void 0 : new ConnectionFault(xhr.statusText || 'Request failed to complete.');
cb(err, xhr.responseText, xhr.status);
}
};
if (params.timeout !== Infinity) {
if (timeout !== Infinity) {
timeoutId = setTimeout(function () {
xhr.onreadystatechange = _.noop;
xhr.abort();
cb(new TimeoutError());
}, params.timeout);
}, timeout);
}
xhr.send(params.body || null);
xhr.send(params.body || void 0);
};
},{"../connection":19,"../errors":22,"../utils":33}],22:[function(require,module,exports){
var _ = require('./utils'),
var process=require("__browserify_process");var _ = require('./utils'),
errors = module.exports;
function ErrorAbstract(msg, constructor) {
this.message = msg;
Error.call(this, this.message);
Error.captureStackTrace(this, constructor);
if (process.browser) {
this.stack = '';
} else {
Error.captureStackTrace(this, constructor);
}
}
_.inherits(ErrorAbstract, Error);
@ -14595,7 +14675,7 @@ _.each(statusCodes, function (name, status) {
errors[status] = StatusCodeError;
});
},{"./utils":33}],23:[function(require,module,exports){
},{"./utils":33,"__browserify_process":12}],23:[function(require,module,exports){
/**
* Class to wrap URLS, formatting them and maintaining their seperate details
* @type {[type]}
@ -14685,12 +14765,8 @@ Host.prototype.makeUrl = function (params) {
// just stringify the hosts query
query = qs.stringify(this.query);
}
// prepend the ? if there is actually a valid query string
if (query) {
query = '?' + query;
}
return this.protocol + '://' + this.host + port + path + query;
return this.protocol + '://' + this.host + port + path + (query ? '?' + query : '');
};
},{"./utils":33,"querystring":6,"url":7}],24:[function(require,module,exports){
@ -14727,7 +14803,7 @@ function Log(config) {
this.config = config || {};
var i;
var output = _.isPlainObject(config.log) ? config.log : 'warning';
var output = config.loggers ? config.loggers : 'warning';
if (_.isString(output) || _.isFinite(output)) {
output = [
@ -14884,7 +14960,7 @@ Log.prototype.addOutput = function (config) {
delete config.level;
config.levels = levels;
var Logger = loggers[config.type];
var Logger = loggers[_.studlyCase(config.type)];
if (Logger) {
return new Logger(config, this);
} else {
@ -14942,7 +15018,7 @@ Log.prototype.info = function (/* ...msg */) {
*/
Log.prototype.debug = function (/* ...msg */) {
if (EventEmitter.listenerCount(this, 'debug')) {
return this.emit('debug', Log.join(arguments) + _.getStackTrace(Log.prototype.debug));
return this.emit('debug', Log.join(arguments) /*+ _.getStackTrace(Log.prototype.debug)*/);
}
};
@ -14960,10 +15036,17 @@ Log.prototype.debug = function (/* ...msg */) {
*/
Log.prototype.trace = function (method, requestUrl, body, responseBody, responseStatus) {
if (EventEmitter.listenerCount(this, 'trace')) {
if (typeof requestUrl === 'object') {
requestUrl = url.format(requestUrl);
if (typeof requestUrl === 'string') {
requestUrl = url.parse(requestUrl, true, true);
}
return this.emit('trace', method, requestUrl, body, responseBody, responseStatus);
requestUrl = _.defaults({
host: 'localhost:9200',
query: _.defaults({
pretty: true
}, requestUrl.query)
}, requestUrl);
delete requestUrl.auth;
return this.emit('trace', method, url.format(requestUrl), body, responseBody, responseStatus);
}
};
@ -15181,10 +15264,9 @@ Console.prototype.setupListeners = function (levels) {
*/
Console.prototype.onError = _.handler(function (e) {
if (console.error && console.trace) {
console.error(e.name === 'Error' ? 'ERROR' : e.name);
console.trace();
console.error(e.name === 'Error' ? 'ERROR' : e.name, e.stack || e.message);
} else {
console.log(e.name === 'Error' ? 'ERROR' : e.name, e.stack);
console.log(e.name === 'Error' ? 'ERROR' : e.name, e.stack || e.message);
}
});
@ -15196,7 +15278,7 @@ Console.prototype.onError = _.handler(function (e) {
* @param {String} msg - The message to be logged
* @return {undefined}
*/
Console.prototype.onWarning = console[console.warn ? 'warn' : 'log'].bind(console, 'WARNING');
Console.prototype.onWarning = _.bindKey(console, console.warn ? 'warn' : 'log', 'WARNING');
/**
* Handler for the bridges "info" event
@ -15206,7 +15288,7 @@ Console.prototype.onWarning = console[console.warn ? 'warn' : 'log'].bind(consol
* @param {String} msg - The message to be logged
* @return {undefined}
*/
Console.prototype.onInfo = console[console.info ? 'info' : 'log'].bind(console, 'INFO');
Console.prototype.onInfo = _.bindKey(console, console.info ? 'info' : 'log', 'INFO');
/**
* Handler for the bridges "debug" event
@ -15216,7 +15298,7 @@ Console.prototype.onInfo = console[console.info ? 'info' : 'log'].bind(console,
* @param {String} msg - The message to be logged
* @return {undefined}
*/
Console.prototype.onDebug = console[console.debug ? 'debug' : 'log'].bind(console, 'DEBUG');
Console.prototype.onDebug = _.bindKey(console, console.debug ? 'debug' : 'log', 'DEBUG');
/**
* Handler for the bridges "trace" event
@ -15231,7 +15313,7 @@ Console.prototype.onTrace = _.handler(function (method, url, body, responseBody,
message += ' -d "' + body.replace(/"/g, '\\"') + '"';
}
message += '\n<- ' + responseStatus + '\n' + responseBody;
console.log('TRACE', message);
console.log('TRACE:\n' + message + '\n\n');
});
},{"../logger":25,"../utils":33}],27:[function(require,module,exports){
@ -15446,7 +15528,7 @@ TransportRequest.prototype._sendReqWithCon = _.handler(function (err, con) {
TransportRequest.prototype._checkRespForFail = _.handler(function (err, body, status) {
if (err && this._remainingRetries) {
this._remainingRetries--;
this._log.info('Connection error, retrying');
this._log.error(err.message, '-- retrying');
this._connectionPool.select(this.bound._sendReqWithCon);
} else {
this._log.info('Request complete');
@ -15681,6 +15763,10 @@ function adjustWordCase(firstWordCap, otherWordsCap, sep) {
if (word.length) {
words.push(word);
}
// add the leading underscore back to strings the had it originally
if (words.lenth && string.charAt(0) === '_') {
words[0] = '_' + words[0];
}
return words.join(sep);
};
}
@ -15934,11 +16020,16 @@ _.makeBoundMethods = function (obj, methods) {
_.noop = function () {};
_.getStackTrace = function (callee) {
var e = {};
Error.captureStackTrace(e, callee || _.getStackTrace);
return '\n' + e.stack.split('\n').slice(1).join('\n');
};
// _.getStackTrace = function (callee) {
// var e = {};
// if (typeof Error.captureStackTrace === 'function') {
// Error.captureStackTrace(e, callee || _.getStackTrace);
// } else {
// e.stack = (new Error()).stack;
// console.log(e.stack);
// }
// return '\n' + e.stack.split('\n').slice(1).join('\n');
// };
module.exports = utils;

File diff suppressed because one or more lines are too long

View File

@ -7,29 +7,33 @@
"homepage": "https://github.com/elasticsearch/elasticsearch-js",
"version": "0.0.1",
"devDependencies": {
"grunt": "~0.4.0",
"grunt-contrib-jshint": "~0.6.0",
"grunt-contrib-nodeunit": "~0.2.0",
"js-yaml": "~2.1.0",
"mkdirp": "~0.3.5",
"moment": "~2.2.1",
"should": "~2.0.1",
"grunt-mocha-test": "~0.7.0",
"grunt-contrib-watch": "~0.5.3",
"expect.js": "~0.2.0",
"async": "~0.2.9",
"optimist": "~0.6.0",
"minimatch": "~0.2.12",
"browserify": "~2.35.0",
"grunt-browserify": "~1.2.9",
"grunt-contrib-clean": "~0.5.0",
"grunt-contrib-uglify": "~0.2.5"
"grunt": "*",
"grunt-contrib-jshint": "*",
"grunt-contrib-nodeunit": "*",
"js-yaml": "*",
"mkdirp": "*",
"moment": "*",
"should": "*",
"grunt-mocha-test": "*",
"grunt-contrib-watch": "*",
"expect.js": "*",
"async": "*",
"optimist": "*",
"minimatch": "*",
"browserify": "*",
"grunt-browserify": "*",
"grunt-contrib-clean": "*",
"grunt-contrib-uglify": "*",
"mocha": "*",
"grunt-mocha": "*",
"grunt-contrib-concat": "~0.3.0"
},
"license": "Apache License",
"dependencies": {
"cli-color": "~0.2.3",
"lodash": "~2.2.1",
"tar": "~0.1.18"
"cli-color": "*",
"lodash": "*",
"tar": "*",
"agentkeepalive": "*"
},
"scripts": {
"test": "grunt"

View File

@ -35,9 +35,6 @@ function transformFile(entry) {
var cmlKey = _.camelCase(key);
if (cmlKey !== key) {
param.name = key;
if (key.charAt(0) === '_') {
cmlKey = '_' + cmlKey;
}
}
note[cmlKey] = param;
}

View File

@ -33,7 +33,6 @@ var es = require('../../../src/elasticsearch'),
startingMoment = moment().startOf('day').subtract('days', days),
endingMoment = moment().endOf('day').add('days', days),
clientConfig = {
maxSockets: 1000,
log: {
level: 'error'
}

View File

@ -4,29 +4,35 @@
*/
var fs = require('fs');
var path = require('path');
var mkdirp = require('mkdirp');
var jsYaml = require('js-yaml');
var spec = require('../../get_spec');
var clean = require('../../clean');
var _ = require('../../../src/lib/utils');
var testDir = path.resolve(__dirname, '../../../test/integration/yaml_suite/tests');
var testFile = path.resolve(__dirname, '../../../test/integration/yaml_suite/yaml_tests.json');
function download() {
clean(testDir);
var tests = {};
clean(testFile);
spec.get('test/**/*.yaml')
.on('entry', function (entry) {
entry.path = path.resolve(testDir, path.relative('test', entry.path));
mkdirp.sync(path.dirname(entry.path));
fs.writeFileSync(entry.path, entry.data, 'utf8');
var filename = path.relative('test', entry.path);
var file = tests[filename] = [];
jsYaml.loadAll(entry.data, function (doc) {
file.push(doc);
});
})
.on('end', function () {
console.log('download yaml tests to', testDir);
fs.writeFileSync(testFile, JSON.stringify(tests, null, ' '), 'utf8');
console.log('download yaml tests to', testFile);
});
}
try {
var stat = fs.statSync(testDir);
if (!stat.isDirectory() || stat.ctime < Date.now() - 86400000) {
var stat = fs.statSync(testFile);
if (!stat.isFile() || stat.ctime < Date.now() - 86400000) {
download();
}
} catch (e) {

View File

@ -973,10 +973,9 @@ api.exists = ca({
* @param {String} params.preference - Specify the node or shard the operation should be performed on (default: random)
* @param {String} params.q - Query in the Lucene query string syntax
* @param {String} params.routing - Specific routing value
* @param {String} params.source - The URL-encoded query definition (instead of using the request body)
* @param {String|ArrayOfStrings|Boolean} params._source - True or false to return the _source field or not, or a list of fields to return
* @param {String|ArrayOfStrings|Boolean} params._sourceExclude - A list of fields to exclude from the returned _source field
* @param {String|ArrayOfStrings|Boolean} params._sourceInclude - A list of fields to extract and return from the _source field
* @param {String|ArrayOfStrings|Boolean} params.source - True or false to return the _source field or not, or a list of fields to return
* @param {String|ArrayOfStrings|Boolean} params.sourceExclude - A list of fields to exclude from the returned _source field
* @param {String|ArrayOfStrings|Boolean} params.sourceInclude - A list of fields to extract and return from the _source field
* @param {String} params.id - The document ID
* @param {String} params.index - The name of the index
* @param {String} params.type - The type of the document
@ -1029,17 +1028,14 @@ api.explain = ca({
type: 'string'
},
source: {
type: 'string'
},
_source: {
type: 'list',
name: '_source'
},
_sourceExclude: {
sourceExclude: {
type: 'list',
name: '_source_exclude'
},
_sourceInclude: {
sourceInclude: {
type: 'list',
name: '_source_include'
}
@ -1073,9 +1069,9 @@ api.explain = ca({
* @param {Boolean} params.realtime - Specify whether to perform the operation in realtime or search mode
* @param {Boolean} params.refresh - Refresh the shard containing the document before performing the operation
* @param {String} params.routing - Specific routing value
* @param {String|ArrayOfStrings|Boolean} params._source - True or false to return the _source field or not, or a list of fields to return
* @param {String|ArrayOfStrings|Boolean} params._sourceExclude - A list of fields to exclude from the returned _source field
* @param {String|ArrayOfStrings|Boolean} params._sourceInclude - A list of fields to extract and return from the _source field
* @param {String|ArrayOfStrings|Boolean} params.source - True or false to return the _source field or not, or a list of fields to return
* @param {String|ArrayOfStrings|Boolean} params.sourceExclude - A list of fields to exclude from the returned _source field
* @param {String|ArrayOfStrings|Boolean} params.sourceInclude - A list of fields to extract and return from the _source field
* @param {String} params.id - The document ID
* @param {String} params.index - The name of the index
* @param {String} [params.type=_all] - The type of the document (use `_all` to fetch the first document matching the ID across all types)
@ -1103,15 +1099,15 @@ api.get = ca({
routing: {
type: 'string'
},
_source: {
source: {
type: 'list',
name: '_source'
},
_sourceExclude: {
sourceExclude: {
type: 'list',
name: '_source_exclude'
},
_sourceInclude: {
sourceInclude: {
type: 'list',
name: '_source_include'
}
@ -1984,6 +1980,63 @@ api.indices.prototype.getAliases = ca({
});
/**
* Perform a [indices.getFieldMapping](http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/indices-get-field-mapping.html) request
*
* @param {Object} params - An object with parameters used to carry out this action
* @param {Boolean} params.includeDefaults - Whether the default mapping values should be returned as well
* @param {String|ArrayOfStrings|Boolean} params.index - A comma-separated list of index names
* @param {String|ArrayOfStrings|Boolean} params.type - A comma-separated list of document types
* @param {String|ArrayOfStrings|Boolean} params.field - A comma-separated list of fields
*/
api.indices.prototype.getFieldMapping = ca({
methods: [
'GET'
],
params: {
includeDefaults: {
type: 'boolean',
name: 'include_defaults'
}
},
urls: [
{
fmt: '/<%=index%>/<%=type%>/_mapping/field/<%=field%>',
req: {
index: {
type: 'list'
},
type: {
type: 'list'
},
field: {
type: 'list'
}
}
},
{
fmt: '/<%=index%>/_mapping/field/<%=field%>',
req: {
index: {
type: 'list'
},
field: {
type: 'list'
}
}
},
{
fmt: '/_mapping/field/<%=field%>',
req: {
field: {
type: 'list'
}
}
}
]
});
/**
* Perform a [indices.getMapping](http://www.elasticsearch.org/guide/reference/api/admin-indices-get-mapping/) request
*
@ -2867,9 +2920,9 @@ api.info = ca({
* @param {String} params.preference - Specify the node or shard the operation should be performed on (default: random)
* @param {Boolean} params.realtime - Specify whether to perform the operation in realtime or search mode
* @param {Boolean} params.refresh - Refresh the shard containing the document before performing the operation
* @param {String|ArrayOfStrings|Boolean} params._source - True or false to return the _source field or not, or a list of fields to return
* @param {String|ArrayOfStrings|Boolean} params._sourceExclude - A list of fields to exclude from the returned _source field
* @param {String|ArrayOfStrings|Boolean} params._sourceInclude - A list of fields to extract and return from the _source field
* @param {String|ArrayOfStrings|Boolean} params.source - True or false to return the _source field or not, or a list of fields to return
* @param {String|ArrayOfStrings|Boolean} params.sourceExclude - A list of fields to exclude from the returned _source field
* @param {String|ArrayOfStrings|Boolean} params.sourceInclude - A list of fields to extract and return from the _source field
* @param {String} params.index - The name of the index
* @param {String} params.type - The type of the document
*/
@ -2891,15 +2944,15 @@ api.mget = ca({
refresh: {
type: 'boolean'
},
_source: {
source: {
type: 'list',
name: '_source'
},
_sourceExclude: {
sourceExclude: {
type: 'list',
name: '_source_exclude'
},
_sourceInclude: {
sourceInclude: {
type: 'list',
name: '_source_include'
}
@ -3208,10 +3261,9 @@ api.scroll = ca({
* @param {String} params.searchType - Search operation type
* @param {Number} params.size - Number of hits to return (default: 10)
* @param {String|ArrayOfStrings|Boolean} params.sort - A comma-separated list of <field>:<direction> pairs
* @param {String} params.source - The URL-encoded request definition using the Query DSL (instead of using request body)
* @param {String|ArrayOfStrings|Boolean} params._source - True or false to return the _source field or not, or a list of fields to return
* @param {String|ArrayOfStrings|Boolean} params._sourceExclude - A list of fields to exclude from the returned _source field
* @param {String|ArrayOfStrings|Boolean} params._sourceInclude - A list of fields to extract and return from the _source field
* @param {String|ArrayOfStrings|Boolean} params.source - True or false to return the _source field or not, or a list of fields to return
* @param {String|ArrayOfStrings|Boolean} params.sourceExclude - A list of fields to exclude from the returned _source field
* @param {String|ArrayOfStrings|Boolean} params.sourceInclude - A list of fields to extract and return from the _source field
* @param {String|ArrayOfStrings|Boolean} params.stats - Specific 'tag' of the request for logging and statistical purposes
* @param {String} params.suggestField - Specify which field to use for suggestions
* @param {String} [params.suggestMode=missing] - Specify suggest mode
@ -3307,17 +3359,14 @@ api.search = ca({
type: 'list'
},
source: {
type: 'string'
},
_source: {
type: 'list',
name: '_source'
},
_sourceExclude: {
sourceExclude: {
type: 'list',
name: '_source_exclude'
},
_sourceInclude: {
sourceInclude: {
type: 'list',
name: '_source_include'
},
@ -3450,7 +3499,7 @@ api.suggest = ca({
* @param {Date|Number} params.timestamp - Explicit timestamp for the document
* @param {Duration} params.ttl - Expiration time for the document
* @param {Number} params.version - Explicit version number for concurrency control
* @param {Number} params.versionType - Explicit version number for concurrency control
* @param {String} params.versionType - Specific version type
* @param {String} params.id - Document ID
* @param {String} params.index - The name of the index
* @param {String} params.type - The type of the document
@ -3512,7 +3561,11 @@ api.update = ca({
type: 'number'
},
versionType: {
type: 'number',
type: 'enum',
options: [
'internal',
'external'
],
name: 'version_type'
}
},

View File

@ -64,10 +64,10 @@ var castType = {
}
},
time: function (param, val, name) {
if (val instanceof Date) {
return val.getTime();
} else if (_.isNumeric(val)) {
if (typeof val === 'string' || _.isNumeric(val)) {
return val;
} else if (val instanceof Date) {
return val.getTime();
} else {
throw new TypeError('Invalid ' + name + ': expected some sort of time.');
}
@ -211,15 +211,17 @@ function exec(transport, spec, params, cb) {
// build a key list on demand
spec.paramKeys = _.keys(spec.params);
}
var key, param;
var key, param, name;
for (i = 0; i < spec.paramKeys.length; i++) {
key = spec.paramKeys[i];
param = spec.params[key];
// param keys don't always match the param name, in those cases it's stored in the param def as "name"
name = param.name || key;
try {
if (params[key] != null) {
query[key] = castType[param.type] ? castType[param.type](param, params[key], key) : params[key];
if (param['default'] && query[key] === param['default']) {
delete query[key];
query[name] = castType[param.type] ? castType[param.type](param, params[key], key) : params[key];
if (param['default'] && query[name] === param['default']) {
delete query[name];
}
} else if (param.required) {
throw new TypeError('Missing required parameter ' + key);

View File

@ -4,7 +4,7 @@
*
* @class connections.Angular
*/
module.exports = AngularConnection;
module.exports = AngularConnector;
var _ = require('../utils');
var ConnectionAbstract = require('../connection');
@ -12,12 +12,12 @@ var ConnectionFault = require('../errors').ConnectionFault;
/* global angular */
function AngularConnection(host, config) {
function AngularConnector(host, config) {
ConnectionAbstract.call(this, host, config);
}
_.inherits(AngularConnection, ConnectionAbstract);
_.inherits(AngularConnector, ConnectionAbstract);
AngularConnection.prototype.request = function (params, cb) {
AngularConnector.prototype.request = function (params, cb) {
var timeoutId;
this.$http({
@ -35,4 +35,4 @@ AngularConnection.prototype.request = function (params, cb) {
};
// must be overwritten before this connection can be used
AngularConnection.prototype.$http = null;
AngularConnector.prototype.$http = null;

View File

@ -4,48 +4,48 @@
* @param client {Client} - The Client that this class belongs to
* @param config {Object} - Configuration options
* @param [config.protocol=http:] {String} - The HTTP protocol that this connection will use, can be set to https:
* @class HttpConnection
* @class HttpConnector
*/
module.exports = HttpConnection;
module.exports = HttpConnector;
var http = require('http');
var https = require('https');
var _ = require('../utils');
var errors = require('../errors');
var qs = require('querystring');
var KeepAliveAgent = require('agentkeepalive/lib/agent');
var ConnectionAbstract = require('../connection');
var defaultHeaders = {
'connection': 'keep-alive'
};
function HttpConnection(host, config) {
function HttpConnector(host, config) {
ConnectionAbstract.call(this, host, config);
this.agent = new http.Agent({
keepAlive: true,
// delay between the last data packet received and the first keepalive probe
keepAliveMsecs: 1000,
this.hand = require(this.host.protocol);
this.agent = new KeepAliveAgent({
maxSockets: 1,
maxFreeSockets: this.config.maxFreeSockets
maxKeepAliveRequests: 0, // max requests per keepalive socket, default is 0, no limit.
maxKeepAliveTime: 30000 // keepalive for 30 seconds
});
this.on('closed', this.bound.onClosed);
this.once('alive', this.bound.onAlive);
this.on('alive', this.bound.onAlive);
}
_.inherits(HttpConnection, ConnectionAbstract);
_.inherits(HttpConnector, ConnectionAbstract);
HttpConnection.prototype.onClosed = _.handler(function () {
HttpConnector.prototype.onClosed = _.handler(function () {
this.agent.destroy();
this.removeAllListeners();
});
HttpConnection.prototype.onAlive = _.handler(function () {
HttpConnector.prototype.onAlive = _.handler(function () {
// only set the agents max agents config once the connection is verified to be alive
this.agent.maxSockets = this.config.maxSockets;
});
HttpConnection.prototype.makeReqParams = function (params) {
HttpConnector.prototype.makeReqParams = function (params) {
var reqParams = {
method: params.method,
protocol: this.host.protocol + ':',
@ -56,14 +56,31 @@ HttpConnection.prototype.makeReqParams = function (params) {
headers: this.host.headers,
agent: this.agent
};
var query = this.host.query ? this.host.query : null;
var queryStr;
if (typeof query === 'string') {
query = qs.parse(query);
}
var query = qs.stringify(this.host.query ? _.defaults(params.query, this.host.query) : params.query);
reqParams.path += query ? '?' + query : '';
if (params.query) {
query = _.defaults({},
typeof params.query === 'string' ? qs.parse(params.query) : params.query,
query || {}
);
}
if (query) {
queryStr = qs.stringify(query);
}
if (queryStr) {
reqParams.path = reqParams.path + '?' + queryStr;
}
return reqParams;
};
HttpConnection.prototype.request = function (params, cb) {
HttpConnector.prototype.request = function (params, cb) {
var incoming;
var timeoutId;
var request;
@ -78,7 +95,7 @@ HttpConnection.prototype.request = function (params, cb) {
// general clean-up procedure to run after the request
// completes, has an error, or is aborted.
var cleanUp = function (err) {
var cleanUp = _.bind(function (err) {
clearTimeout(timeoutId);
request && request.removeAllListeners();
@ -88,22 +105,14 @@ HttpConnection.prototype.request = function (params, cb) {
err = void 0;
} else {
log.error(err);
if (err instanceof errors.RequestTimeout) {
request.on('error', function catchAbortError() {
request.removeListener('error', catchAbortError);
});
} else {
this.setStatus('dead');
}
this.setStatus('dead');
}
log.trace(params.method, reqParams, params.body, response, status);
cb(err, response, status);
};
}, this);
request = http.request(reqParams, function (_incoming) {
request = this.hand.request(reqParams, function (_incoming) {
incoming = _incoming;
status = incoming.statusCode;
incoming.setEncoding('utf8');

View File

@ -5,11 +5,11 @@
*
* @class {XhrConnection}
*/
module.exports = JqueryConnection;
module.exports = JqueryConnector;
function JqueryConnection() {}
function JqueryConnector() {}
JqueryConnection.prototype.request = function (params, cb) {
JqueryConnector.prototype.request = function (params, cb) {
var $xhr = jQuery.ajax(params).done(cb);
};

View File

@ -3,7 +3,7 @@
*
* @class connections.Xhr
*/
module.exports = XhrConnection;
module.exports = XhrConnector;
/* jshint browser:true */
@ -11,11 +11,12 @@ var _ = require('../utils');
var ConnectionAbstract = require('../connection');
var ConnectionFault = require('../errors').ConnectionFault;
var TimeoutError = require('../errors').RequestTimeout;
var asyncDefault = !(navigator && /PhantomJS/i.test(navigator.userAgent));
function XhrConnection(host, config) {
function XhrConnector(host, config) {
ConnectionAbstract.call(this, host, config);
}
_.inherits(XhrConnection, ConnectionAbstract);
_.inherits(XhrConnector, ConnectionAbstract);
/**
* Simply returns an XHR object cross browser
@ -46,30 +47,36 @@ if (!getXhr) {
throw new Error('getXhr(): XMLHttpRequest not available');
}
XhrConnection.prototype.request = function (params, cb) {
XhrConnector.prototype.request = function (params, cb) {
var xhr = getXhr();
var timeout = params.timeout ? params.timeout : 10000;
var timeoutId;
var url = this.host.makeUrl(params);
var log = this.config.log;
var async = params.async === false ? false : asyncDefault;
if (params.auth) {
xhr.open(params.method, url, true, params.auth.user, params.auth.pass);
xhr.open(params.method, url, async, params.auth.user, params.auth.pass);
} else {
xhr.open(params.method, url, true);
xhr.open(params.method, url, async);
}
xhr.onreadystatechange = function (e) {
if (xhr.readyState === 4) {
clearTimeout(timeoutId);
cb(xhr.status ? null : new ConnectionFault(), xhr.responseText, xhr.status);
log.trace(params.method, url, params.body, xhr.responseText, xhr.status);
var err = xhr.status ? void 0 : new ConnectionFault(xhr.statusText || 'Request failed to complete.');
cb(err, xhr.responseText, xhr.status);
}
};
if (params.timeout !== Infinity) {
if (timeout !== Infinity) {
timeoutId = setTimeout(function () {
xhr.onreadystatechange = _.noop;
xhr.abort();
cb(new TimeoutError());
}, params.timeout);
}, timeout);
}
xhr.send(params.body || null);
xhr.send(params.body || void 0);
};

View File

@ -5,7 +5,11 @@ function ErrorAbstract(msg, constructor) {
this.message = msg;
Error.call(this, this.message);
Error.captureStackTrace(this, constructor);
if (process.browser) {
this.stack = '';
} else {
Error.captureStackTrace(this, constructor);
}
}
_.inherits(ErrorAbstract, Error);

View File

@ -87,10 +87,6 @@ Host.prototype.makeUrl = function (params) {
// just stringify the hosts query
query = qs.stringify(this.query);
}
// prepend the ? if there is actually a valid query string
if (query) {
query = '?' + query;
}
return this.protocol + '://' + this.host + port + path + query;
return this.protocol + '://' + this.host + port + path + (query ? '?' + query : '');
};

View File

@ -246,7 +246,7 @@ Log.prototype.info = function (/* ...msg */) {
*/
Log.prototype.debug = function (/* ...msg */) {
if (EventEmitter.listenerCount(this, 'debug')) {
return this.emit('debug', Log.join(arguments) + _.getStackTrace(Log.prototype.debug));
return this.emit('debug', Log.join(arguments) /*+ _.getStackTrace(Log.prototype.debug)*/);
}
};
@ -264,10 +264,17 @@ Log.prototype.debug = function (/* ...msg */) {
*/
Log.prototype.trace = function (method, requestUrl, body, responseBody, responseStatus) {
if (EventEmitter.listenerCount(this, 'trace')) {
if (typeof requestUrl === 'object') {
requestUrl = url.format(requestUrl);
if (typeof requestUrl === 'string') {
requestUrl = url.parse(requestUrl, true, true);
}
return this.emit('trace', method, requestUrl, body, responseBody, responseStatus);
requestUrl = _.defaults({
host: 'localhost:9200',
query: _.defaults({
pretty: true
}, requestUrl.query)
}, requestUrl);
delete requestUrl.auth;
return this.emit('trace', method, url.format(requestUrl), body, responseBody, responseStatus);
}
};

View File

@ -49,10 +49,9 @@ Console.prototype.setupListeners = function (levels) {
*/
Console.prototype.onError = _.handler(function (e) {
if (console.error && console.trace) {
console.error(e.name === 'Error' ? 'ERROR' : e.name);
console.trace();
console.error(e.name === 'Error' ? 'ERROR' : e.name, e.stack || e.message);
} else {
console.log(e.name === 'Error' ? 'ERROR' : e.name, e.stack);
console.log(e.name === 'Error' ? 'ERROR' : e.name, e.stack || e.message);
}
});
@ -64,7 +63,7 @@ Console.prototype.onError = _.handler(function (e) {
* @param {String} msg - The message to be logged
* @return {undefined}
*/
Console.prototype.onWarning = console[console.warn ? 'warn' : 'log'].bind(console, 'WARNING');
Console.prototype.onWarning = _.bindKey(console, console.warn ? 'warn' : 'log', 'WARNING');
/**
* Handler for the bridges "info" event
@ -74,7 +73,7 @@ Console.prototype.onWarning = console[console.warn ? 'warn' : 'log'].bind(consol
* @param {String} msg - The message to be logged
* @return {undefined}
*/
Console.prototype.onInfo = console[console.info ? 'info' : 'log'].bind(console, 'INFO');
Console.prototype.onInfo = _.bindKey(console, console.info ? 'info' : 'log', 'INFO');
/**
* Handler for the bridges "debug" event
@ -84,7 +83,7 @@ Console.prototype.onInfo = console[console.info ? 'info' : 'log'].bind(console,
* @param {String} msg - The message to be logged
* @return {undefined}
*/
Console.prototype.onDebug = console[console.debug ? 'debug' : 'log'].bind(console, 'DEBUG');
Console.prototype.onDebug = _.bindKey(console, console.debug ? 'debug' : 'log', 'DEBUG');
/**
* Handler for the bridges "trace" event
@ -99,5 +98,5 @@ Console.prototype.onTrace = _.handler(function (method, url, body, responseBody,
message += ' -d "' + body.replace(/"/g, '\\"') + '"';
}
message += '\n<- ' + responseStatus + '\n' + responseBody;
console.log('TRACE', message);
console.log('TRACE:\n' + message + '\n\n');
});

View File

@ -71,7 +71,7 @@ TransportRequest.prototype._sendReqWithCon = _.handler(function (err, con) {
TransportRequest.prototype._checkRespForFail = _.handler(function (err, body, status) {
if (err && this._remainingRetries) {
this._remainingRetries--;
this._log.info('Connection error, retrying');
this._log.error(err.message, '-- retrying');
this._connectionPool.select(this.bound._sendReqWithCon);
} else {
this._log.info('Request complete');

View File

@ -156,6 +156,10 @@ function adjustWordCase(firstWordCap, otherWordsCap, sep) {
if (word.length) {
words.push(word);
}
// add the leading underscore back to strings the had it originally
if (words.lenth && string.charAt(0) === '_') {
words[0] = '_' + words[0];
}
return words.join(sep);
};
}
@ -409,10 +413,15 @@ _.makeBoundMethods = function (obj, methods) {
_.noop = function () {};
_.getStackTrace = function (callee) {
var e = {};
Error.captureStackTrace(e, callee || _.getStackTrace);
return '\n' + e.stack.split('\n').slice(1).join('\n');
};
// _.getStackTrace = function (callee) {
// var e = {};
// if (typeof Error.captureStackTrace === 'function') {
// Error.captureStackTrace(e, callee || _.getStackTrace);
// } else {
// e.stack = (new Error()).stack;
// console.log(e.stack);
// }
// return '\n' + e.stack.split('\n').slice(1).join('\n');
// };
module.exports = utils;

View File

@ -34,6 +34,7 @@
"before": true,
"after": true,
"it": true,
"beforeEach": true
"beforeEach": true,
"afterEach": true
}
}

View File

@ -0,0 +1,22 @@
<html>
<head>
<meta charset="utf-8">
<title>Mocha Tests</title>
<link rel="stylesheet" href="mocha.css" />
</head>
<body>
<div id="mocha"></div>
<script src="expect.js?_c={{ts}}"></script>
<script src="mocha.js?_c={{ts}}"></script>
<script>mocha.setup('bdd')</script>
<script src="client.js?_c={{ts}}"></script>
<script src="yaml_tests.js?_c={{ts}}"></script>
<script>
mocha.checkLeaks();
mocha.slow(1000);
mocha.timeout(11000);
mocha.bail();
/PhantomJS/i.test(navigator.userAgent) || mocha.run();
</script>
</body>
</html>

View File

@ -0,0 +1,146 @@
var http = require('http');
var url = require('url');
var path = require('path');
var fs = require('fs');
var browserify = require('browserify');
var port = process.argv[2] || 8888;
var middleware = [];
Error.stackTraceLimit = Infinity;
function sendBundle(req, resp, files, opts, extend) {
resp.setHeader('Content-Type', 'application/javascript');
resp.writeHead(200);
var b = browserify(files);
if (typeof extend === 'function') {
extend(b);
}
var out = b.bundle(opts);
out.on('data', function (chunk) {
resp.write(chunk);
});
out.on('end', function () {
resp.end();
});
}
var server = http.createServer(function (req, resp) {
req.uri = url.parse(req.url).pathname;
req.filename = path.join(__dirname, req.uri);
resp._end = resp.end;
resp.end = function () {
console.log(this.statusCode, req.uri);
resp._end.apply(resp, arguments);
};
var middleIndex = -1;
function next() {
middleIndex++;
if (middleIndex < middleware.length) {
middleware[middleIndex](req, resp, next);
} else {
resp.writeHead(500);
resp.end('500 Bad Gateway\n');
}
}
next();
});
middleware.push(function (req, resp, next) {
// resolve filenames
switch (req.uri) {
case '/expect.js':
req.filename = path.join(__dirname, '../../node_modules/expect.js/expect.js');
break;
case '/mocha.js':
case '/mocha.css':
req.filename = path.join(__dirname, '../../node_modules/mocha', req.uri);
break;
case '/client.js':
req.filename = path.join(__dirname, '../../dist/elasticsearch.js');
break;
}
next();
});
middleware.push(function (req, resp, next) {
// catch 404's, add directory's index.html
fs.stat(req.filename, function (err, stats) {
if (err) {
resp.writeHead(404, {'Content-Type': 'text/plain'});
resp.write('404 Not Found\n');
resp.end();
} else {
if (stats.isDirectory()) {
req.filename = path.join(req.filename, './index.html');
}
next();
}
});
});
middleware.push(function (req, resp, next) {
// static files
var reader = fs.createReadStream(req.filename);
var data = '';
reader.on('data', onData);
reader.on('error', onError);
reader.on('end', onEnd);
function cleanupListeners() {
reader.removeListener('end', onEnd);
reader.removeListener('data', onData);
reader.removeListener('error', onError);
}
function onData(chunk) {
data += chunk;
}
function onError(err) {
cleanupListeners();
console.error(err);
resp.setHeader('Content-Type', 'text/plain');
resp.writeHead(500);
resp.write(err.message + '\n');
resp.end();
}
function onEnd() {
cleanupListeners();
var contentType = 'text/plain';
switch (req.filename.split('.').pop()) {
case 'js':
contentType = 'application/javascript';
break;
case 'css':
contentType = 'text/css';
break;
case 'html':
contentType = 'text/html';
break;
}
resp.setHeader('Content-Type', contentType);
resp.writeHead(200);
resp.end(
data
.replace(/\{\{ts\}\}/g, Date.now())
.replace(/\{\{phantom\}\}/g, req.filename === '/phantom.html' ? '-phantom' : '')
);
}
});
server.listen(parseInt(port, 10), function () {
console.log('server listening on port', server.address().port);
});

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,20 @@
var path = require('path');
var _ = require('../../../src/lib/utils');
var defaults = {
executable: process.env.ES_HOME ? path.join(process.env.ES_HOME, './bin/elasticsearch') : null,
clusterName: 'yaml-test-runner',
dataPath: '/tmp/yaml-test-runner',
host: 'localhost',
port: '9200',
match: '**'
};
if (process.browser) {
module.exports = defaults;
} else {
module.exports = require('optimist')
.default(defaults)
.boolean('createServer')
.argv;
}

View File

@ -0,0 +1,92 @@
if (process.browser) {
/* jshint browser: true */
var es = window.elasticsearch;
} else {
var es = require('../../../src/elasticsearch');
}
var argv = require('./argv');
var server = require('./server');
var path = require('path');
var fs = require('fs');
var _ = require('../../../src/lib/utils');
// location that the logger will write to
var logFile = path.resolve(__dirname, './log');
// current client
var client = null;
// when set to a boolean, hold the test of a ping
var externalExists;
// a reference to a personal instance of ES Server
var esServer = null;
module.exports = {
create: function create(cb) {
if (argv.createServer || externalExists === false) {
if (!esServer) {
server.start(function (err, _server) {
esServer = _server;
if (err) {
done(err);
} else {
doCreateClient(done);
}
});
} else {
doCreateClient(done);
}
} else if (externalExists === void 0) {
doCreateClient(function () {
client.ping(function (err) {
externalExists = !err;
create(cb);
});
});
} else {
doCreateClient(done);
}
function done(err) {
cb(err, client);
}
function doCreateClient(cb) {
// close existing client
if (client) {
client.close();
}
if (!process.browser) {
// delete existing log file
try {
fs.unlinkSync(logFile);
} catch (e) {
if (!~e.message.indexOf('ENOENT')) {
return _.nextTick(cb, e);
}
}
}
client = new es.Client({
hosts: [
{
host: esServer ? esServer.__hostname : argv.host,
port: esServer ? esServer.__port : argv.port
}
],
log: {
type: process.browser ? 'console' : 'file',
level: 'trace',
path: logFile
}
});
_.nextTick(cb);
}
},
get: function () {
return client;
}
};

View File

@ -1,610 +1,33 @@
var path = require('path'),
fs = require('fs'),
async = require('async'),
jsYaml = require('js-yaml'),
expect = require('expect.js'),
server = require('./server'),
_ = require('../../../src/lib/utils'),
es = require('../../../src/elasticsearch'),
Minimatch = require('minimatch').Minimatch;
var argv = require('optimist')
.default('executable', process.env.ES_HOME ? path.join(process.env.ES_HOME, './bin/elasticsearch') : null)
.default('clusterName', 'yaml-test-runner')
.default('dataPath', '/tmp/yaml-test-runner')
.default('host', 'localhost')
.default('port', '9200')
.default('match', '**')
.boolean('createServer')
.argv;
Error.stackTraceLimit = Infinity;
var path = require('path');
var async = require('async');
var jsYaml = require('js-yaml');
var expect = require('expect.js');
var YamlFile = require('./yaml_file');
var _ = require('../../../src/lib/utils');
var es = require('../../../src/elasticsearch');
var clientManager = require('./client_manager');
var Minimatch = require('minimatch').Minimatch;
var argv = require('./argv');
var testDir = path.resolve(__dirname, './tests');
var doPattern = new Minimatch(argv.match);
// a reference to a personal instance of ES Server
var esServer = null;
// the client
var client = null;
// location that the logger will write to
var logFile = path.resolve(__dirname, './log');
// empty all of the indices in ES please
function clearIndices(done) {
client.indices.delete({
index: '*',
ignore: 404
}, done);
}
function createClient() {
if (client) {
client.close();
}
client = new es.Client({
hosts: [
{
host: esServer ? esServer.__hostname : argv.host,
port: esServer ? esServer.__port : argv.port
}
],
// log: null
log: {
type: 'file',
level: 'trace',
path: logFile
}
});
}
function createServer(done) {
server.start(argv, function (err, server) {
esServer = server;
done(err);
});
}
// before running any tests...
before(function (done) {
// start our personal ES Server
this.timeout(null);
if (argv.createServer) {
createServer(done);
} else {
createClient();
client.ping(function (err) {
if (err) {
createServer(done);
} else {
done();
}
});
}
clientManager.create(done);
});
before(function (done) {
// delete the integration log
fs.unlink(logFile, function (err) {
if (err && ~err.message.indexOf('ENOENT')) {
done();
} else {
done(err);
}
});
// make sure ES is empty
clientManager.get().indices.delete({
index: '*',
ignore: 404
}, done);
});
before(function () {
createClient();
var files = _.map(require('./yaml_tests.json'), function (docs, filename) {
if (doPattern.match(filename)) {
return new YamlFile(filename, docs);
}
});
before(clearIndices);
/**
* The version that ES is running, in comparable string form XXX-XXX-XXX, fetched when needed
* @type {String}
*/
var ES_VERSION = null;
// core expression for finding a version
var versionExp = '([\\d\\.]*\\d)(?:\\.\\w+)?';
/**
* Regular Expression to extract a version number from a string
* @type {RegExp}
*/
var versionRE = new RegExp(versionExp);
/**
* Regular Expression to extract a version range from a string
* @type {RegExp}
*/
var versionRangeRE = new RegExp(versionExp + '\\s*\\-\\s*' + versionExp);
/**
* Fetches the client.info, and parses out the version number to a comparable string
* @param done {Function} - callback
*/
function getVersionFromES(done) {
client.info({}, function (err, resp) {
if (err) {
throw new Error('unable to get info about ES');
}
expect(resp.version.number).to.match(versionRE);
ES_VERSION = versionToComparableString(versionRE.exec(resp.version.number)[1]);
done();
});
}
/**
* Transform x.x.x into xxx.xxx.xxx, striping off any text at the end like beta or pre-alpha35
*
* @param {String} version - Version number represented as a string
* @return {String} - Version number represented as three numbers, seperated by -, all numbers are
* padded with 0 and will be three characters long so the strings can be compared.
*/
function versionToComparableString(version) {
var parts = _.map(version.split('.'), function (part) {
part = '' + _.parseInt(part);
return (new Array(4 - part.length)).join('0') + part;
});
while (parts.length < 3) {
parts.push('000');
}
return parts.join('-');
}
/**
* Compare a version range to the ES_VERSION, determining if the current version
* falls within the range.
*
* @param {String} rangeString - a string representing two version numbers seperated by a "-"
* @return {Boolean} - is the current version within the range (inclusive)
*/
function rangeMatchesCurrentVersion(rangeString, done) {
function doWork() {
expect(rangeString).to.match(versionRangeRE);
var range = versionRangeRE.exec(rangeString);
range = _.map(_.last(range, 2), versionToComparableString);
done(ES_VERSION >= range[0] && ES_VERSION <= range[1]);
}
if (!ES_VERSION) {
getVersionFromES(doWork);
} else {
doWork();
}
}
/**
* Read the test descriptions from a yaml document (usually only one test, per doc but
* sometimes multiple docs per file, and because of the layout there COULD be
* multiple test per test...)
*
* @param {Object} testConfigs - The yaml document
* @return {undefined}
*/
function makeTestFromYamlDoc(yamlDoc, count) {
var setup;
if (_.has(yamlDoc, 'setup')) {
(new ActionRunner(yamlDoc.setup)).each(function (action, name) {
before(action);
});
delete yamlDoc.setup;
}
_.forOwn(yamlDoc, function (test, description) {
describe(description, function () {
var actions = new ActionRunner(test);
actions.each(function (action, name) {
it(name, action);
});
});
});
// after running the tests, remove all indices
after(clearIndices);
}
/**
* Class to wrap a single document from a yaml test file
*
* @constructor
* @class ActionRunner
* @param actions {Array} - The array of actions directly from the Yaml file
*/
function ActionRunner(actions) {
this._actions = [];
this._stash = {};
this._last_requests_response = null;
// setup the actions, creating a bound and testable method for each
_.each(this.flattenTestActions(actions), function (action, i) {
// get the method that will do the action
var method = this['do_' + action.name];
var runner = this;
// check that it's a function
expect(method).to.be.a('function');
if (typeof action.args === 'object') {
action.name += ' ' + Object.keys(action.args).join(', ');
} else {
action.name += ' ' + action.args;
}
// wrap in a check for skipping
action.bound = _.bind(method, this, action.args);
// create a function that can be passed to
action.testable = function (done) {
if (runner.skipping) {
return done();
}
if (method.length > 1) {
action.bound(done);
} else {
action.bound();
done();
}
};
this._actions.push(action);
}, this);
}
ActionRunner.prototype = {
/**
* convert tests actions
* from: [ {name:args, name:args}, {name:args}, ... ]
* to: [ {name:'', args:'' }, {name:'', args:''} ]
* so it's easier to work with
* @param {ArrayOfObjects} config - Actions to be taken as defined in the yaml specs
*/
flattenTestActions: function (config) {
// creates [ [ {name:"", args:"" }, ... ], ... ]
// from [ {name:args, name:args}, {name:args} ]
var actionSets = _.map(config, function (set) {
return _.map(_.pairs(set), function (pair) {
return { name: pair[0], args: pair[1] };
});
});
// do a single level flatten, merge=ing the nested arrays from step one
// into a master array, creating an array of action objects
return _.reduce(actionSets, function (note, set) {
return note.concat(set);
}, []);
},
/**
* Itterate over each of the actions, provides the testable function, and a name/description.
* return a litteral false to stop itterating
* @param {Function} ittr - The function to call for each action.
* @return {undefined}
*/
each: function (ittr) {
var action;
while (action = this._actions.shift()) {
if (ittr(action.testable, action.name) === false) {
break;
}
}
},
/**
* Get a value from the last response, using dot-notation
*
* Example
* ===
*
* get '_source.tags.1'
*
* from {
* _source: {
* tags: [
* 'one',
* 'two'
* ]
* }
* }
*
* returns 'two'
*
* @param {string} path - The dot-notation path to the value needed.
* @return {*} - The value requested, or undefined if it was not found
*/
get: function (path, from) {
var i
, log = process.env.LOG_GETS && !from ? console.log.bind(console) : function () {};
if (!from) {
if (path[0] === '$') {
from = this._stash;
path = path.substring(1);
} else {
from = this._last_requests_response;
}
}
log('getting', path, 'from', from);
var steps = path ? path.split('.') : []
, remainingSteps;
for (i = 0; from != null && i < steps.length; i++) {
if (from[steps[i]] === void 0) {
remainingSteps = steps.slice(i).join('.').replace(/\\\./g, '.');
from = from[remainingSteps];
break;
} else {
from = from[steps[i]];
}
}
log('found', typeof from !== 'function' ? from : 'function');
return from;
},
/**
* Do a skip operation, setting the skipping flag to true if the version matches
* the range defined in args.version
*
* @param args
* @param done
*/
do_skip: function (args, done) {
rangeMatchesCurrentVersion(args.version, _.bind(function (match) {
if (match) {
this.skipping = true;
// console.log('skipping the rest of these actions' + (args.reason ? ' because ' + args.reason : ''));
} else {
this.skipping = false;
}
done();
}, this));
},
/**
* Do a request, as outlined in the args
*
* @param {[type]} args [description]
* @param {Function} done [description]
* @return {[type]} [description]
*/
do_do: function (args, done) {
var catcher;
// resolve the catch arg to a value used for matching once the request is complete
switch (args.catch) {
case void 0:
catcher = null;
break;
case 'missing':
catcher = 404;
break;
case 'conflict':
catcher = 409;
break;
case 'forbidden':
catcher = 403;
break;
case 'request':
catcher = /.*/;
break;
case 'param':
catcher = TypeError;
break;
default:
catcher = args.catch.match(/^\/(.*)\/$/);
if (catcher) {
catcher = new RegExp(catcher[1]);
}
}
delete args.catch;
var action = Object.keys(args).pop()
, clientActionName = _.map(action.split('.'), _.camelCase).join('.')
, clientAction = this.get(clientActionName, client)
, params = _.transform(args[action], function (note, val, name) {
note[name] = (typeof val === 'string' && val[0] === '$') ? this.get(val) : val;
}, {}, this);
expect(clientAction || clientActionName).to.be.a('function');
if (typeof clientAction === 'function') {
if (_.isNumeric(catcher)) {
params.ignore = _.union(params.ignore || [], [catcher]);
catcher = null;
}
var cb = _.bind(function (error, body, status) {
this._last_requests_response = body;
if (error) {
if (catcher) {
if (catcher instanceof RegExp) {
// error message should match the regexp
expect(error.message).to.match(catcher);
error = null;
} else if (typeof catcher === 'function') {
// error should be an instance of
expect(error).to.be.a(catcher);
error = null;
} else {
return done(new Error('Invalid catcher ' + catcher));
}
} else {
return done(error);
}
}
done(error);
}, this);
// switch (Math.round(Math.random() * 100) % 3) {
// case 0:
// clientAction.call(client, params).then(function (resp) {
// cb(void 0, resp.body, resp.status);
// }, function (err) {
// cb(err);
// });
// break;
// case 1:
// clientAction.call(client, params).once('done', cb);
// break;
// case 2:
clientAction.call(client, params, cb);
// break;
// }
} else {
done(new Error('stepped in do_do, did not find a function'));
}
},
/**
* Set a value from the respose into the stash
*
* Example
* ====
* { _id: id } # stash the value of `response._id` as `id`
*
* @param {Object} args - The object set to the "set" key in the test
* @return {undefined}
*/
do_set: function (args) {
_.forOwn(args, function (name, path) {
this._stash[name] = this.get(path);
}, this);
},
/**
* Test that the specified path exists in the response and has a
* true value (eg. not 0, false, undefined, null or the empty string)
*
* @param {string} path - Path to the response value to test
* @return {undefined}
*/
do_is_true: function (path) {
expect(this.get(path)).to.be.ok;
},
/**
* Test that the specified path exists in the response and has a
* false value (eg. 0, false, undefined, null or the empty string)
*
* @param {string} path - Path to the response value to test
* @return {undefined}
*/
do_is_false: function (path) {
expect(this.get(path)).to.not.be.ok;
},
/**
* Test that the response field (arg key) matches the value specified
*
* @param {Object} args - Hash of fields->values that need to be checked
* @return {undefined}
*/
do_match: function (args) {
_.forOwn(args, function (val, path) {
if (val[0] === '$') {
val = this.get(val);
}
expect(this.get(path), path).to.eql(val, path);
}, this);
},
/**
* Test that the response field (arg key) is less than the value specified
*
* @param {Object} args - Hash of fields->values that need to be checked
* @return {undefined}
*/
do_lt: function (args) {
_.forOwn(args, function (num, path) {
expect(this.get(path)).to.be.below(num);
}, this);
},
/**
* Test that the response field (arg key) is greater than the value specified
*
* @param {Object} args - Hash of fields->values that need to be checked
* @return {undefined}
*/
do_gt: function (args) {
_.forOwn(args, function (num, path) {
expect(this.get(path)).to.be.above(num);
}, this);
},
/**
* Test that the response field (arg key) has a length equal to that specified.
* For object values, checks the length of the keys.
*
* @param {Object} args - Hash of fields->values that need to be checked
* @return {undefined}
*/
do_length: function (args) {
_.forOwn(args, function (len, path) {
expect(_.size(this.get(path))).to.be(len);
}, this);
}
};
/**
* recursively crawl the directory, looking for yaml files which will be passed to loadFile
* @param {String} dir - The directory to crawl
* @return {undefined}
*/
(function () {
/**
* read the file's contents, parse the yaml, pass to makeTestFromYamlDoc
*
* @param {String} path - Full path to yaml file
* @return {undefined}
*/
function loadFile(location) {
jsYaml.loadAll(
fs.readFileSync(location, { encoding: 'utf8' }),
function (doc) {
makeTestFromYamlDoc(doc);
},
{
filename: location
}
);
}
function loadDir(dir) {
fs.readdirSync(dir).forEach(function (fileName) {
describe(fileName, function () {
var location = path.join(dir, fileName),
stat = fs.statSync(location);
if (stat.isFile() && fileName.match(/\.yaml$/) && doPattern.match(path.relative(testDir, location))) {
loadFile(location);
}
else if (stat.isDirectory()) {
loadDir(location);
}
});
});
}
loadDir(testDir);
})();

View File

@ -1,22 +1,23 @@
var childProc = require('child_process'),
events = require('events'),
path = require('path'),
fs = require('fs'),
_ = require('../../../src/lib/utils');
var childProc = require('child_process');
var events = require('events');
var path = require('path');
var fs = require('fs');
var _ = require('../../../src/lib/utils');
var argv = require('./argv');
exports.start = function (params, cb) {
exports.start = function (cb) {
if (!params.executable || !fs.existsSync(params.executable)) {
if (!argv.executable || !fs.existsSync(argv.executable)) {
return cb(new Error('unable to find elasticsearch executable, ' +
'set ES_HOME env var to the instalation path of elasticsearch'));
}
var server = childProc.spawn(
params.executable,
argv.executable,
[
'-f',
'-Des.cluster.name=' + params.clusterName,
'-Des.path.data=' + params.dataPath,
'-Des.cluster.name=' + argv.clusterName,
'-Des.path.data=' + argv.dataPath,
// '-Des.logger.level=DEBUG',
'-Des.discovery.zen.ping.multicast.enabled=false',
],
@ -34,7 +35,7 @@ exports.start = function (params, cb) {
server.stdout.on('data', function onProcessData(line) {
line = _.trim(line.toString());
var match;
if (match = line.match(/bound_address \{inet\[\/?([^:]+):(\d+)\]\}/)) {
if (match = line.match(/\{inet\[\/?([^:]+):(\d+)\]\}/)) {
server.__hostname = match[1];
server.__port = match[2];
}

View File

@ -0,0 +1,439 @@
/**
* Class to wrap a single document from a yaml test file
*
* @constructor
* @class YamlDoc
* @param actions {Array} - The array of actions directly from the Yaml file
*/
module.exports = YamlDoc;
var _ = require('../../../src/lib/utils');
var clientManager = require('./client_manager');
var expect = require('expect.js');
/**
* The version that ES is running, in comparable string form XXX-XXX-XXX, fetched when needed
* @type {String}
*/
var ES_VERSION = null;
// core expression for finding a version
var versionExp = '([\\d\\.]*\\d)(?:\\.\\w+)?';
/**
* Regular Expression to extract a version number from a string
* @type {RegExp}
*/
var versionRE = new RegExp(versionExp);
/**
* Regular Expression to extract a version range from a string
* @type {RegExp}
*/
var versionRangeRE = new RegExp(versionExp + '\\s*\\-\\s*' + versionExp);
/**
* Fetches the client.info, and parses out the version number to a comparable string
* @param done {Function} - callback
*/
function getVersionFromES(done) {
clientManager.get().info({}, function (err, resp) {
if (err) {
throw new Error('unable to get info about ES');
}
expect(resp.version.number).to.match(versionRE);
ES_VERSION = versionToComparableString(versionRE.exec(resp.version.number)[1]);
done();
});
}
/**
* Transform x.x.x into xxx.xxx.xxx, striping off any text at the end like beta or pre-alpha35
*
* @param {String} version - Version number represented as a string
* @return {String} - Version number represented as three numbers, seperated by -, all numbers are
* padded with 0 and will be three characters long so the strings can be compared.
*/
function versionToComparableString(version) {
var parts = _.map(version.split('.'), function (part) {
part = '' + _.parseInt(part);
return (new Array(4 - part.length)).join('0') + part;
});
while (parts.length < 3) {
parts.push('000');
}
return parts.join('-');
}
/**
* Compare a version range to the ES_VERSION, determining if the current version
* falls within the range.
*
* @param {String} rangeString - a string representing two version numbers seperated by a "-"
* @return {Boolean} - is the current version within the range (inclusive)
*/
function rangeMatchesCurrentVersion(rangeString, done) {
function doWork() {
expect(rangeString).to.match(versionRangeRE);
var range = versionRangeRE.exec(rangeString);
range = _.map(_.last(range, 2), versionToComparableString);
done(ES_VERSION >= range[0] && ES_VERSION <= range[1]);
}
if (!ES_VERSION) {
getVersionFromES(doWork);
} else {
doWork();
}
}
// empty all of the indices in ES please
function clearIndices(done) {
clientManager.get().indices.delete({
index: '*',
ignore: 404
}, done);
}
function YamlDoc(doc, file) {
var self = this;
self.file = file;
self.description = _.keys(doc).shift();
self._stash = {};
self._last_requests_response = null;
// setup the actions, creating a bound and testable method for each
self._actions = _.map(self.flattenTestActions(doc[self.description]), function (action, i) {
// get the method that will do the action
var method = self['do_' + action.name];
// check that it's a function
expect(method).to.be.a('function');
if (typeof action.args === 'object') {
action.name += ' ' + _.keys(action.args).join(', ');
} else if (action.args) {
action.name += ' ' + action.args;
}
// wrap in a check for skipping
action.bound = _.bind(method, self, action.args);
// create a function that can be passed to
action.testable = function (done) {
if (self.skipping || self.file.skipping) {
return done();
}
if (method.length > 1) {
action.bound(done);
} else {
action.bound();
done();
}
};
return action;
});
}
YamlDoc.prototype = {
/**
* convert tests actions
* from: [ {name:args, name:args}, {name:args}, ... ]
* to: [ {name:'', args:'' }, {name:'', args:''} ]
* so it's easier to work with
* @param {ArrayOfObjects} config - Actions to be taken as defined in the yaml specs
*/
flattenTestActions: function (config) {
// creates [ [ {name:"", args:"" }, ... ], ... ]
// from [ {name:args, name:args}, {name:args} ]
var actionSets = _.map(config, function (set) {
return _.map(_.pairs(set), function (pair) {
return { name: pair[0], args: pair[1] };
});
});
// do a single level flatten, merge=ing the nested arrays from step one
// into a master array, creating an array of action objects
return _.reduce(actionSets, function (note, set) {
return note.concat(set);
}, []);
},
/**
* Itterate over each of the actions, provides the testable function, and a name/description.
* return a litteral false to stop itterating
* @param {Function} ittr - The function to call for each action.
* @return {undefined}
*/
each: function (ittr) {
for (var i = 0; i < this._actions.length; i++) {
if (ittr(this._actions[i].testable, this._actions[i].name) === false) {
break;
}
}
},
/**
* Get a value from the last response, using dot-notation
*
* Example
* ===
*
* get '_source.tags.1'
*
* from {
* _source: {
* tags: [
* 'one',
* 'two'
* ]
* }
* }
*
* returns 'two'
*
* @param {string} path - The dot-notation path to the value needed.
* @return {*} - The value requested, or undefined if it was not found
*/
get: function (path, from) {
var log = process.env.LOG_GETS && !from ? console.log.bind(console) : function () {};
var i;
if (!from) {
if (path[0] === '$') {
from = this._stash;
path = path.substring(1);
} else {
from = this._last_requests_response;
}
}
log('getting', path, 'from', from);
var steps = path ? path.split('.') : [];
var remainingSteps;
for (i = 0; from != null && i < steps.length; i++) {
if (from[steps[i]] === void 0) {
remainingSteps = steps.slice(i).join('.').replace(/\\\./g, '.');
from = from[remainingSteps];
break;
} else {
from = from[steps[i]];
}
}
log('found', typeof from !== 'function' ? from : 'function');
return from;
},
/**
* Do a skip operation, setting the skipping flag to true if the version matches
* the range defined in args.version
*
* @param args
* @param done
*/
do_skip: function (args, done) {
rangeMatchesCurrentVersion(args.version, _.bind(function (match) {
if (match) {
if (this.description === 'setup') {
this.file.skipping = true;
// console.log('skipping this file' + (args.reason ? ' because ' + args.reason : ''));
} else {
this.skipping = true;
// console.log('skipping the rest of this doc' + (args.reason ? ' because ' + args.reason : ''));
}
} else {
this.skipping = false;
this.file.skipping = false;
}
done();
}, this));
},
/**
* Do a request, as outlined in the args
*
* @param {[type]} args [description]
* @param {Function} done [description]
* @return {[type]} [description]
*/
do_do: function (args, done) {
var catcher;
// resolve the catch arg to a value used for matching once the request is complete
switch (args.catch) {
case void 0:
catcher = null;
break;
case 'missing':
catcher = 404;
break;
case 'conflict':
catcher = 409;
break;
case 'forbidden':
catcher = 403;
break;
case 'request':
catcher = /.*/;
break;
case 'param':
catcher = TypeError;
break;
default:
catcher = args.catch.match(/^\/(.*)\/$/);
if (catcher) {
catcher = new RegExp(catcher[1]);
}
}
delete args.catch;
var client = clientManager.get();
var action = Object.keys(args).pop();
var clientActionName = _.map(action.split('.'), _.camelCase).join('.');
var clientAction = this.get(clientActionName, client);
var params = _.transform(args[action], function (note, val, name) {
note[_.camelCase(name)] = (typeof val === 'string' && val[0] === '$') ? this.get(val) : val;
}, {}, this);
expect(clientAction || clientActionName).to.be.a('function');
if (typeof clientAction === 'function') {
if (_.isNumeric(catcher)) {
params.ignore = _.union(params.ignore || [], [catcher]);
catcher = null;
}
var cb = _.bind(function (error, body, status) {
this._last_requests_response = body;
if (error) {
if (catcher) {
if (catcher instanceof RegExp) {
// error message should match the regexp
expect(error.message).to.match(catcher);
error = null;
} else if (typeof catcher === 'function') {
// error should be an instance of
expect(error).to.be.a(catcher);
error = null;
} else {
return done(new Error('Invalid catcher ' + catcher));
}
} else {
return done(error);
}
}
done(error);
}, this);
clientAction.call(client, params, cb);
} else {
done(new Error('stepped in do_do, did not find a function'));
}
},
/**
* Set a value from the respose into the stash
*
* Example
* ====
* { _id: id } # stash the value of `response._id` as `id`
*
* @param {Object} args - The object set to the "set" key in the test
* @return {undefined}
*/
do_set: function (args) {
_.forOwn(args, function (name, path) {
this._stash[name] = this.get(path);
}, this);
},
/**
* Test that the specified path exists in the response and has a
* true value (eg. not 0, false, undefined, null or the empty string)
*
* @param {string} path - Path to the response value to test
* @return {undefined}
*/
do_is_true: function (path) {
expect(this.get(path)).to.be.ok;
},
/**
* Test that the specified path exists in the response and has a
* false value (eg. 0, false, undefined, null or the empty string)
*
* @param {string} path - Path to the response value to test
* @return {undefined}
*/
do_is_false: function (path) {
expect(this.get(path)).to.not.be.ok;
},
/**
* Test that the response field (arg key) matches the value specified
*
* @param {Object} args - Hash of fields->values that need to be checked
* @return {undefined}
*/
do_match: function (args) {
_.forOwn(args, function (val, path) {
if (val[0] === '$') {
val = this.get(val);
}
expect(this.get(path)).to.eql(val);
}, this);
},
/**
* Test that the response field (arg key) is less than the value specified
*
* @param {Object} args - Hash of fields->values that need to be checked
* @return {undefined}
*/
do_lt: function (args) {
_.forOwn(args, function (num, path) {
expect(this.get(path)).to.be.below(num);
}, this);
},
/**
* Test that the response field (arg key) is greater than the value specified
*
* @param {Object} args - Hash of fields->values that need to be checked
* @return {undefined}
*/
do_gt: function (args) {
_.forOwn(args, function (num, path) {
expect(this.get(path)).to.be.above(num);
}, this);
},
/**
* Test that the response field (arg key) has a length equal to that specified.
* For object values, checks the length of the keys.
*
* @param {Object} args - Hash of fields->values that need to be checked
* @return {undefined}
*/
do_length: function (args) {
_.forOwn(args, function (len, path) {
expect(_.size(this.get(path))).to.be(len);
}, this);
}
};

View File

@ -0,0 +1,43 @@
/**
* Class representing a YAML file
* @type {[type]}
*/
module.exports = YamlFile;
var YamlDoc = require('./yaml_doc');
var clientManager = require('./client_manager');
var _ = require('../../../src/lib/utils');
var async = require('async');
function YamlFile(filename, docs) {
var file = this;
// file level skipping flag
file.skipping = false;
describe(filename, function () {
file.docs = _.map(docs, function (doc) {
doc = new YamlDoc(doc, file);
if (doc.description === 'setup') {
beforeEach(/* doc */function (done) {
// console.log('setting up:', filename);
async.series(_.pluck(doc._actions, 'testable'), done);
});
} else {
it(doc.description, function (done) {
// console.log('test doc');
async.series(_.pluck(doc._actions, 'testable'), done);
});
}
});
afterEach(/* doc */function (done) {
// console.log('clearing indices');
clientManager.get().indices.delete({
index: '*',
ignore: 404
}, done);
});
});
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,39 @@
describe('Http Connector', function () {
var Host = require('../../src/lib/host');
var HttpConnection = require('../../src/lib/connectors/http');
var host = new Host('http://someesserver.com:9205/prefix');
var con;
describe('#makeReqParams', function () {
before(function () {
con = new HttpConnection(host, {});
});
it('creates the request params property', function () {
var reqParams = con.makeReqParams({
method: 'GET',
path: '/_cluster/nodes/stats',
query: {
jvm: true
}
});
reqParams.should.include({
method: 'GET',
protocol: 'http:',
auth: '',
hostname: 'someesserver.com',
port: '9205',
path: '/prefix/_cluster/nodes/stats?jvm=true'
});
Object.keys(reqParams).should.not.include([
'host', 'pathname', 'query'
]);
});
});
});

View File

@ -1,23 +0,0 @@
require('mocha-as-promised')();
var NodeHttp = require('../../src/lib/transports/node_http')
, expect = require('expect.js');
describe('NodeHttp Transport', function () {
var transport = new NodeHttp(['localhost:9200']);
describe('#send', function () {
it('should ignore host in url', function (done) {
transport.request({
url: 'http://google.com/',
method: 'get',
body: null
}).then(function (resp) {
expect(resp.version.number).to.be.a('string');
done();
});
});
});
});