Compare commits

..

12 Commits

Author SHA1 Message Date
4466461828 Bumped v5.6.19 2019-07-05 11:40:33 +02:00
66fd94643d Remove auth data from inspect and toJSON in connection class (#887)
* Remove auth data from inspect and toJSON in connection class

* Updated test
2019-07-04 14:43:48 +02:00
39bbd77bec Updated type definitions (#882)
* Updated type definitions

* Updated test
2019-06-19 09:15:57 +02:00
a4f893d563 Support branch for code generation (#883) 2019-06-18 15:47:17 +02:00
6707a8603e Added cloud configuration example (#880)
* Added cloud configuration example

* Used cloud example from auth docs and added link
2019-06-14 10:13:57 +02:00
a38e89aa04 [DOCS] Fixes typo (#877) 2019-06-12 08:42:21 +02:00
0311ce9e42 Docs: added missing configuration options (#870) 2019-06-03 12:29:23 +02:00
2e90d5b55e Bumped v5.6.18 2019-05-22 12:20:41 +02:00
32836b4f6c Support for non-friendly chars in url username and password (#858)
* Support for non-friendly chars in url username and password
- Added auth option to Connection class
- Updated pool.addConnection

* Updated test
2019-05-20 17:10:49 +02:00
f8034c60bc API generation 2019-05-16 16:56:01 -04:00
c68a5ce9a2 Patch deprecated parameters (#851)
* Updated code generation

* API generation

* API generation

* Updated code generation
2019-05-16 16:55:07 -04:00
554bb1ff05 missing comma (#854) 2019-05-16 09:39:59 -04:00
15 changed files with 191 additions and 55 deletions

View File

@ -8,7 +8,7 @@ The client is designed to be easily configured as you see fit for your needs, fo
const { Client } = require('@elastic/elasticsearch')
const client = new Client({
node: 'http://localhost:9200'
node: 'http://localhost:9200',
maxRetries: 5,
requestTimeout: 60000,
sniffOnStart: true
@ -150,6 +150,26 @@ function generateRequestId (params, options) {
|`name`
|`string` - The name to identify the client instance in the events. +
_Default:_ `elasticsearch-js`
|`headers`
|`object` - A set of custom headers to send in every request. +
_Default:_ `{}`
|`cloud`
a|`object` - Custom configuration for connecting to https://cloud.elastic.co[Elastic Cloud]. See https://www.elastic.co/guide/en/elasticsearch/client/javascript-api/current/auth-reference.html[Authentication] for more details. +
_Default:_ `null` +
_Cloud configuration example:_
[source,js]
----
const client = new Client({
cloud: {
id: 'name:bG9jYWxob3N0JGFiY2QkZWZnaA==',
username: 'elastic',
password: 'changeme'
}
})
----
|===
=== Advanced configuration

View File

@ -59,7 +59,7 @@ async function run (): void {
client
.search(params)
.then((result: ApiResponse) => {
console.og(result.body.hits.hits)
console.log(result.body.hits.hits)
})
.catch((err: Error) => {
console.log(err)

22
index.d.ts vendored
View File

@ -20,7 +20,7 @@
/// <reference types="node" />
import { EventEmitter } from 'events';
import { SecureContextOptions } from 'tls';
import { ConnectionOptions as TlsConnectionOptions } from 'tls';
import Transport, {
ApiResponse,
RequestEvent,
@ -31,6 +31,7 @@ import Transport, {
generateRequestIdFn,
TransportRequestCallback
} from './lib/Transport';
import { URL } from 'url';
import Connection, { AgentOptions, agentFn } from './lib/Connection';
import ConnectionPool, { ResurrectEvent } from './lib/ConnectionPool';
import Serializer from './lib/Serializer';
@ -72,8 +73,22 @@ interface ClientExtends {
}
// /Extend API
interface NodeOptions {
url: URL;
id?: string;
agent?: AgentOptions;
ssl?: TlsConnectionOptions;
headers?: anyObject;
roles?: {
master: boolean;
data: boolean;
ingest: boolean;
ml: boolean;
}
}
interface ClientOptions {
node?: string | string[];
node?: string | string[] | NodeOptions | NodeOptions[];
nodes?: string | string[];
Connection?: typeof Connection;
ConnectionPool?: typeof ConnectionPool;
@ -89,7 +104,7 @@ interface ClientOptions {
resurrectStrategy?: 'ping' | 'optimistic' | 'none';
suggestCompression?: boolean;
compression?: 'gzip';
ssl?: SecureContextOptions;
ssl?: TlsConnectionOptions;
agent?: AgentOptions | agentFn;
nodeFilter?: nodeFilterFn;
nodeSelector?: nodeSelectorFn | string;
@ -327,5 +342,6 @@ export {
ResurrectEvent,
RequestParams,
ClientOptions,
NodeOptions,
ClientExtendsCallbackOptions
};

6
lib/Connection.d.ts vendored
View File

@ -22,13 +22,13 @@
import { URL } from 'url';
import { inspect, InspectOptions } from 'util';
import * as http from 'http';
import { SecureContextOptions } from 'tls';
import { ConnectionOptions as TlsConnectionOptions } from 'tls';
export declare type agentFn = () => any;
interface ConnectionOptions {
url: URL;
ssl?: SecureContextOptions;
ssl?: TlsConnectionOptions;
id?: string;
headers?: any;
agent?: AgentOptions | agentFn;
@ -59,7 +59,7 @@ export default class Connection {
ML: string;
};
url: URL;
ssl: SecureContextOptions | null;
ssl: TlsConnectionOptions | null;
id: string;
headers: any;
deadCount: number;

View File

@ -35,6 +35,7 @@ class Connection {
this.ssl = opts.ssl || null
this.id = opts.id || stripAuth(opts.url.href)
this.headers = opts.headers || null
this.auth = opts.auth || { username: null, password: null }
this.deadCount = 0
this.resurrectTimeout = 0
@ -180,6 +181,7 @@ class Connection {
buildRequestObject (params) {
const url = this.url
const { username, password } = this.auth
const request = {
protocol: url.protocol,
hostname: url.hostname[0] === '['
@ -194,8 +196,8 @@ class Connection {
// https://github.com/elastic/elasticsearch-js/issues/843
port: url.port !== '' ? url.port : undefined,
headers: this.headers,
auth: !!url.username === true || !!url.password === true
? `${url.username}:${url.password}`
auth: username != null && password != null
? `${username}:${password}`
: undefined,
agent: this.agent
}
@ -224,12 +226,12 @@ class Connection {
}
// Handles console.log and utils.inspect invocations.
// We want to hide `agent` and `ssl` since they made
// We want to hide `auth`, `agent` and `ssl` since they made
// the logs very hard to read. The user can still
// access them with `instance.agent` and `instance.ssl`.
[inspect.custom] (depth, options) {
return {
url: this.url,
url: stripAuth(this.url.toString()),
id: this.id,
headers: this.headers,
deadCount: this.deadCount,
@ -242,7 +244,7 @@ class Connection {
toJSON () {
return {
url: this.url,
url: stripAuth(this.url.toString()),
id: this.id,
headers: this.headers,
deadCount: this.deadCount,

View File

@ -222,21 +222,24 @@ class ConnectionPool {
// we can add it to them once the connection instance has been created
if (opts.url.username !== '' && opts.url.password !== '') {
this._auth = {
username: opts.url.username,
password: opts.url.password
username: decodeURIComponent(opts.url.username),
password: decodeURIComponent(opts.url.password)
}
opts.auth = this._auth
}
if (this._auth != null) {
if (opts.auth == null || (opts.auth.username == null && opts.auth.password == null)) {
opts.auth = this._auth
opts.url.username = this._auth.username
opts.url.password = this._auth.password
}
}
if (opts.ssl == null) opts.ssl = this._ssl
if (opts.agent == null) opts.agent = this._agent
const connection = new this.Connection(opts)
if (connection.url.username === '' &&
connection.url.password === '' &&
this._auth != null) {
connection.url.username = this._auth.username
connection.url.password = this._auth.password
}
debug('Adding a new connection', connection)
if (this.connections.has(connection.id)) {
throw new Error(`Connection with id '${connection.id}' is already present`)

View File

@ -4,7 +4,7 @@
"main": "index.js",
"types": "index.d.ts",
"homepage": "http://www.elastic.co/guide/en/elasticsearch/client/javascript-api/current/index.html",
"version": "5.6.17",
"version": "5.6.19",
"keywords": [
"elasticsearch",
"elastic",

View File

@ -35,12 +35,12 @@ const {
} = require('./utils')
start(minimist(process.argv.slice(2), {
string: ['tag']
string: ['tag', 'branch']
}))
function start (opts) {
const log = ora('Loading Elasticsearch Repository').start()
if (semver.valid(opts.tag) === null) {
if (opts.branch == null && semver.valid(opts.tag) === null) {
log.fail(`Missing or invalid tag: ${opts.tag}`)
return
}
@ -55,7 +55,7 @@ function start (opts) {
log.text = 'Cleaning API folder...'
rimraf.sync(join(apiOutputFolder, '*.js'))
cloneAndCheckout({ log, tag: opts.tag }, (err, { apiFolder, xPackFolder }) => {
cloneAndCheckout({ log, tag: opts.tag, branch: opts.branch }, (err, { apiFolder, xPackFolder }) => {
if (err) {
log.fail(err.message)
return
@ -74,7 +74,7 @@ function start (opts) {
writeFileSync(
requestParamsOutputFile,
generateRequestTypes(allSpec),
generateRequestTypes(opts.branch || opts.tag, allSpec),
{ encoding: 'utf8' }
)
@ -118,7 +118,7 @@ function start (opts) {
const spec = require(join(apiFolder, file))
allSpec.push(spec)
const code = generate(spec, common)
const code = generate(opts.branch || opts.tag, spec, common)
const filePath = join(apiOutputFolder, `${file.slice(0, file.lastIndexOf('.'))}.js`)
writeFileSync(filePath, code, { encoding: 'utf8' })

View File

@ -29,7 +29,7 @@ const apiFolder = join(esFolder, 'rest-api-spec', 'src', 'main', 'resources', 'r
const xPackFolder = join(esFolder, 'x-pack', 'plugin', 'src', 'test', 'resources', 'rest-api-spec', 'api')
function cloneAndCheckout (opts, callback) {
const { log, tag } = opts
const { log, tag, branch } = opts
withTag(tag, callback)
/**
@ -57,13 +57,19 @@ function cloneAndCheckout (opts, callback) {
if (fresh) {
clone(checkout)
} else if (opts.branch) {
checkout(true)
} else {
checkout()
}
function checkout () {
log.text = `Checking out tag '${tag}'`
git.checkout(tag, err => {
function checkout (alsoPull = false) {
if (branch) {
log.text = `Checking out branch '${branch}'`
} else {
log.text = `Checking out tag '${tag}'`
}
git.checkout(branch || tag, err => {
if (err) {
if (retry++ > 0) {
callback(new Error(`Cannot checkout tag '${tag}'`), { apiFolder, xPackFolder })
@ -71,6 +77,9 @@ function cloneAndCheckout (opts, callback) {
}
return pull(checkout)
}
if (alsoPull) {
return pull(checkout)
}
callback(null, { apiFolder, xPackFolder })
})
}

View File

@ -20,11 +20,16 @@
'use strict'
const dedent = require('dedent')
const semver = require('semver')
const allowedMethods = {
noBody: ['GET', 'HEAD', 'DELETE'],
body: ['POST', 'PUT', 'DELETE']
}
// if a parameter is depracted in a minor release
// we should be able to support it until the next major
const deprecatedParameters = require('./patch.json')
// list of apis that does not need any kind of validation
// because of how the url is built or the `type` handling in ES7
const noPathValidation = [
@ -59,7 +64,8 @@ const ndjsonApi = [
'xpack.monitoring.bulk'
]
function generate (spec, common) {
function generate (version, spec, common) {
const release = semver.valid(version) ? semver.major(version) : version
const api = Object.keys(spec)[0]
const name = api
.replace(/\.([a-z])/g, k => k[1].toUpperCase())
@ -80,7 +86,11 @@ function generate (spec, common) {
if (params[key].required) {
required.push(key)
}
acceptedQuerystring.push(key)
if (deprecatedParameters[release] && deprecatedParameters[release][key]) {
acceptedQuerystring.push(deprecatedParameters[release][key])
}
}
for (const key in spec[api]) {

View File

@ -19,7 +19,11 @@
'use strict'
function generate (api) {
const semver = require('semver')
const deprecatedParameters = require('./patch.json')
function generate (version, api) {
const release = semver.valid(version) ? semver.major(version) : version
var types = `/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
@ -64,9 +68,20 @@ export interface Generic {
const partsArr = Object.keys(parts)
.map(k => ({ key: k, value: parts[k] }))
const deprecatedParametersToAdd = []
const paramsArr = Object.keys(params)
.filter(k => !Object.keys(parts).includes(k))
.map(k => ({ key: k, value: params[k] }))
.map(k => {
if (deprecatedParameters[release] && deprecatedParameters[release][k]) {
deprecatedParametersToAdd.push({
key: deprecatedParameters[release][k],
value: params[k]
})
}
return { key: k, value: params[k] }
})
deprecatedParametersToAdd.forEach(k => partsArr.push(k))
const genLine = e => {
const optional = e.value.required ? '' : '?'

14
scripts/utils/patch.json Normal file
View File

@ -0,0 +1,14 @@
{
"6": {
"_source_includes": "_source_include",
"_source_excludes": "_source_exclude"
},
"7": {
"_source_includes": "_source_include",
"_source_excludes": "_source_exclude"
},
"master": {
"_source_includes": "_source_include",
"_source_excludes": "_source_exclude"
}
}

View File

@ -27,13 +27,29 @@ import {
ResurrectEvent,
events,
errors,
ClientExtendsCallbackOptions
ClientExtendsCallbackOptions,
NodeOptions
} from '../../index'
import { TransportRequestParams, TransportRequestOptions } from '../../lib/Transport'
import { URL } from 'url'
const client = new Client({ node: 'http://localhost:9200' })
const nodeOpts: NodeOptions = {
url: new URL('http://localhost:9200'),
id: 'winteriscoming',
headers: { 'foo': 'bar' },
roles: {
master: false,
data: true,
ingest: false,
ml: false
}
}
const client2 = new Client({ node: nodeOpts })
client.on(events.RESPONSE, (err: errors.ElasticsearchClientError | null, request: RequestEvent) => {
if (err) console.log(err)
const { body, statusCode } = request

View File

@ -61,8 +61,23 @@ test('API', t => {
t.deepEqual(pool._auth, { username: 'foo', password: 'bar' })
pool.addConnection('http://localhost:9201')
t.strictEqual(pool.connections.get('http://localhost:9201/').url.username, 'foo')
t.strictEqual(pool.connections.get('http://localhost:9201/').url.password, 'bar')
const conn = pool.connections.get('http://localhost:9201/')
t.strictEqual(conn.url.username, 'foo')
t.strictEqual(conn.url.password, 'bar')
t.strictEqual(conn.auth.username, 'foo')
t.strictEqual(conn.auth.password, 'bar')
t.end()
})
t.test('addConnection should handle not-friendly url parameters for user and password', t => {
const pool = new ConnectionPool({ Connection })
const href = 'http://us"er:p@assword@localhost:9200/'
pool.addConnection(href)
const conn = pool.getConnection()
t.strictEqual(conn.url.username, 'us%22er')
t.strictEqual(conn.url.password, 'p%40assword')
t.strictEqual(conn.auth.username, 'us"er')
t.strictEqual(conn.auth.password, 'p@assword')
t.end()
})

View File

@ -526,7 +526,8 @@ test('Url with auth', t => {
buildServer(handler, ({ port }, server) => {
const connection = new Connection({
url: new URL(`http://foo:bar@localhost:${port}`)
url: new URL(`http://foo:bar@localhost:${port}`),
auth: { username: 'foo', password: 'bar' }
})
connection.request({
path: '/hello',
@ -722,11 +723,11 @@ test('setRole', t => {
t.end()
})
test('Util.inspect Connection class should hide agent and ssl', t => {
test('Util.inspect Connection class should hide agent, ssl and auth', t => {
t.plan(1)
const connection = new Connection({
url: new URL('http://localhost:9200'),
url: new URL('http://user:password@localhost:9200'),
id: 'node-id',
headers: { foo: 'bar' }
})
@ -740,20 +741,7 @@ test('Util.inspect Connection class should hide agent and ssl', t => {
.replace(/(\r\n|\n|\r)/gm, '')
}
t.strictEqual(cleanStr(inspect(connection)), cleanStr(`{ url:
URL {
href: 'http://localhost:9200/',
origin: 'http://localhost:9200',
protocol: 'http:',
username: '',
password: '',
host: 'localhost:9200',
hostname: 'localhost',
port: '9200',
pathname: '/',
search: '',
searchParams: URLSearchParams {},
hash: '' },
t.strictEqual(cleanStr(inspect(connection)), cleanStr(`{ url: 'http://localhost:9200/',
id: 'node-id',
headers: { foo: 'bar' },
deadCount: 0,
@ -764,6 +752,34 @@ test('Util.inspect Connection class should hide agent and ssl', t => {
)
})
test('connection.toJSON should hide agent, ssl and auth', t => {
t.plan(1)
const connection = new Connection({
url: new URL('http://user:password@localhost:9200'),
id: 'node-id',
headers: { foo: 'bar' }
})
t.deepEqual(connection.toJSON(), {
url: 'http://localhost:9200/',
id: 'node-id',
headers: {
foo: 'bar'
},
deadCount: 0,
resurrectTimeout: 0,
_openRequests: 0,
status: 'alive',
roles: {
master: true,
data: true,
ingest: true,
ml: false
}
})
})
// https://github.com/elastic/elasticsearch-js/issues/843
test('Port handling', t => {
t.test('http 80', t => {