@elastic/elasticsearch 7.10.0 → 7.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (94) hide show
  1. package/README.md +2 -2
  2. package/api/api/async_search.js +35 -8
  3. package/api/api/autoscaling.js +15 -15
  4. package/api/api/bulk.js +4 -4
  5. package/api/api/cat.js +127 -127
  6. package/api/api/ccr.js +40 -40
  7. package/api/api/clear_scroll.js +2 -2
  8. package/api/api/close_point_in_time.js +2 -2
  9. package/api/api/cluster.js +36 -36
  10. package/api/api/count.js +3 -3
  11. package/api/api/create.js +5 -5
  12. package/api/api/dangling_indices.js +8 -8
  13. package/api/api/delete.js +4 -4
  14. package/api/api/delete_by_query.js +5 -5
  15. package/api/api/delete_by_query_rethrottle.js +4 -4
  16. package/api/api/delete_script.js +3 -3
  17. package/api/api/enrich.js +14 -14
  18. package/api/api/eql.js +41 -10
  19. package/api/api/exists.js +4 -4
  20. package/api/api/exists_source.js +6 -6
  21. package/api/api/explain.js +4 -4
  22. package/api/api/features.js +81 -0
  23. package/api/api/field_caps.js +2 -2
  24. package/api/api/fleet.js +65 -0
  25. package/api/api/get.js +4 -4
  26. package/api/api/get_script.js +3 -3
  27. package/api/api/get_script_context.js +2 -2
  28. package/api/api/get_script_languages.js +2 -2
  29. package/api/api/get_source.js +4 -4
  30. package/api/api/graph.js +4 -4
  31. package/api/api/ilm.js +26 -26
  32. package/api/api/index.js +4 -4
  33. package/api/api/indices.js +385 -339
  34. package/api/api/info.js +2 -2
  35. package/api/api/ingest.js +37 -15
  36. package/api/api/license.js +14 -14
  37. package/api/api/logstash.js +125 -0
  38. package/api/api/mget.js +4 -4
  39. package/api/api/migration.js +2 -2
  40. package/api/api/ml.js +344 -204
  41. package/api/api/monitoring.js +3 -3
  42. package/api/api/msearch.js +4 -4
  43. package/api/api/msearch_template.js +4 -4
  44. package/api/api/mtermvectors.js +3 -3
  45. package/api/api/nodes.js +12 -12
  46. package/api/api/open_point_in_time.js +2 -2
  47. package/api/api/ping.js +2 -2
  48. package/api/api/put_script.js +5 -5
  49. package/api/api/rank_eval.js +3 -3
  50. package/api/api/reindex.js +3 -3
  51. package/api/api/reindex_rethrottle.js +4 -4
  52. package/api/api/render_search_template.js +2 -2
  53. package/api/api/rollup.js +66 -25
  54. package/api/api/scripts_painless_execute.js +2 -2
  55. package/api/api/scroll.js +2 -2
  56. package/api/api/search.js +5 -5
  57. package/api/api/search_shards.js +2 -2
  58. package/api/api/search_template.js +4 -4
  59. package/api/api/searchable_snapshots.js +42 -15
  60. package/api/api/security.js +295 -85
  61. package/api/api/shutdown.js +124 -0
  62. package/api/api/slm.js +21 -21
  63. package/api/api/snapshot.js +48 -48
  64. package/api/api/sql.js +9 -9
  65. package/api/api/ssl.js +2 -2
  66. package/api/api/tasks.js +7 -7
  67. package/api/api/termvectors.js +3 -3
  68. package/api/api/text_structure.js +65 -0
  69. package/api/api/transform.js +27 -27
  70. package/api/api/update.js +5 -5
  71. package/api/api/update_by_query.js +4 -4
  72. package/api/api/update_by_query_rethrottle.js +4 -4
  73. package/api/api/watcher.js +50 -28
  74. package/api/api/xpack.js +4 -4
  75. package/api/index.js +176 -120
  76. package/api/new.d.ts +1498 -0
  77. package/api/requestParams.d.ts +168 -8
  78. package/api/types.d.ts +13881 -0
  79. package/api/utils.js +4 -4
  80. package/free-report-junit.xml +3410 -0
  81. package/index.d.ts +275 -30
  82. package/index.js +29 -30
  83. package/lib/Connection.js +7 -5
  84. package/lib/Helpers.d.ts +2 -2
  85. package/lib/Helpers.js +30 -18
  86. package/lib/Serializer.d.ts +5 -0
  87. package/lib/Serializer.js +17 -6
  88. package/lib/Transport.d.ts +2 -1
  89. package/lib/Transport.js +38 -8
  90. package/lib/errors.js +14 -1
  91. package/lib/pool/BaseConnectionPool.js +3 -3
  92. package/lib/pool/ConnectionPool.js +4 -5
  93. package/package.json +26 -26
  94. package/api/kibana.d.ts +0 -479
