Added x-elastic-client-meta header (#1373)

This commit is contained in:
Tomas Della Vedova
2020-12-16 10:42:26 +01:00
committed by GitHub
parent 4cace4c234
commit 61eee69424
8 changed files with 115 additions and 8 deletions

View File

@ -218,10 +218,15 @@ _Default:_ `null`
_Default:_ `{}`
|`context`
|`object` - A custom object that you can use for observability in yoru events.
|`object` - A custom object that you can use for observability in your events.
It will be merged with the API level context option. +
_Default:_ `null`
|`enableMetaHeader`
|`boolean` - If true, adds an header named `'x-elastic-client-meta'`, containing some minimal telemetry data,
such as the client and platform version. +
_Default:_ `true`
|`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]

1
index.d.ts vendored
View File

@ -108,6 +108,7 @@ interface ClientOptions {
auth?: BasicAuth | ApiKeyAuth;
context?: Context;
proxy?: string | URL;
enableMetaHeader?: boolean;
cloud?: {
id: string;
// TODO: remove username and password here in 8

View File

@ -33,6 +33,8 @@ const Serializer = require('./lib/Serializer')
const errors = require('./lib/errors')
const { ConfigurationError } = errors
const { prepareHeaders } = Connection.internals
const clientVersion = require('./package.json').version
const nodeVersion = process.versions.node
const kInitialOptions = Symbol('elasticsearchjs-initial-options')
const kChild = Symbol('elasticsearchjs-child')
@ -125,13 +127,18 @@ class Client extends ESAPI {
auth: null,
opaqueIdPrefix: null,
context: null,
proxy: null
proxy: null,
enableMetaHeader: true
}, opts)
this[kInitialOptions] = options
this[kExtensions] = []
this.name = options.name
if (options.enableMetaHeader) {
options.headers['x-elastic-client-meta'] = `es=${clientVersion},js=${nodeVersion},t=${clientVersion},hc=${nodeVersion}`
}
if (opts[kChild] !== undefined) {
this.serializer = options[kChild].serializer
this.connectionPool = options[kChild].connectionPool
@ -179,7 +186,13 @@ class Client extends ESAPI {
/* istanbul ignore else */
if (Helpers !== null) {
this.helpers = new Helpers({ client: this, maxRetries: options.maxRetries })
this.helpers = new Helpers({
client: this,
maxRetries: options.maxRetries,
metaHeader: options.enableMetaHeader
? `es=${clientVersion},js=${nodeVersion},t=${clientVersion},hc=${nodeVersion}`
: null
})
}
}

View File

@ -28,12 +28,14 @@ const { ResponseError, ConfigurationError } = require('./errors')
const pImmediate = promisify(setImmediate)
const sleep = promisify(setTimeout)
const kClient = Symbol('elasticsearch-client')
const kMetaHeader = Symbol('meta header')
/* istanbul ignore next */
const noop = () => {}
class Helpers {
constructor (opts) {
this[kClient] = opts.client
this[kMetaHeader] = opts.metaHeader
this.maxRetries = opts.maxRetries
}
@ -71,6 +73,10 @@ class Helpers {
* @return {iterator} the async iterator
*/
async * scrollSearch (params, options = {}) {
if (this[kMetaHeader] !== null) {
options.headers = options.headers || {}
options.headers['x-elastic-client-meta'] = this[kMetaHeader] + ',h=s'
}
// TODO: study scroll search slices
const wait = options.wait || 5000
const maxRetries = options.maxRetries || this.maxRetries
@ -99,7 +105,7 @@ class Helpers {
stop = true
await this[kClient].clearScroll(
{ body: { scroll_id } },
{ ignore: [400] }
{ ignore: [400], ...options }
)
}
@ -414,6 +420,7 @@ class Helpers {
bulk (options) {
const client = this[kClient]
const { serialize, deserialize } = client.serializer
const reqOptions = this[kMetaHeader] !== null ? { headers: { 'x-elastic-client-meta': this[kMetaHeader] + ',h=bp' } } : {}
const {
datasource,
onDocument,
@ -676,7 +683,7 @@ class Helpers {
function tryBulk (bulkBody, callback) {
if (shouldAbort === true) return callback(null, [])
client.bulk(Object.assign({}, bulkOptions, { body: bulkBody }), (err, { body }) => {
client.bulk(Object.assign({}, bulkOptions, { body: bulkBody }), reqOptions, (err, { body }) => {
if (err) return callback(err, null)
if (body.errors === false) {
stats.successful += body.items.length

View File

@ -44,6 +44,7 @@ class Transport {
if (typeof opts.compression === 'string' && opts.compression !== 'gzip') {
throw new ConfigurationError(`Invalid compression: '${opts.compression}'`)
}
this.emit = opts.emit
this.connectionPool = opts.connectionPool
this.serializer = opts.serializer

View File

@ -26,6 +26,8 @@ const intoStream = require('into-stream')
const { Client, ConnectionPool, Transport, Connection, errors } = require('../../index')
const { CloudConnectionPool } = require('../../lib/pool')
const { buildServer } = require('../utils')
const clientVersion = require('../../package.json').version
const nodeVersion = process.versions.node
test('Configure host', t => {
t.test('Single string', t => {
@ -1300,3 +1302,62 @@ test('Content length too big (string)', t => {
t.strictEqual(result.meta.attempts, 0)
})
})
test('Meta header enabled', t => {
t.plan(2)
class MockConnection extends Connection {
request (params, callback) {
t.match(params.headers, { 'x-elastic-client-meta': `es=${clientVersion},js=${nodeVersion},t=${clientVersion},hc=${nodeVersion}` })
const stream = intoStream(JSON.stringify({ hello: 'world' }))
stream.statusCode = 200
stream.headers = {
'content-type': 'application/json;utf=8',
'content-length': '17',
connection: 'keep-alive',
date: new Date().toISOString()
}
process.nextTick(callback, null, stream)
return { abort () {} }
}
}
const client = new Client({
node: 'http://localhost:9200',
Connection: MockConnection
})
client.info((err, result) => {
t.error(err)
})
})
test('Meta header disabled', t => {
t.plan(2)
class MockConnection extends Connection {
request (params, callback) {
t.notMatch(params.headers, { 'x-elastic-client-meta': `es=${clientVersion},js=${nodeVersion},t=${clientVersion},hc=${nodeVersion}` })
const stream = intoStream(JSON.stringify({ hello: 'world' }))
stream.statusCode = 200
stream.headers = {
'content-type': 'application/json;utf=8',
'content-length': '17',
connection: 'keep-alive',
date: new Date().toISOString()
}
process.nextTick(callback, null, stream)
return { abort () {} }
}
}
const client = new Client({
node: 'http://localhost:9200',
Connection: MockConnection,
enableMetaHeader: false
})
client.info((err, result) => {
t.error(err)
})
})

View File

@ -27,6 +27,8 @@ const semver = require('semver')
const { test } = require('tap')
const { Client, errors } = require('../../../')
const { buildServer, connection } = require('../../utils')
const clientVersion = require('../../../package.json').version
const nodeVersion = process.versions.node
const dataset = [
{ user: 'jon', age: 23 },
@ -41,7 +43,10 @@ test('bulk index', t => {
const MockConnection = connection.buildMockConnection({
onRequest (params) {
t.strictEqual(params.path, '/_bulk')
t.match(params.headers, { 'content-type': 'application/x-ndjson' })
t.match(params.headers, {
'content-type': 'application/x-ndjson',
'x-elastic-client-meta': `es=${clientVersion},js=${nodeVersion},t=${clientVersion},hc=${nodeVersion},h=bp`
})
const [action, payload] = params.body.split('\n')
t.deepEqual(JSON.parse(action), { index: { _index: 'test' } })
t.deepEqual(JSON.parse(payload), dataset[count++])
@ -84,6 +89,9 @@ test('bulk index', t => {
onRequest (params) {
t.strictEqual(params.path, '/_bulk')
t.match(params.headers, { 'content-type': 'application/x-ndjson' })
t.notMatch(params.headers, {
'x-elastic-client-meta': `es=${clientVersion},js=${nodeVersion},t=${clientVersion},hc=${nodeVersion},h=bp`
})
const [action, payload] = params.body.split('\n')
t.deepEqual(JSON.parse(action), { index: { _index: 'test' } })
t.deepEqual(JSON.parse(payload), dataset[count++])
@ -93,7 +101,8 @@ test('bulk index', t => {
const client = new Client({
node: 'http://localhost:9200',
Connection: MockConnection
Connection: MockConnection,
enableMetaHeader: false
})
const result = await client.helpers.bulk({
datasource: dataset.slice(),

View File

@ -22,11 +22,17 @@
const { test } = require('tap')
const { Client, errors } = require('../../../')
const { connection } = require('../../utils')
const clientVersion = require('../../../package.json').version
const nodeVersion = process.versions.node
test('Scroll search', async t => {
var count = 0
const MockConnection = connection.buildMockConnection({
onRequest (params) {
t.match(params.headers, {
'x-elastic-client-meta': `es=${clientVersion},js=${nodeVersion},t=${clientVersion},hc=${nodeVersion},h=s`
})
count += 1
if (params.method === 'POST') {
t.strictEqual(params.querystring, 'scroll=1m')
@ -73,6 +79,9 @@ test('Clear a scroll search', async t => {
var count = 0
const MockConnection = connection.buildMockConnection({
onRequest (params) {
t.notMatch(params.headers, {
'x-elastic-client-meta': `es=${clientVersion},js=${nodeVersion},t=${clientVersion},hc=${nodeVersion},h=s`
})
if (params.method === 'DELETE') {
const body = JSON.parse(params.body)
t.strictEqual(body.scroll_id, 'id')
@ -95,7 +104,8 @@ test('Clear a scroll search', async t => {
const client = new Client({
node: 'http://localhost:9200',
Connection: MockConnection
Connection: MockConnection,
enableMetaHeader: false
})
const scrollSearch = client.helpers.scrollSearch({