Compare commits

..

26 Commits
v7.15.0 ... 7.x

Author SHA1 Message Date
e20ddc7e6d fix: add types to exports (v7.x) (#1930) 2023-07-10 16:07:23 -05:00
9ff5fc34bc Hard code jsclient links to 7.x (#1580) 2021-10-20 20:43:56 -04:00
1ad8501d73 [DOCS] Update branch attribute to fix doc links (#1577)
Co-authored-by: debadair <debadair@elastic.co>
2021-10-20 19:55:54 -04:00
158ea917e2 [DOCS] Hardcode fix for broken link to unblock doc build (#1575) 2021-10-20 18:14:22 -04:00
26887ca76b [DOCS] Retitle Elasticsearch JavaScript Client doc book (#1565) 2021-10-13 18:52:05 +02:00
53ba8ba726 Bumped v7.16.0-canary.4 2021-09-29 07:25:25 +02:00
8ad6d993e6 Fix broken link 2021-09-23 15:31:02 +02:00
fb363fe5c3 Changelog for 7.15 (#1557) 2021-09-23 14:49:18 +02:00
1e7ccfab33 [Backport 7.x] Show socket local/remote address in case of ECONNRESET (#1556)
Co-authored-by: Tomas Della Vedova <delvedor@users.noreply.github.com>
2021-09-22 10:06:50 +02:00
1362fb568e Bumped v7.16.0-canary.3 2021-09-16 08:43:47 +02:00
34a168dd99 [Backport 7.x] Update compatibility info (#1554)
Co-authored-by: Tomas Della Vedova <delvedor@users.noreply.github.com>
2021-09-16 08:21:36 +02:00
be1c0f235c [Backport 7.x] Add support for maxResponseSize and maxCompressedResponseSize (#1553)
Co-authored-by: Tomas Della Vedova <delvedor@users.noreply.github.com>
2021-09-16 08:20:33 +02:00
da0bfd2fb5 [DOCS] Adds a link to the Quick Start section that points to an EC ingest example (#1546) 2021-09-07 10:01:39 +02:00
9daf8f360c Merge branch '7.x' of https://github.com/elastic/elasticsearch-js into 7.x 2021-08-30 15:37:51 +02:00
f6180fae6b Add test for mocking library (#1545) 2021-08-30 15:37:43 +02:00
996ee29009 Bumped v7.16.0-canary.2 2021-08-28 18:24:55 +02:00
bb8f397742 Do not use a singleton for EE (#1543) (#1544)
Co-authored-by: Tomas Della Vedova <delvedor@users.noreply.github.com>
2021-08-28 18:22:26 +02:00
7c69f043e5 [Backport 7.x] Documentation Update for FaaS use cases (#1541)
Co-authored-by: Tomas Della Vedova <delvedor@users.noreply.github.com>
2021-08-24 14:55:18 +02:00
93a7e52be0 [Backport 7.x] Always emit request aborted event (#1539)
Co-authored-by: Tomas Della Vedova <delvedor@users.noreply.github.com>
2021-08-24 11:56:15 +02:00
642b15cd5c [Backport 7.x] Always display request params and options in request event (#1538)
Co-authored-by: Tomas Della Vedova <delvedor@users.noreply.github.com>
2021-08-24 11:27:59 +02:00
ee867daad0 [Backport 7.x] Fix parcel build (#1536)
Co-authored-by: Tomas Della Vedova <delvedor@users.noreply.github.com>
2021-08-23 15:31:01 +02:00
ef11962ad4 Bumped v7.16.0-canary.1 2021-08-20 12:42:15 +02:00
65fabb267d [Backport 7.x] Fix isHttpConnection check (#1528)
Co-authored-by: Tomas Della Vedova <delvedor@users.noreply.github.com>
2021-08-20 12:41:00 +02:00
c520967790 [Backport 7.x] Add warning.name to product check security exception (#1527)
Co-authored-by: Tomas Della Vedova <delvedor@users.noreply.github.com>
2021-08-20 10:40:19 +02:00
f555329496 API generation 2021-08-18 17:51:19 +02:00
c55b287c63 Bumped v7.16.0 2021-08-18 17:50:32 +02:00
23 changed files with 594 additions and 66 deletions

View File

@ -1,6 +1,6 @@
---
STACK_VERSION:
- 7.15.0-SNAPSHOT
- 7.x-SNAPSHOT
NODE_JS_VERSION:
- 16

View File

@ -61,7 +61,7 @@ jobs:
- name: Runs Elasticsearch
uses: elastic/elastic-github-actions/elasticsearch@master
with:
stack-version: 7.15.0-SNAPSHOT
stack-version: 7.x-SNAPSHOT
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
@ -93,7 +93,7 @@ jobs:
- name: Runs Elasticsearch
uses: elastic/elastic-github-actions/elasticsearch@master
with:
stack-version: 7.15.0-SNAPSHOT
stack-version: 8.0.0-SNAPSHOT
- name: Use Node.js 14.x
uses: actions/setup-node@v1
@ -119,6 +119,27 @@ jobs:
npm start --prefix test/bundlers/rollup-test
npm start --prefix test/bundlers/webpack-test
mock-support:
name: Mock support
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Use Node.js 14.x
uses: actions/setup-node@v1
with:
node-version: 14.x
- name: Install
run: |
npm install
npm install --prefix test/mock
- name: Run test
run: |
npm test --prefix test/mock
code-coverage:
name: Code coverage
runs-on: ubuntu-latest

View File

@ -28,7 +28,7 @@ npm install @elastic/elasticsearch
### Node.js support
NOTE: The minimum supported version of Node.js is `v10`.
NOTE: The minimum supported version of Node.js is `v12`.
The client versioning follows the Elastc Stack versioning, this means that
major, minor, and patch releases are done following a precise schedule that
@ -49,13 +49,13 @@ of `^7.10.0`).
| Node.js Version | Node.js EOL date | End of support |
| --------------- |------------------| ---------------------- |
| `8.x` | `December 2019` | `7.11` (early 2021) |
| `10.x` | `Apri 2021` | `7.12` (mid 2021) |
| `8.x` | `December 2019` | `7.11` (early 2021) |
| `10.x` | `April 2021` | `7.12` (mid 2021) |
### Compatibility
Language clients are forward compatible; meaning that clients support communicating with greater minor versions of Elasticsearch.
Elastic language clients are also backwards compatible with lesser supported minor Elasticsearch versions.
Language clients are forward compatible; meaning that clients support communicating with greater or equal minor versions of Elasticsearch.
Elasticsearch language clients are only backwards compatible with default distributions and without guarantees made.
| Elasticsearch Version | Client Version |
| --------------------- |----------------|

View File

@ -31,7 +31,7 @@ function NodesApi (transport, ConfigurationError) {
this[kConfigurationError] = ConfigurationError
}
NodesApi.prototype.clearRepositoriesMeteringArchive = function nodesClearRepositoriesMeteringArchiveApi (params, options, callback) {
NodesApi.prototype.clearMeteringArchive = function nodesClearMeteringArchiveApi (params, options, callback) {
;[params, options, callback] = normalizeArguments(params, options, callback)
// check required parameters
@ -68,7 +68,7 @@ NodesApi.prototype.clearRepositoriesMeteringArchive = function nodesClearReposit
return this.transport.request(request, options, callback)
}
NodesApi.prototype.getRepositoriesMeteringInfo = function nodesGetRepositoriesMeteringInfoApi (params, options, callback) {
NodesApi.prototype.getMeteringInfo = function nodesGetMeteringInfoApi (params, options, callback) {
;[params, options, callback] = normalizeArguments(params, options, callback)
// check required parameters
@ -259,8 +259,8 @@ NodesApi.prototype.usage = function nodesUsageApi (params, options, callback) {
}
Object.defineProperties(NodesApi.prototype, {
clear_repositories_metering_archive: { get () { return this.clearRepositoriesMeteringArchive } },
get_repositories_metering_info: { get () { return this.getRepositoriesMeteringInfo } },
clear_metering_archive: { get () { return this.clearMeteringArchive } },
get_metering_info: { get () { return this.getMeteringInfo } },
hot_threads: { get () { return this.hotThreads } },
reload_secure_settings: { get () { return this.reloadSecureSettings } }
})

View File

@ -29,18 +29,17 @@ const snakeCase = { ignoreUnavailable: 'ignore_unavailable', expandWildcards: 'e
function openPointInTimeApi (params, options, callback) {
;[params, options, callback] = normalizeArguments(params, options, callback)
// check required parameters
if (params.index == null) {
const err = new this[kConfigurationError]('Missing required parameter: index')
return handleError(err, callback)
}
let { method, body, index, ...querystring } = params
querystring = snakeCaseKeys(acceptedQuerystring, snakeCase, querystring)
let path = ''
if (method == null) method = 'POST'
path = '/' + encodeURIComponent(index) + '/' + '_pit'
if ((index) != null) {
if (method == null) method = 'POST'
path = '/' + encodeURIComponent(index) + '/' + '_pit'
} else {
if (method == null) method = 'POST'
path = '/' + '_pit'
}
// build request object
const request = {

View File

@ -2025,12 +2025,12 @@ export interface Mtermvectors<T = RequestBody> extends Generic {
body?: T;
}
export interface NodesClearRepositoriesMeteringArchive extends Generic {
export interface NodesClearMeteringArchive extends Generic {
node_id: string | string[];
max_archive_version: number;
}
export interface NodesGetRepositoriesMeteringInfo extends Generic {
export interface NodesGetMeteringInfo extends Generic {
node_id: string | string[];
}
@ -2079,7 +2079,7 @@ export interface NodesUsage extends Generic {
}
export interface OpenPointInTime extends Generic {
index: string | string[];
index?: string | string[];
preference?: string;
routing?: string;
ignore_unavailable?: boolean;

View File

@ -259,6 +259,14 @@ _Default:_ `false`
|`string` - If configured, verify that the fingerprint of the CA certificate that has signed the certificate of the server matches the supplied fingerprint. Only accepts SHA256 digest fingerprints. +
_Default:_ `null`
|`maxResponseSize`
|`number` - When configured, it verifies that the uncompressed response size is lower than the configured number, if it's higher it will abort the request. It cannot be higher than buffer.constants.MAX_STRING_LENTGH +
_Default:_ `null`
|`maxCompressedResponseSize`
|`number` - When configured, it verifies that the compressed response size is lower than the configured number, if it's higher it will abort the request. It cannot be higher than buffer.constants.MAX_LENTGH +
_Default:_ `null`
|===
[discrete]

View File

@ -1,6 +1,60 @@
[[changelog-client]]
== Release notes
[discrete]
=== 7.15.0
[discrete]
==== Features
[discrete]
===== Support for Elasticsearch `v7.15`
You can find all the API changes
https://www.elastic.co/guide/en/elasticsearch/reference/7.15/release-notes-7.15.0.html[here].
[discrete]
===== Support mapbox content type https://github.com/elastic/elasticsearch-js/pull/1500[#1500]
If you call an API that returns a mapbox conten type, the response body will be a buffer.
[discrete]
===== Support CA fingerprint validation https://github.com/elastic/elasticsearch-js/pull/1499[#1499]
You can configure the client to only trust certificates that are signed by a specific CA certificate ( CA certificate pinning ) by providing a `caFingerprint` option. This will verify that the fingerprint of the CA certificate that has signed the certificate of the server matches the supplied value.
a `caFingerprint` option, which will verify the supplied certificate authority fingerprint.
You must configure a SHA256 digest.
[source,js]
----
const { Client } = require('@elastic/elasticsearch')
const client = new Client({
node: 'https://example.com'
auth: { ... },
// the fingerprint (SHA256) of the CA certificate that is used to sign the certificate that the Elasticsearch node presents for TLS.
caFingerprint: '20:0D:CA:FA:76:...',
ssl: {
// might be required if it's a self-signed certificate
rejectUnauthorized: false
}
})
----
[discrete]
===== Show the body as string if the response error can't be read as ES error https://github.com/elastic/elasticsearch-js/pull/1509[#1509]
Useful if the errored response does not come from Elasticsearch, but a proxy in the middle for example.
[discrete]
===== Always display request params and options in request event https://github.com/elastic/elasticsearch-js/pull/1531[#1531]
In some edge cases the params and options weren't available in observabilty events, now they are always defined.
[discrete]
===== Always emit request aborted event https://github.com/elastic/elasticsearch-js/pull/1534[#1534]
If the client is busy running an async operation, the `.abort()` call might be executed before sending the actual request. In such case, the error was swallowed, now it will always be emitted, either in the `request` or `response` event.
[discrete]
=== 7.14.0

View File

@ -8,6 +8,7 @@ This page contains the information you need to connect and use the Client with
* <<auth-reference, Authentication options>>
* <<client-usage, Using the client>>
* <<client-faas-env, Using the Client in a Function-as-a-Service Environment>>
* <<client-connect-proxy, Connecting through a proxy>>
* <<client-error-handling, Handling errors>>
* <<product-check, Automatic product check>>
@ -60,11 +61,12 @@ const client = new Client({
==== ApiKey authentication
You can use the
https://www.elastic.co/guide/en/elasticsearch/reference/7.x/security-api-create-api-key.html[ApiKey]
{ref-7x}/security-api-create-api-key.html[ApiKey]
authentication by passing the `apiKey` parameter via the `auth` option. The
`apiKey` parameter can be either a base64 encoded string or an object with the
values that you can obtain from the
https://www.elastic.co/guide/en/elasticsearch/reference/7.x/security-api-create-api-key.html[create api key endpoint].
{ref-7x}/security-api-create-api-key.html[create api key endpoint].
NOTE: If you provide both basic authentication credentials and the ApiKey
configuration, the ApiKey takes precedence.
@ -417,8 +419,87 @@ _Default:_ `null`
|`context`
|`any` - Custom object per request. _(you can use it to pass data to the clients events)_ +
_Default:_ `null`
|`maxResponseSize`
|`number` - When configured, it verifies that the uncompressed response size is lower than the configured number, if it's higher it will abort the request. It cannot be higher than buffer.constants.MAX_STRING_LENTGH +
_Default:_ `null`
|`maxCompressedResponseSize`
|`number` - When configured, it verifies that the compressed response size is lower than the configured number, if it's higher it will abort the request. It cannot be higher than buffer.constants.MAX_LENTGH +
_Default:_ `null`
|===
[discrete]
[[client-faas-env]]
=== Using the Client in a Function-as-a-Service Environment
This section illustrates the best practices for leveraging the {es} client in a Function-as-a-Service (FaaS) environment.
The most influential optimization is to initialize the client outside of the function, the global scope.
This practice does not only improve performance but also enables background functionality as for example https://www.elastic.co/blog/elasticsearch-sniffing-best-practices-what-when-why-how[sniffing].
The following examples provide a skeleton for the best practices.
[discrete]
==== GCP Cloud Functions
[source,js]
----
'use strict'
const { Client } = require('@elastic/elasticsearch')
const client = new Client({
// client initialisation
})
exports.testFunction = async function (req, res) {
// use the client
}
----
[discrete]
==== AWS Lambda
[source,js]
----
'use strict'
const { Client } = require('@elastic/elasticsearch')
const client = new Client({
// client initialisation
})
exports.handler = async function (event, context) {
// use the client
}
----
[discrete]
==== Azure Functions
[source,js]
----
'use strict'
const { Client } = require('@elastic/elasticsearch')
const client = new Client({
// client initialisation
})
module.exports = async function (context, req) {
// use the client
}
----
Resources used to assess these recommendations:
- https://cloud.google.com/functions/docs/bestpractices/tips#use_global_variables_to_reuse_objects_in_future_invocations[GCP Cloud Functions: Tips & Tricks]
- https://docs.aws.amazon.com/lambda/latest/dg/best-practices.html[Best practices for working with AWS Lambda functions]
- https://docs.microsoft.com/en-us/azure/azure-functions/functions-reference-python?tabs=azurecli-linux%2Capplication-level#global-variables[Azure Functions Python developer guide]
- https://docs.aws.amazon.com/lambda/latest/operatorguide/global-scope.html[AWS Lambda: Comparing the effect of global scope]
[discrete]
[[client-connect-proxy]]

View File

@ -1,8 +1,13 @@
= Elasticsearch Node.js client
= Elasticsearch JavaScript Client
:branch: 7.x
:branch: 7.16
include::{asciidoc-dir}/../../shared/attributes.asciidoc[]
// 7.x exists in this repo but not in stack repos
// This line overwrites the jsclient attribute so it can point to 7.x, but stack links can point to 7.16
// Remove this line when a 7.16 branch exists in this repo
:jsclient: https://www.elastic.co/guide/en/elasticsearch/client/javascript-api/7.x
include::introduction.asciidoc[]
include::installation.asciidoc[]
include::connecting.asciidoc[]

View File

@ -24,7 +24,7 @@ To learn more about the supported major versions, please refer to the
[[nodejs-support]]
=== Node.js support
NOTE: The minimum supported version of Node.js is `v10`.
NOTE: The minimum supported version of Node.js is `v12`.
The client versioning follows the {stack} versioning, this means that
major, minor, and patch releases are done following a precise schedule that
@ -62,12 +62,8 @@ of `^7.10.0`).
[[js-compatibility-matrix]]
=== Compatibility matrix
Elastic language clients are guaranteed to be able to communicate with Elasticsearch
or Elastic solutions running on the same major version and greater or equal minor version.
Language clients are forward compatible; meaning that clients support communicating
with greater minor versions of Elasticsearch. Elastic language clients are not
guaranteed to be backwards compatible.
Language clients are forward compatible; meaning that clients support communicating with greater or equal minor versions of Elasticsearch.
Elasticsearch language clients are only backwards compatible with default distributions and without guarantees made.
[%header,cols=2*]
|===

View File

@ -132,6 +132,9 @@ async function run () {
run().catch(console.log)
----
TIP: For an elaborate example of how to ingest data into Elastic Cloud,
refer to {cloud}/ec-getting-started-node-js.html[this page].
[discrete]
==== Install multiple versions

View File

@ -4132,8 +4132,8 @@ link:{ref}/indices-delete-index.html[Documentation] +
|`boolean` - Ignore if a wildcard expression resolves to no concrete indices (default: false)
|`expand_wildcards` or `expandWildcards`
|`'open' \| 'closed' \| 'hidden' \| 'none' \| 'all'` - Whether wildcard expressions should get expanded to open, closed, or hidden indices +
_Default:_ `open,closed`
|`'open' \| 'closed' \| 'hidden' \| 'none' \| 'all'` - Whether wildcard expressions should get expanded to open or closed indices (default: open) +
_Default:_ `open`
|===
@ -8369,11 +8369,11 @@ _Default:_ `true`
|===
[discrete]
=== nodes.clearRepositoriesMeteringArchive
=== nodes.clearMeteringArchive
*Stability:* experimental
[source,ts]
----
client.nodes.clearRepositoriesMeteringArchive({
client.nodes.clearMeteringArchive({
node_id: string | string[],
max_archive_version: number
})
@ -8390,11 +8390,11 @@ link:{ref}/clear-repositories-metering-archive-api.html[Documentation] +
|===
[discrete]
=== nodes.getRepositoriesMeteringInfo
=== nodes.getMeteringInfo
*Stability:* experimental
[source,ts]
----
client.nodes.getRepositoriesMeteringInfo({
client.nodes.getMeteringInfo({
node_id: string | string[]
})
----

34
index.d.ts vendored
View File

@ -119,6 +119,8 @@ interface ClientOptions {
};
disablePrototypePoisoningProtection?: boolean | 'proto' | 'constructor';
caFingerprint?: string;
maxResponseSize?: number;
maxCompressedResponseSize?: number;
}
declare class Client {
@ -1926,22 +1928,22 @@ declare class Client {
mtermvectors<TResponse = Record<string, any>, TRequestBody extends RequestBody = Record<string, any>, TContext = Context>(params: RequestParams.Mtermvectors<TRequestBody>, callback: callbackFn<TResponse, TContext>): TransportRequestCallback
mtermvectors<TResponse = Record<string, any>, TRequestBody extends RequestBody = Record<string, any>, TContext = Context>(params: RequestParams.Mtermvectors<TRequestBody>, options: TransportRequestOptions, callback: callbackFn<TResponse, TContext>): TransportRequestCallback
nodes: {
clear_repositories_metering_archive<TResponse = Record<string, any>, TContext = Context>(params?: RequestParams.NodesClearRepositoriesMeteringArchive, options?: TransportRequestOptions): TransportRequestPromise<ApiResponse<TResponse, TContext>>
clear_repositories_metering_archive<TResponse = Record<string, any>, TContext = Context>(callback: callbackFn<TResponse, TContext>): TransportRequestCallback
clear_repositories_metering_archive<TResponse = Record<string, any>, TContext = Context>(params: RequestParams.NodesClearRepositoriesMeteringArchive, callback: callbackFn<TResponse, TContext>): TransportRequestCallback
clear_repositories_metering_archive<TResponse = Record<string, any>, TContext = Context>(params: RequestParams.NodesClearRepositoriesMeteringArchive, options: TransportRequestOptions, callback: callbackFn<TResponse, TContext>): TransportRequestCallback
clearRepositoriesMeteringArchive<TResponse = Record<string, any>, TContext = Context>(params?: RequestParams.NodesClearRepositoriesMeteringArchive, options?: TransportRequestOptions): TransportRequestPromise<ApiResponse<TResponse, TContext>>
clearRepositoriesMeteringArchive<TResponse = Record<string, any>, TContext = Context>(callback: callbackFn<TResponse, TContext>): TransportRequestCallback
clearRepositoriesMeteringArchive<TResponse = Record<string, any>, TContext = Context>(params: RequestParams.NodesClearRepositoriesMeteringArchive, callback: callbackFn<TResponse, TContext>): TransportRequestCallback
clearRepositoriesMeteringArchive<TResponse = Record<string, any>, TContext = Context>(params: RequestParams.NodesClearRepositoriesMeteringArchive, options: TransportRequestOptions, callback: callbackFn<TResponse, TContext>): TransportRequestCallback
get_repositories_metering_info<TResponse = Record<string, any>, TContext = Context>(params?: RequestParams.NodesGetRepositoriesMeteringInfo, options?: TransportRequestOptions): TransportRequestPromise<ApiResponse<TResponse, TContext>>
get_repositories_metering_info<TResponse = Record<string, any>, TContext = Context>(callback: callbackFn<TResponse, TContext>): TransportRequestCallback
get_repositories_metering_info<TResponse = Record<string, any>, TContext = Context>(params: RequestParams.NodesGetRepositoriesMeteringInfo, callback: callbackFn<TResponse, TContext>): TransportRequestCallback
get_repositories_metering_info<TResponse = Record<string, any>, TContext = Context>(params: RequestParams.NodesGetRepositoriesMeteringInfo, options: TransportRequestOptions, callback: callbackFn<TResponse, TContext>): TransportRequestCallback
getRepositoriesMeteringInfo<TResponse = Record<string, any>, TContext = Context>(params?: RequestParams.NodesGetRepositoriesMeteringInfo, options?: TransportRequestOptions): TransportRequestPromise<ApiResponse<TResponse, TContext>>
getRepositoriesMeteringInfo<TResponse = Record<string, any>, TContext = Context>(callback: callbackFn<TResponse, TContext>): TransportRequestCallback
getRepositoriesMeteringInfo<TResponse = Record<string, any>, TContext = Context>(params: RequestParams.NodesGetRepositoriesMeteringInfo, callback: callbackFn<TResponse, TContext>): TransportRequestCallback
getRepositoriesMeteringInfo<TResponse = Record<string, any>, TContext = Context>(params: RequestParams.NodesGetRepositoriesMeteringInfo, options: TransportRequestOptions, callback: callbackFn<TResponse, TContext>): TransportRequestCallback
clear_metering_archive<TResponse = Record<string, any>, TContext = Context>(params?: RequestParams.NodesClearMeteringArchive, options?: TransportRequestOptions): TransportRequestPromise<ApiResponse<TResponse, TContext>>
clear_metering_archive<TResponse = Record<string, any>, TContext = Context>(callback: callbackFn<TResponse, TContext>): TransportRequestCallback
clear_metering_archive<TResponse = Record<string, any>, TContext = Context>(params: RequestParams.NodesClearMeteringArchive, callback: callbackFn<TResponse, TContext>): TransportRequestCallback
clear_metering_archive<TResponse = Record<string, any>, TContext = Context>(params: RequestParams.NodesClearMeteringArchive, options: TransportRequestOptions, callback: callbackFn<TResponse, TContext>): TransportRequestCallback
clearMeteringArchive<TResponse = Record<string, any>, TContext = Context>(params?: RequestParams.NodesClearMeteringArchive, options?: TransportRequestOptions): TransportRequestPromise<ApiResponse<TResponse, TContext>>
clearMeteringArchive<TResponse = Record<string, any>, TContext = Context>(callback: callbackFn<TResponse, TContext>): TransportRequestCallback
clearMeteringArchive<TResponse = Record<string, any>, TContext = Context>(params: RequestParams.NodesClearMeteringArchive, callback: callbackFn<TResponse, TContext>): TransportRequestCallback
clearMeteringArchive<TResponse = Record<string, any>, TContext = Context>(params: RequestParams.NodesClearMeteringArchive, options: TransportRequestOptions, callback: callbackFn<TResponse, TContext>): TransportRequestCallback
get_metering_info<TResponse = Record<string, any>, TContext = Context>(params?: RequestParams.NodesGetMeteringInfo, options?: TransportRequestOptions): TransportRequestPromise<ApiResponse<TResponse, TContext>>
get_metering_info<TResponse = Record<string, any>, TContext = Context>(callback: callbackFn<TResponse, TContext>): TransportRequestCallback
get_metering_info<TResponse = Record<string, any>, TContext = Context>(params: RequestParams.NodesGetMeteringInfo, callback: callbackFn<TResponse, TContext>): TransportRequestCallback
get_metering_info<TResponse = Record<string, any>, TContext = Context>(params: RequestParams.NodesGetMeteringInfo, options: TransportRequestOptions, callback: callbackFn<TResponse, TContext>): TransportRequestCallback
getMeteringInfo<TResponse = Record<string, any>, TContext = Context>(params?: RequestParams.NodesGetMeteringInfo, options?: TransportRequestOptions): TransportRequestPromise<ApiResponse<TResponse, TContext>>
getMeteringInfo<TResponse = Record<string, any>, TContext = Context>(callback: callbackFn<TResponse, TContext>): TransportRequestCallback
getMeteringInfo<TResponse = Record<string, any>, TContext = Context>(params: RequestParams.NodesGetMeteringInfo, callback: callbackFn<TResponse, TContext>): TransportRequestCallback
getMeteringInfo<TResponse = Record<string, any>, TContext = Context>(params: RequestParams.NodesGetMeteringInfo, options: TransportRequestOptions, callback: callbackFn<TResponse, TContext>): TransportRequestCallback
hot_threads<TResponse = Record<string, any>, TContext = Context>(params?: RequestParams.NodesHotThreads, options?: TransportRequestOptions): TransportRequestPromise<ApiResponse<TResponse, TContext>>
hot_threads<TResponse = Record<string, any>, TContext = Context>(callback: callbackFn<TResponse, TContext>): TransportRequestCallback
hot_threads<TResponse = Record<string, any>, TContext = Context>(params: RequestParams.NodesHotThreads, callback: callbackFn<TResponse, TContext>): TransportRequestCallback

View File

@ -21,6 +21,7 @@
const { EventEmitter } = require('events')
const { URL } = require('url')
const buffer = require('buffer')
const debug = require('debug')('elasticsearch')
const Transport = require('./lib/Transport')
const Connection = require('./lib/Connection')
@ -114,9 +115,19 @@ class Client extends ESAPI {
context: null,
proxy: null,
enableMetaHeader: true,
disablePrototypePoisoningProtection: false
disablePrototypePoisoningProtection: false,
maxResponseSize: null,
maxCompressedResponseSize: null
}, opts)
if (options.maxResponseSize !== null && options.maxResponseSize > buffer.constants.MAX_STRING_LENGTH) {
throw new ConfigurationError(`The maxResponseSize cannot be bigger than ${buffer.constants.MAX_STRING_LENGTH}`)
}
if (options.maxCompressedResponseSize !== null && options.maxCompressedResponseSize > buffer.constants.MAX_LENGTH) {
throw new ConfigurationError(`The maxCompressedResponseSize cannot be bigger than ${buffer.constants.MAX_LENGTH}`)
}
if (options.caFingerprint !== null && isHttpConnection(opts.node || opts.nodes)) {
throw new ConfigurationError('You can\'t configure the caFingerprint with a http connection')
}
@ -178,7 +189,9 @@ class Client extends ESAPI {
generateRequestId: options.generateRequestId,
name: options.name,
opaqueIdPrefix: options.opaqueIdPrefix,
context: options.context
context: options.context,
maxResponseSize: options.maxResponseSize,
maxCompressedResponseSize: options.maxCompressedResponseSize
})
this.helpers = new Helpers({

View File

@ -113,7 +113,14 @@ class Connection {
const onError = err => {
cleanListeners()
this._openRequests--
callback(new ConnectionError(err.message), null)
let message = err.message
if (err.code === 'ECONNRESET') {
/* istanbul ignore next */
const socket = request.socket || {}
/* istanbul ignore next */
message += ` - Local: ${socket.localAddress || 'unknown'}:${socket.localPort || 'unknown'}, Remote: ${socket.remoteAddress || 'unknown'}:${socket.remotePort || 'unknown'}`
}
callback(new ConnectionError(message), null)
}
const onAbort = () => {

4
lib/Transport.d.ts vendored
View File

@ -61,6 +61,8 @@ interface TransportOptions {
generateRequestId?: generateRequestIdFn;
name?: string;
opaqueIdPrefix?: string;
maxResponseSize?: number;
maxCompressedResponseSize?: number;
}
export interface RequestEvent<TResponse = Record<string, any>, TContext = Context> {
@ -113,6 +115,8 @@ export interface TransportRequestOptions {
context?: Context;
warnings?: string[];
opaqueId?: string;
maxResponseSize?: number;
maxCompressedResponseSize?: number;
}
export interface TransportRequestCallback {

View File

@ -43,6 +43,8 @@ const MAX_STRING_LENGTH = buffer.constants.MAX_STRING_LENGTH
const kProductCheck = Symbol('product check')
const kApiVersioning = Symbol('api versioning')
const kEventEmitter = Symbol('event emitter')
const kMaxResponseSize = Symbol('max response size')
const kMaxCompressedResponseSize = Symbol('max compressed response size')
class Transport {
constructor (opts) {
@ -72,6 +74,8 @@ class Transport {
this[kProductCheck] = 0 // 0 = to be checked, 1 = checking, 2 = checked-ok, 3 checked-notok, 4 checked-nodefault
this[kApiVersioning] = process.env.ELASTIC_CLIENT_APIVERSIONING === 'true'
this[kEventEmitter] = new EventEmitter()
this[kMaxResponseSize] = opts.maxResponseSize || MAX_STRING_LENGTH
this[kMaxCompressedResponseSize] = opts.maxCompressedResponseSize || MAX_BUFFER_LENGTH
this.nodeFilter = opts.nodeFilter || defaultNodeFilter
if (typeof opts.nodeSelector === 'function') {
@ -162,6 +166,8 @@ class Transport {
? 0
: (typeof options.maxRetries === 'number' ? options.maxRetries : this.maxRetries)
const compression = options.compression !== undefined ? options.compression : this.compression
const maxResponseSize = options.maxResponseSize || this[kMaxResponseSize]
const maxCompressedResponseSize = options.maxCompressedResponseSize || this[kMaxCompressedResponseSize]
let request = { abort: noop }
const transportReturn = {
then (onFulfilled, onRejected) {
@ -244,15 +250,15 @@ class Transport {
/* istanbul ignore else */
if (result.headers['content-length'] !== undefined) {
const contentLength = Number(result.headers['content-length'])
if (isCompressed && contentLength > MAX_BUFFER_LENGTH) {
if (isCompressed && contentLength > maxCompressedResponseSize) {
response.destroy()
return onConnectionError(
new RequestAbortedError(`The content length (${contentLength}) is bigger than the maximum allowed buffer (${MAX_BUFFER_LENGTH})`, result)
new RequestAbortedError(`The content length (${contentLength}) is bigger than the maximum allowed buffer (${maxCompressedResponseSize})`, result)
)
} else if (contentLength > MAX_STRING_LENGTH) {
} else if (contentLength > maxResponseSize) {
response.destroy()
return onConnectionError(
new RequestAbortedError(`The content length (${contentLength}) is bigger than the maximum allowed string (${MAX_STRING_LENGTH})`, result)
new RequestAbortedError(`The content length (${contentLength}) is bigger than the maximum allowed string (${maxResponseSize})`, result)
)
}
}

View File

@ -6,13 +6,14 @@
"exports": {
".": {
"require": "./index.js",
"import": "./index.mjs"
"import": "./index.mjs",
"types": "./index.d.ts"
},
"./": "./"
},
"homepage": "http://www.elastic.co/guide/en/elasticsearch/client/javascript-api/current/index.html",
"version": "7.15.0",
"versionCanary": "7.15.0-canary.3",
"version": "7.16.0",
"versionCanary": "7.16.0-canary.4",
"keywords": [
"elasticsearch",
"elastic",

71
test/mock/index.js Normal file
View File

@ -0,0 +1,71 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
'use strict'
const { test } = require('tap')
const { Client, errors } = require('../../')
const Mock = require('@elastic/elasticsearch-mock')
test('Mock should work', async t => {
t.plan(1)
const mock = new Mock()
const client = new Client({
node: 'http://localhost:9200',
Connection: mock.getConnection()
})
mock.add({
method: 'GET',
path: '/_cat/indices'
}, () => {
return { status: 'ok' }
})
const response = await client.cat.indices()
t.same(response.body, { status: 'ok' })
})
test('Return an error', async t => {
t.plan(1)
const mock = new Mock()
const client = new Client({
node: 'http://localhost:9200',
Connection: mock.getConnection()
})
mock.add({
method: 'GET',
path: '/_cat/indices'
}, () => {
return new errors.ResponseError({
body: { errors: {}, status: 500 },
statusCode: 500
})
})
try {
await client.cat.indices()
t.fail('Should throw')
} catch (err) {
t.ok(err instanceof errors.ResponseError)
}
})

18
test/mock/package.json Normal file
View File

@ -0,0 +1,18 @@
{
"name": "mock",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "standard && tap index.js"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"@elastic/elasticsearch": "file:../..",
"@elastic/elasticsearch-mock": "^0.3.1",
"standard": "^16.0.3",
"tap": "^15.0.9"
}
}

View File

@ -1308,6 +1308,223 @@ test('Content length too big (string)', t => {
})
})
test('Content length too big custom (buffer)', t => {
t.plan(4)
class MockConnection extends Connection {
request (params, callback) {
const stream = intoStream(JSON.stringify({ hello: 'world' }))
stream.statusCode = 200
stream.headers = {
'content-type': 'application/json;utf=8',
'content-encoding': 'gzip',
'content-length': 1100,
connection: 'keep-alive',
date: new Date().toISOString()
}
stream.on('close', () => t.pass('Stream destroyed'))
process.nextTick(callback, null, stream)
return { abort () {} }
}
}
const client = new Client({
node: 'http://localhost:9200',
Connection: MockConnection,
maxCompressedResponseSize: 1000
})
client.info((err, result) => {
t.ok(err instanceof errors.RequestAbortedError)
t.equal(err.message, 'The content length (1100) is bigger than the maximum allowed buffer (1000)')
t.equal(result.meta.attempts, 0)
})
})
test('Content length too big custom (string)', t => {
t.plan(4)
class MockConnection extends Connection {
request (params, callback) {
const stream = intoStream(JSON.stringify({ hello: 'world' }))
stream.statusCode = 200
stream.headers = {
'content-type': 'application/json;utf=8',
'content-length': 1100,
connection: 'keep-alive',
date: new Date().toISOString()
}
stream.on('close', () => t.pass('Stream destroyed'))
process.nextTick(callback, null, stream)
return { abort () {} }
}
}
const client = new Client({
node: 'http://localhost:9200',
Connection: MockConnection,
maxResponseSize: 1000
})
client.info((err, result) => {
t.ok(err instanceof errors.RequestAbortedError)
t.equal(err.message, 'The content length (1100) is bigger than the maximum allowed string (1000)')
t.equal(result.meta.attempts, 0)
})
})
test('Content length too big custom option (buffer)', t => {
t.plan(4)
class MockConnection extends Connection {
request (params, callback) {
const stream = intoStream(JSON.stringify({ hello: 'world' }))
stream.statusCode = 200
stream.headers = {
'content-type': 'application/json;utf=8',
'content-encoding': 'gzip',
'content-length': 1100,
connection: 'keep-alive',
date: new Date().toISOString()
}
stream.on('close', () => t.pass('Stream destroyed'))
process.nextTick(callback, null, stream)
return { abort () {} }
}
}
const client = new Client({
node: 'http://localhost:9200',
Connection: MockConnection
})
client.info({}, { maxCompressedResponseSize: 1000 }, (err, result) => {
t.ok(err instanceof errors.RequestAbortedError)
t.equal(err.message, 'The content length (1100) is bigger than the maximum allowed buffer (1000)')
t.equal(result.meta.attempts, 0)
})
})
test('Content length too big custom option (string)', t => {
t.plan(4)
class MockConnection extends Connection {
request (params, callback) {
const stream = intoStream(JSON.stringify({ hello: 'world' }))
stream.statusCode = 200
stream.headers = {
'content-type': 'application/json;utf=8',
'content-length': 1100,
connection: 'keep-alive',
date: new Date().toISOString()
}
stream.on('close', () => t.pass('Stream destroyed'))
process.nextTick(callback, null, stream)
return { abort () {} }
}
}
const client = new Client({
node: 'http://localhost:9200',
Connection: MockConnection
})
client.info({}, { maxResponseSize: 1000 }, (err, result) => {
t.ok(err instanceof errors.RequestAbortedError)
t.equal(err.message, 'The content length (1100) is bigger than the maximum allowed string (1000)')
t.equal(result.meta.attempts, 0)
})
})
test('Content length too big custom option override (buffer)', t => {
t.plan(4)
class MockConnection extends Connection {
request (params, callback) {
const stream = intoStream(JSON.stringify({ hello: 'world' }))
stream.statusCode = 200
stream.headers = {
'content-type': 'application/json;utf=8',
'content-encoding': 'gzip',
'content-length': 1100,
connection: 'keep-alive',
date: new Date().toISOString()
}
stream.on('close', () => t.pass('Stream destroyed'))
process.nextTick(callback, null, stream)
return { abort () {} }
}
}
const client = new Client({
node: 'http://localhost:9200',
Connection: MockConnection,
maxCompressedResponseSize: 2000
})
client.info({}, { maxCompressedResponseSize: 1000 }, (err, result) => {
t.ok(err instanceof errors.RequestAbortedError)
t.equal(err.message, 'The content length (1100) is bigger than the maximum allowed buffer (1000)')
t.equal(result.meta.attempts, 0)
})
})
test('Content length too big custom option override (string)', t => {
t.plan(4)
class MockConnection extends Connection {
request (params, callback) {
const stream = intoStream(JSON.stringify({ hello: 'world' }))
stream.statusCode = 200
stream.headers = {
'content-type': 'application/json;utf=8',
'content-length': 1100,
connection: 'keep-alive',
date: new Date().toISOString()
}
stream.on('close', () => t.pass('Stream destroyed'))
process.nextTick(callback, null, stream)
return { abort () {} }
}
}
const client = new Client({
node: 'http://localhost:9200',
Connection: MockConnection,
maxResponseSize: 2000
})
client.info({}, { maxResponseSize: 1000 }, (err, result) => {
t.ok(err instanceof errors.RequestAbortedError)
t.equal(err.message, 'The content length (1100) is bigger than the maximum allowed string (1000)')
t.equal(result.meta.attempts, 0)
})
})
test('maxResponseSize cannot be bigger than buffer.constants.MAX_STRING_LENGTH', t => {
t.plan(2)
try {
new Client({ // eslint-disable-line
node: 'http://localhost:9200',
maxResponseSize: buffer.constants.MAX_STRING_LENGTH + 10
})
t.fail('should throw')
} catch (err) {
t.ok(err instanceof errors.ConfigurationError)
t.equal(err.message, `The maxResponseSize cannot be bigger than ${buffer.constants.MAX_STRING_LENGTH}`)
}
})
test('maxCompressedResponseSize cannot be bigger than buffer.constants.MAX_STRING_LENGTH', t => {
t.plan(2)
try {
new Client({ // eslint-disable-line
node: 'http://localhost:9200',
maxCompressedResponseSize: buffer.constants.MAX_LENGTH + 10
})
t.fail('should throw')
} catch (err) {
t.ok(err instanceof errors.ConfigurationError)
t.equal(err.message, `The maxCompressedResponseSize cannot be bigger than ${buffer.constants.MAX_LENGTH}`)
}
})
test('Meta header enabled', t => {
t.plan(2)

View File

@ -1084,3 +1084,25 @@ test('getIssuerCertificate detects invalid/malformed certificates', t => {
}
t.equal(getIssuerCertificate(socket), null)
})
test('Should show local/remote socket address in case of ECONNRESET', t => {
t.plan(2)
function handler (req, res) {
res.destroy()
}
buildServer(handler, ({ port }, server) => {
const connection = new Connection({
url: new URL(`http://localhost:${port}`)
})
connection.request({
path: '/hello',
method: 'GET'
}, (err, res) => {
t.ok(err instanceof ConnectionError)
t.match(err.message, /socket\shang\sup\s-\sLocal:\s127.0.0.1:\d+,\sRemote:\s127.0.0.1:\d+/)
server.stop()
})
})
})