package/index.js CHANGED
@@ -19,20 +19,24 @@
19
19
 
20
20
  'use strict'
21
21
 
22
- const nodeMajor = Number(process.versions.node.split('.')[0])
23
-
24
22
  const { EventEmitter } = require('events')
25
23
  const { URL } = require('url')
26
24
  const debug = require('debug')('elasticsearch')
27
25
  const Transport = require('./lib/Transport')
28
26
  const Connection = require('./lib/Connection')
29
27
  const { ConnectionPool, CloudConnectionPool } = require('./lib/pool')
30
- // Helpers works only in Node.js >= 10
31
- const Helpers = nodeMajor < 10 ? /* istanbul ignore next */ null : require('./lib/Helpers')
28
+ const Helpers = require('./lib/Helpers')
32
29
  const Serializer = require('./lib/Serializer')
33
30
  const errors = require('./lib/errors')
34
31
  const { ConfigurationError } = errors
35
32
  const { prepareHeaders } = Connection.internals
33
+ let clientVersion = require('./package.json').version
34
+ /* istanbul ignore next */
35
+ if (clientVersion.includes('-')) {
36
+ // clean prerelease
37
+ clientVersion = clientVersion.slice(0, clientVersion.indexOf('-')) + 'p'
38
+ }
39
+ const nodeVersion = process.versions.node
36
40
 
37
41
  const kInitialOptions = Symbol('elasticsearchjs-initial-options')
38
42
  const kChild = Symbol('elasticsearchjs-child')
@@ -41,24 +45,6 @@ const kEventEmitter = Symbol('elasticsearchjs-event-emitter')
41
45
 
42
46
  const ESAPI = require('./api')
43
47
 
44
- /* istanbul ignore next */
45
- if (nodeMajor < 10) {
46
- process.emitWarning('You are using a version of Node.js that is currently in EOL. ' +
47
- 'The support for this version will be dropped in 7.12. ' +
48
- 'Please refer to https://ela.st/nodejs-support for additional information.',
49
- 'DeprecationWarning'
50
- )
51
- }
52
-
53
- /* istanbul ignore next */
54
- if (nodeMajor >= 10 && nodeMajor < 12) {
55
- process.emitWarning('You are using a version of Node.js that will reach EOL in April 2021. ' +
56
- 'The support for this version will be dropped in 7.13. ' +
57
- 'Please refer to https://ela.st/nodejs-support for additional information.',
58
- 'DeprecationWarning'
59
- )
60
- }
61
-
62
48
  class Client extends ESAPI {
63
49
  constructor (opts = {}) {
64
50
  super({ ConfigurationError })
@@ -125,20 +111,28 @@ class Client extends ESAPI {
125
111
  auth: null,
126
112
  opaqueIdPrefix: null,
127
113
  context: null,
128
- proxy: null
114
+ proxy: null,
115
+ enableMetaHeader: true,
116
+ disablePrototypePoisoningProtection: false
129
117
  }, opts)
130
118
 
131
119
  this[kInitialOptions] = options
132
120
  this[kExtensions] = []
133
121
  this.name = options.name
134
122
 
123
+ if (options.enableMetaHeader) {
124
+ options.headers['x-elastic-client-meta'] = `es=${clientVersion},js=${nodeVersion},t=${clientVersion},hc=${nodeVersion}`
125
+ }
126
+
135
127
  if (opts[kChild] !== undefined) {
136
128
  this.serializer = options[kChild].serializer
137
129
  this.connectionPool = options[kChild].connectionPool
138
130
  this[kEventEmitter] = options[kChild].eventEmitter
139
131
  } else {
140
132
  this[kEventEmitter] = new EventEmitter()
141
- this.serializer = new options.Serializer()
133
+ this.serializer = new options.Serializer({
134
+ disablePrototypePoisoningProtection: options.disablePrototypePoisoningProtection
135
+ })
142
136
  this.connectionPool = new options.ConnectionPool({
143
137
  pingTimeout: options.pingTimeout,
144
138
  resurrectStrategy: options.resurrectStrategy,
@@ -177,10 +171,13 @@ class Client extends ESAPI {
177
171
  context: options.context
178
172
  })
179
173
 
180
- /* istanbul ignore else */
181
- if (Helpers !== null) {
182
- this.helpers = new Helpers({ client: this, maxRetries: options.maxRetries })
183
- }
174
+ this.helpers = new Helpers({
175
+ client: this,
176
+ maxRetries: options.maxRetries,
177
+ metaHeader: options.enableMetaHeader
178
+ ? `es=${clientVersion},js=${nodeVersion},t=${clientVersion},hc=${nodeVersion}`
179
+ : null
180
+ })
184
181
  }
185
182
 
186
183
  get emit () {
@@ -205,7 +202,7 @@ class Client extends ESAPI {
205
202
  opts = {}
206
203
  }
207
204
 
208
- var [namespace, method] = name.split('.')
205
+ let [namespace, method] = name.split('.')
209
206
  if (method == null) {
210
207
  method = namespace
211
208
  namespace = null
@@ -314,7 +311,9 @@ const events = {
314
311
  RESPONSE: 'response',
315
312
  REQUEST: 'request',
316
313
  SNIFF: 'sniff',
317
- RESURRECT: 'resurrect'
314
+ RESURRECT: 'resurrect',
315
+ SERIALIZATION: 'serialization',
316
+ DESERIALIZATION: 'deserialization'
318
317
  }
319
318
 
320
319
  module.exports = {
package/lib/Connection.js CHANGED
@@ -25,7 +25,7 @@ const hpagent = require('hpagent')
25
25
  const http = require('http')
26
26
  const https = require('https')
27
27
  const debug = require('debug')('elasticsearch')
28
- const pump = require('pump')
28
+ const { pipeline } = require('stream')
29
29
  const INVALID_PATH_REGEX = /[^\u0021-\u00ff]/
30
30
  const {
31
31
  ConnectionError,
@@ -82,6 +82,7 @@ class Connection {
82
82
 
83
83
  request (params, callback) {
84
84
  this._openRequests++
85
+ let cleanedListeners = false
85
86
 
86
87
  const requestParams = this.buildRequestObject(params)
87
88
  // https://github.com/nodejs/node/commit/b961d9fd83
@@ -132,9 +133,9 @@ class Connection {
132
133
 
133
134
  // starts the request
134
135
  if (isStream(params.body) === true) {
135
- pump(params.body, request, err => {
136
+ pipeline(params.body, request, err => {
136
137
  /* istanbul ignore if */
137
- if (err != null) {
138
+ if (err != null && cleanedListeners === false) {
138
139
  cleanListeners()
139
140
  this._openRequests--
140
141
  callback(err, null)
@@ -151,6 +152,7 @@ class Connection {
151
152
  request.removeListener('timeout', onTimeout)
152
153
  request.removeListener('error', onError)
153
154
  request.removeListener('abort', onAbort)
155
+ cleanedListeners = true
154
156
  }
155
157
  }
156
158
 
@@ -211,8 +213,8 @@ class Connection {
211
213
  }
212
214
 
213
215
  const paramsKeys = Object.keys(params)
214
- for (var i = 0, len = paramsKeys.length; i < len; i++) {
215
- var key = paramsKeys[i]
216
+ for (let i = 0, len = paramsKeys.length; i < len; i++) {
217
+ const key = paramsKeys[i]
216
218
  if (key === 'path') {
217
219
  request.pathname = resolve(request.pathname, params[key])
218
220
  } else if (key === 'querystring' && !!params[key] === true) {
package/lib/Helpers.d.ts CHANGED
@@ -25,8 +25,8 @@ export default class Helpers {
25
25
  search<TDocument = unknown, TRequestBody extends RequestBody = Record<string, any>>(params: Search<TRequestBody>, options?: TransportRequestOptions): Promise<TDocument[]>
26
26
  scrollSearch<TDocument = unknown, TResponse = Record<string, any>, TRequestBody extends RequestBody = Record<string, any>, TContext = Context>(params: Search<TRequestBody>, options?: TransportRequestOptions): AsyncIterable<ScrollSearchResponse<TDocument, TResponse, TContext>>
27
27
  scrollDocuments<TDocument = unknown, TRequestBody extends RequestBody = Record<string, any>>(params: Search<TRequestBody>, options?: TransportRequestOptions): AsyncIterable<TDocument>
28
- msearch(options?: MsearchHelperOptions): MsearchHelper
29
- bulk<TDocument = unknown>(options: BulkHelperOptions<TDocument>): BulkHelper<BulkStats>
28
+ msearch(options?: MsearchHelperOptions, reqOptions?: TransportRequestOptions): MsearchHelper
29
+ bulk<TDocument = unknown>(options: BulkHelperOptions<TDocument>, reqOptions?: TransportRequestOptions): BulkHelper<BulkStats>
30
30
  }
31
31
 
32
32
  export interface ScrollSearchResponse<TDocument = unknown, TResponse = Record<string, any>, TContext = Context> extends ApiResponse<TResponse, TContext> {
package/lib/Helpers.js CHANGED
@@ -28,12 +28,14 @@ const { ResponseError, ConfigurationError } = require('./errors')
28
28
  const pImmediate = promisify(setImmediate)
29
29
  const sleep = promisify(setTimeout)
30
30
  const kClient = Symbol('elasticsearch-client')
31
+ const kMetaHeader = Symbol('meta header')
31
32
  /* istanbul ignore next */
32
33
  const noop = () => {}
33
34
 
34
35
  class Helpers {
35
36
  constructor (opts) {
36
37
  this[kClient] = opts.client
38
+ this[kMetaHeader] = opts.metaHeader
37
39
  this.maxRetries = opts.maxRetries
38
40
  }
39
41
 
@@ -71,6 +73,10 @@ class Helpers {
71
73
  * @return {iterator} the async iterator
72
74
  */
73
75
  async * scrollSearch (params, options = {}) {
76
+ if (this[kMetaHeader] !== null) {
77
+ options.headers = options.headers || {}
78
+ options.headers['x-elastic-client-meta'] = this[kMetaHeader] + ',h=s'
79
+ }
74
80
  // TODO: study scroll search slices
75
81
  const wait = options.wait || 5000
76
82
  const maxRetries = options.maxRetries || this.maxRetries
@@ -99,7 +105,7 @@ class Helpers {
99
105
  stop = true
100
106
  await this[kClient].clearScroll(
101
107
  { body: { scroll_id } },
102
- { ignore: [400] }
108
+ { ignore: [400], ...options }
103
109
  )
104
110
  }
105
111
 
@@ -152,7 +158,7 @@ class Helpers {
152
158
  */
153
159
  async * scrollDocuments (params, options) {
154
160
  appendFilterPath('hits.hits._source', params, true)
155
- for await (const { documents } of this.scrollSearch(params)) {
161
+ for await (const { documents } of this.scrollSearch(params, options)) {
156
162
  for (const document of documents) {
157
163
  yield document
158
164
  }
@@ -163,9 +169,10 @@ class Helpers {
163
169
  * Creates a msearch helper instance. Once you configure it, you can use the provided
164
170
  * `search` method to add new searches in the queue.
165
171
  * @param {object} options - The configuration of the msearch operations.
172
+ * @param {object} reqOptions - The client optional configuration for this request.
166
173
  * @return {object} The possible operations to run.
167
174
  */
168
- msearch (options = {}) {
175
+ msearch (options = {}, reqOptions = {}) {
169
176
  const client = this[kClient]
170
177
  const {
171
178
  operations = 5,
@@ -372,7 +379,7 @@ class Helpers {
372
379
  // This function never returns an error, if the msearch operation fails,
373
380
  // the error is dispatched to all search executors.
374
381
  function tryMsearch (msearchBody, callbacks, done) {
375
- client.msearch(Object.assign({}, msearchOptions, { body: msearchBody }), (err, results) => {
382
+ client.msearch(Object.assign({}, msearchOptions, { body: msearchBody }), reqOptions, (err, results) => {
376
383
  const retryBody = []
377
384
  const retryCallbacks = []
378
385
  if (err) {
@@ -409,11 +416,16 @@ class Helpers {
409
416
  * Creates a bulk helper instance. Once you configure it, you can pick which operation
410
417
  * to execute with the given dataset, index, create, update, and delete.
411
418
  * @param {object} options - The configuration of the bulk operation.
419
+ * @param {object} reqOptions - The client optional configuration for this request.
412
420
  * @return {object} The possible operations to run with the datasource.
413
421
  */
414
- bulk (options) {
422
+ bulk (options, reqOptions = {}) {
415
423
  const client = this[kClient]
416
- const { serialize, deserialize } = client.serializer
424
+ const { serializer } = client
425
+ if (this[kMetaHeader] !== null) {
426
+ reqOptions.headers = reqOptions.headers || {}
427
+ reqOptions.headers['x-elastic-client-meta'] = this[kMetaHeader] + ',h=bp'
428
+ }
417
429
  const {
418
430
  datasource,
419
431
  onDocument,
@@ -493,19 +505,19 @@ class Helpers {
493
505
  ? Object.keys(action[0])[0]
494
506
  : Object.keys(action)[0]
495
507
  if (operation === 'index' || operation === 'create') {
496
- actionBody = serialize(action)
497
- payloadBody = typeof chunk === 'string' ? chunk : serialize(chunk)
508
+ actionBody = serializer.serialize(action)
509
+ payloadBody = typeof chunk === 'string' ? chunk : serializer.serialize(chunk)
498
510
  chunkBytes += Buffer.byteLength(actionBody) + Buffer.byteLength(payloadBody)
499
511
  bulkBody.push(actionBody, payloadBody)
500
512
  } else if (operation === 'update') {
501
- actionBody = serialize(action[0])
513
+ actionBody = serializer.serialize(action[0])
502
514
  payloadBody = typeof chunk === 'string'
503
515
  ? `{"doc":${chunk}}`
504
- : serialize({ doc: chunk, ...action[1] })
516
+ : serializer.serialize({ doc: chunk, ...action[1] })
505
517
  chunkBytes += Buffer.byteLength(actionBody) + Buffer.byteLength(payloadBody)
506
518
  bulkBody.push(actionBody, payloadBody)
507
519
  } else if (operation === 'delete') {
508
- actionBody = serialize(action)
520
+ actionBody = serializer.serialize(action)
509
521
  chunkBytes += Buffer.byteLength(actionBody)
510
522
  bulkBody.push(actionBody)
511
523
  } else {
@@ -538,7 +550,7 @@ class Helpers {
538
550
  index: typeof refreshOnCompletion === 'string'
539
551
  ? refreshOnCompletion
540
552
  : '_all'
541
- })
553
+ }, reqOptions)
542
554
  }
543
555
 
544
556
  stats.time = Date.now() - startTime
@@ -657,13 +669,13 @@ class Helpers {
657
669
  return
658
670
  }
659
671
  for (let i = 0, len = bulkBody.length; i < len; i = i + 2) {
660
- const operation = Object.keys(deserialize(bulkBody[i]))[0]
672
+ const operation = Object.keys(serializer.deserialize(bulkBody[i]))[0]
661
673
  onDrop({
662
674
  status: 429,
663
675
  error: null,
664
- operation: deserialize(bulkBody[i]),
676
+ operation: serializer.deserialize(bulkBody[i]),
665
677
  document: operation !== 'delete'
666
- ? deserialize(bulkBody[i + 1])
678
+ ? serializer.deserialize(bulkBody[i + 1])
667
679
  /* istanbul ignore next */
668
680
  : null,
669
681
  retried: isRetrying
@@ -676,7 +688,7 @@ class Helpers {
676
688
 
677
689
  function tryBulk (bulkBody, callback) {
678
690
  if (shouldAbort === true) return callback(null, [])
679
- client.bulk(Object.assign({}, bulkOptions, { body: bulkBody }), (err, { body }) => {
691
+ client.bulk(Object.assign({}, bulkOptions, { body: bulkBody }), reqOptions, (err, { body }) => {
680
692
  if (err) return callback(err, null)
681
693
  if (body.errors === false) {
682
694
  stats.successful += body.items.length
@@ -704,9 +716,9 @@ class Helpers {
704
716
  onDrop({
705
717
  status: status,
706
718
  error: action[operation].error,
707
- operation: deserialize(bulkBody[indexSlice]),
719
+ operation: serializer.deserialize(bulkBody[indexSlice]),
708
720
  document: operation !== 'delete'
709
- ? deserialize(bulkBody[indexSlice + 1])
721
+ ? serializer.deserialize(bulkBody[indexSlice + 1])
710
722
  : null,
711
723
  retried: isRetrying
712
724
  })
@@ -17,7 +17,12 @@
17
17
  * under the License.
18
18
  */
19
19
 
20
+ export interface SerializerOptions {
21
+ disablePrototypePoisoningProtection: boolean | 'proto' | 'constructor'
22
+ }
23
+
20
24
  export default class Serializer {
25
+ constructor (opts?: SerializerOptions)
21
26
  serialize(object: any): string;
22
27
  deserialize(json: string): any;
23
28
  ndserialize(array: any[]): string;
package/lib/Serializer.js CHANGED
@@ -23,12 +23,22 @@ const { stringify } = require('querystring')
23
23
  const debug = require('debug')('elasticsearch')
24
24
  const sjson = require('secure-json-parse')
25
25
  const { SerializationError, DeserializationError } = require('./errors')
26
+ const kJsonOptions = Symbol('secure json parse options')
26
27
 
27
28
  class Serializer {
29
+ constructor (opts = {}) {
30
+ const disable = opts.disablePrototypePoisoningProtection
31
+ this[kJsonOptions] = {
32
+ protoAction: disable === true || disable === 'proto' ? 'ignore' : 'error',
33
+ constructorAction: disable === true || disable === 'constructor' ? 'ignore' : 'error'
34
+ }
35
+ }
36
+
28
37
  serialize (object) {
29
38
  debug('Serializing', object)
39
+ let json
30
40
  try {
31
- var json = JSON.stringify(object)
41
+ json = JSON.stringify(object)
32
42
  } catch (err) {
33
43
  throw new SerializationError(err.message, object)
34
44
  }
@@ -37,8 +47,9 @@ class Serializer {
37
47
 
38
48
  deserialize (json) {
39
49
  debug('Deserializing', json)
50
+ let object
40
51
  try {
41
- var object = sjson.parse(json)
52
+ object = sjson.parse(json, this[kJsonOptions])
42
53
  } catch (err) {
43
54
  throw new DeserializationError(err.message, json)
44
55
  }
@@ -50,8 +61,8 @@ class Serializer {
50
61
  if (Array.isArray(array) === false) {
51
62
  throw new SerializationError('The argument provided is not an array')
52
63
  }
53
- var ndjson = ''
54
- for (var i = 0, len = array.length; i < len; i++) {
64
+ let ndjson = ''
65
+ for (let i = 0, len = array.length; i < len; i++) {
55
66
  if (typeof array[i] === 'string') {
56
67
  ndjson += array[i] + '\n'
57
68
  } else {
@@ -67,8 +78,8 @@ class Serializer {
67
78
  if (typeof object === 'string') return object
68
79
  // arrays should be serialized as comma separated list
69
80
  const keys = Object.keys(object)
70
- for (var i = 0, len = keys.length; i < len; i++) {
71
- var key = keys[i]
81
+ for (let i = 0, len = keys.length; i < len; i++) {
82
+ const key = keys[i]
72
83
  // elasticsearch will complain for keys without a value
73
84
  if (object[key] === undefined) {
74
85
  delete object[key]
@@ -28,7 +28,7 @@ export type ApiError = errors.ConfigurationError | errors.ConnectionError |
28
28
  errors.NoLivingConnectionsError | errors.ResponseError |
29
29
  errors.TimeoutError | errors.RequestAbortedError
30
30
 
31
- export type Context = Record<string, unknown> | null
31
+ export type Context = unknown
32
32
 
33
33
  export interface nodeSelectorFn {
34
34
  (connections: Connection[]): Connection;
@@ -120,6 +120,7 @@ export interface TransportRequestCallback {
120
120
 
121
121
  export interface TransportRequestPromise<T> extends Promise<T> {
122
122
  abort: () => void;
123
+ finally(onFinally?: (() => void) | undefined | null): Promise<T>;
123
124
  }
124
125
 
125
126
  export interface TransportGetConnectionOptions {
package/lib/Transport.js CHANGED
@@ -22,6 +22,7 @@
22
22
  const debug = require('debug')('elasticsearch')
23
23
  const os = require('os')
24
24
  const { gzip, unzip, createGzip } = require('zlib')
25
+ const buffer = require('buffer')
25
26
  const ms = require('ms')
26
27
  const {
27
28
  ConnectionError,
@@ -35,12 +36,15 @@ const noop = () => {}
35
36
 
36
37
  const clientVersion = require('../package.json').version
37
38
  const userAgent = `elasticsearch-js/${clientVersion} (${os.platform()} ${os.release()}-${os.arch()}; Node.js ${process.version})`
39
+ const MAX_BUFFER_LENGTH = buffer.constants.MAX_LENGTH
40
+ const MAX_STRING_LENGTH = buffer.constants.MAX_STRING_LENGTH
38
41
 
39
42
  class Transport {
40
43
  constructor (opts) {
41
44
  if (typeof opts.compression === 'string' && opts.compression !== 'gzip') {
42
45
  throw new ConfigurationError(`Invalid compression: '${opts.compression}'`)
43
46
  }
47
+
44
48
  this.emit = opts.emit
45
49
  this.connectionPool = opts.connectionPool
46
50
  this.serializer = opts.serializer
@@ -87,7 +91,7 @@ class Transport {
87
91
  callback = options
88
92
  options = {}
89
93
  }
90
- var p = null
94
+ let p = null
91
95
 
92
96
  // promises support
93
97
  if (callback === undefined) {
@@ -143,9 +147,10 @@ class Transport {
143
147
  // the size of the stream, we risk to take too much memory.
144
148
  // Furthermore, copying everytime the stream is very a expensive operation.
145
149
  const maxRetries = isStream(params.body) || isStream(params.bulkBody)
146
- ? 0 : (typeof options.maxRetries === 'number' ? options.maxRetries : this.maxRetries)
150
+ ? 0
151
+ : (typeof options.maxRetries === 'number' ? options.maxRetries : this.maxRetries)
147
152
  const compression = options.compression !== undefined ? options.compression : this.compression
148
- var request = { abort: noop }
153
+ let request = { abort: noop }
149
154
  const transportReturn = {
150
155
  then (onFulfilled, onRejected) {
151
156
  return p.then(onFulfilled, onRejected)
@@ -158,6 +163,9 @@ class Transport {
158
163
  request.abort()
159
164
  debug('Aborting request', params)
160
165
  return this
166
+ },
167
+ finally (onFinally) {
168
+ return p.finally(onFinally)
161
169
  }
162
170
  }
163
171
 
@@ -218,6 +226,22 @@ class Transport {
218
226
 
219
227
  const contentEncoding = (result.headers['content-encoding'] || '').toLowerCase()
220
228
  const isCompressed = contentEncoding.indexOf('gzip') > -1 || contentEncoding.indexOf('deflate') > -1
229
+
230
+ /* istanbul ignore else */
231
+ if (result.headers['content-length'] !== undefined) {
232
+ const contentLength = Number(result.headers['content-length'])
233
+ if (isCompressed && contentLength > MAX_BUFFER_LENGTH) {
234
+ response.destroy()
235
+ return onConnectionError(
236
+ new RequestAbortedError(`The content length (${contentLength}) is bigger than the maximum allowed buffer (${MAX_BUFFER_LENGTH})`, result)
237
+ )
238
+ } else if (contentLength > MAX_STRING_LENGTH) {
239
+ response.destroy()
240
+ return onConnectionError(
241
+ new RequestAbortedError(`The content length (${contentLength}) is bigger than the maximum allowed string (${MAX_STRING_LENGTH})`, result)
242
+ )
243
+ }
244
+ }
221
245
  // if the response is compressed, we must handle it
222
246
  // as buffer for allowing decompression later
223
247
  let payload = isCompressed ? [] : ''
@@ -249,6 +273,8 @@ class Transport {
249
273
  if (!isCompressed) {
250
274
  response.setEncoding('utf8')
251
275
  }
276
+
277
+ this.emit('deserialization', null, result)
252
278
  response.on('data', onData)
253
279
  response.on('error', onEnd)
254
280
  response.on('end', onEnd)
@@ -280,8 +306,8 @@ class Transport {
280
306
  return callback(err, result)
281
307
  }
282
308
  } else {
283
- // cast to boolean if the request method was HEAD
284
- result.body = isHead === true ? true : payload
309
+ // cast to boolean if the request method was HEAD and there was no error
310
+ result.body = isHead === true && result.statusCode < 400 ? true : payload
285
311
  }
286
312
 
287
313
  // we should ignore the statusCode if the user has configured the `ignore` field with
@@ -321,6 +347,7 @@ class Transport {
321
347
  }
322
348
  }
323
349
 
350
+ this.emit('serialization', null, result)
324
351
  const headers = Object.assign({}, this.headers, lowerCaseHeaders(options.headers))
325
352
 
326
353
  if (options.opaqueId !== undefined) {
@@ -335,6 +362,7 @@ class Transport {
335
362
  try {
336
363
  params.body = this.serializer.serialize(params.body)
337
364
  } catch (err) {
365
+ this.emit('request', err, result)
338
366
  process.nextTick(callback, err, result)
339
367
  return transportReturn
340
368
  }
@@ -350,6 +378,7 @@ class Transport {
350
378
  try {
351
379
  params.body = this.serializer.ndserialize(params.bulkBody)
352
380
  } catch (err) {
381
+ this.emit('request', err, result)
353
382
  process.nextTick(callback, err, result)
354
383
  return transportReturn
355
384
  }
@@ -389,6 +418,7 @@ class Transport {
389
418
  gzip(params.body, (err, buffer) => {
390
419
  /* istanbul ignore next */
391
420
  if (err) {
421
+ this.emit('request', err, result)
392
422
  return callback(err, result)
393
423
  }
394
424
  params.headers['content-encoding'] = compression
@@ -499,7 +529,7 @@ function defaultNodeFilter (node) {
499
529
  }
500
530
 
501
531
  function roundRobinSelector () {
502
- var current = -1
532
+ let current = -1
503
533
  return function _roundRobinSelector (connections) {
504
534
  if (++current >= connections.length) {
505
535
  current = 0
@@ -514,8 +544,8 @@ function randomSelector (connections) {
514
544
  }
515
545
 
516
546
  function generateRequestId () {
517
- var maxInt = 2147483647
518
- var nextReqId = 0
547
+ const maxInt = 2147483647
548
+ let nextReqId = 0
519
549
  return function genReqId (params, options) {
520
550
  return (nextReqId = (nextReqId + 1) & maxInt)
521
551
  }
package/lib/errors.js CHANGED
@@ -90,7 +90,16 @@ class ResponseError extends ElasticsearchClientError {
90
90
  super('Response Error')
91
91
  Error.captureStackTrace(this, ResponseError)
92
92
  this.name = 'ResponseError'
93
- this.message = (meta.body && meta.body.error && meta.body.error.type) || 'Response Error'
93
+ if (meta.body && meta.body.error && meta.body.error.type) {
94
+ if (Array.isArray(meta.body.error.root_cause)) {
95
+ this.message = meta.body.error.type + ': '
96
+ this.message += meta.body.error.root_cause.map(entry => `[${entry.type}] Reason: ${entry.reason}`).join('; ')
97
+ } else {
98
+ this.message = meta.body.error.type
99
+ }
100
+ } else {
101
+ this.message = 'Response Error'
102
+ }
94
103
  this.meta = meta
95
104
  }
96
105
 
@@ -108,6 +117,10 @@ class ResponseError extends ElasticsearchClientError {
108
117
  get headers () {
109
118
  return this.meta.headers
110
119
  }
120
+
121
+ toString () {
122
+ return JSON.stringify(this.meta.body)
123
+ }
111
124
  }
112
125
 
113
126
  class RequestAbortedError extends ElasticsearchClientError {
@@ -128,7 +128,7 @@ class BaseConnectionPool {
128
128
  */
129
129
  empty (callback) {
130
130
  debug('Emptying the connection pool')
131
- var openConnections = this.size
131
+ let openConnections = this.size
132
132
  this.connections.forEach(connection => {
133
133
  connection.close(() => {
134
134
  if (--openConnections === 0) {
@@ -201,7 +201,7 @@ class BaseConnectionPool {
201
201
  const ids = Object.keys(nodes)
202
202
  const hosts = []
203
203
 
204
- for (var i = 0, len = ids.length; i < len; i++) {
204
+ for (let i = 0, len = ids.length; i < len; i++) {
205
205
  const node = nodes[ids[i]]
206
206
  // If there is no protocol in
207
207
  // the `publish_address` new URL will throw
@@ -210,7 +210,7 @@ class BaseConnectionPool {
210
210
  // - hostname/ip:port
211
211
  // if we encounter the second case, we should
212
212
  // use the hostname instead of the ip
213
- var address = node.http.publish_address
213
+ let address = node.http.publish_address
214
214
  const parts = address.split('/')
215
215
  // the url is in the form of hostname/ip:port
216
216
  if (parts.length > 1) {
@@ -80,7 +80,7 @@ class ConnectionPool extends BaseConnectionPool {
80
80
  // list a node that no longer exist. The following check verify
81
81
  // that the connection is still part of the pool before
82
82
  // marking it as dead.
83
- for (var i = 0; i < this.size; i++) {
83
+ for (let i = 0; i < this.size; i++) {
84
84
  if (this.connections[i].id === id) {
85
85
  this.dead.push(id)
86
86
  break
@@ -138,7 +138,7 @@ class ConnectionPool extends BaseConnectionPool {
138
138
  path: '/',
139
139
  timeout: this.pingTimeout
140
140
  }, (err, response) => {
141
- var isAlive = true
141
+ let isAlive = true
142
142
  const statusCode = response !== null ? response.statusCode : 0
143
143
  if (err != null ||
144
144
  (statusCode === 502 || statusCode === 503 || statusCode === 504)) {
@@ -170,8 +170,7 @@ class ConnectionPool extends BaseConnectionPool {
170
170
  isAlive: true,
171
171
  connection
172
172
  })
173
- // eslint-disable-next-line standard/no-callback-literal
174
- callback(true, connection)
173
+ callback(true, connection) // eslint-disable-line
175
174
  }
176
175
  }
177
176
 
@@ -199,7 +198,7 @@ class ConnectionPool extends BaseConnectionPool {
199
198
 
200
199
  // TODO: can we cache this?
201
200
  const connections = []
202
- for (var i = 0; i < this.size; i++) {
201
+ for (let i = 0; i < this.size; i++) {
203
202
  const connection = this.connections[i]
204
203
  if (noAliveConnections || connection.status === Connection.statuses.ALIVE) {
205
204
  if (filter(connection) === true) {