Compare commits
20 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b99654602a | |||
| 733070963b | |||
| 726d1824bd | |||
| e94eefe8a2 | |||
| cd61e30bb3 | |||
| 21683e6826 | |||
| 647546a4e5 | |||
| 1a6c36e291 | |||
| a34c6dd3a7 | |||
| 2a59c634f7 | |||
| 2d9bfd6730 | |||
| 68730dc0e6 | |||
| 0f60d78e5d | |||
| 0455b76fb8 | |||
| 63a68fb615 | |||
| d58365eb70 | |||
| 51568ed505 | |||
| be7c9f5e9d | |||
| 35b03aed17 | |||
| c51fbfaafd |
@ -5,6 +5,10 @@
|
||||
#
|
||||
# Export the ELASTICSEARCH_VERSION variable, eg. 'elasticsearch:8.0.0-SNAPSHOT'.
|
||||
|
||||
# Version 1.0
|
||||
# - Initial version of the run-elasticsearch.sh script
|
||||
|
||||
|
||||
if [[ -z "$ELASTICSEARCH_VERSION" ]]; then
|
||||
echo -e "\033[31;1mERROR:\033[0m Required environment variable [ELASTICSEARCH_VERSION] not set\033[0m"
|
||||
exit 1
|
||||
@ -12,6 +16,8 @@ fi
|
||||
|
||||
set -euxo pipefail
|
||||
|
||||
SCRIPT_PATH=$(dirname $(realpath -s $0))
|
||||
|
||||
moniker=$(echo "$ELASTICSEARCH_VERSION" | tr -C "[:alnum:]" '-')
|
||||
suffix=rest-test
|
||||
|
||||
@ -21,9 +27,10 @@ CLUSTER_NAME=${CLUSTER_NAME-${moniker}${suffix}}
|
||||
HTTP_PORT=${HTTP_PORT-9200}
|
||||
|
||||
ELASTIC_PASSWORD=${ELASTIC_PASSWORD-changeme}
|
||||
SSL_CERT=${SSL_CERT-"$PWD/certs/testnode.crt"}
|
||||
SSL_KEY=${SSL_KEY-"$PWD/certs/testnode.key"}
|
||||
SSL_CA=${SSL_CA-"$PWD/certs/ca.crt"}
|
||||
SSL_CERT=${SSL_CERT-"${SCRIPT_PATH}/certs/testnode.crt"}
|
||||
SSL_KEY=${SSL_KEY-"${SCRIPT_PATH}/certs/testnode.key"}
|
||||
SSL_CA=${SSL_CA-"${SCRIPT_PATH}/certs/ca.crt"}
|
||||
SSL_CA_PEM=${SSL_CA-"${SCRIPT_PATH}/certs/ca.pem"}
|
||||
|
||||
DETACH=${DETACH-false}
|
||||
CLEANUP=${CLEANUP-false}
|
||||
@ -40,8 +47,14 @@ function cleanup_volume {
|
||||
(docker volume rm "$1") || true
|
||||
fi
|
||||
}
|
||||
function container_running {
|
||||
if [[ "$(docker ps -q -f name=$1)" ]]; then
|
||||
return 0;
|
||||
else return 1;
|
||||
fi
|
||||
}
|
||||
function cleanup_node {
|
||||
if [[ "$(docker ps -q -f name=$1)" ]]; then
|
||||
if container_running "$1"; then
|
||||
echo -e "\033[34;1mINFO:\033[0m Removing container $1\033[0m"
|
||||
(docker container rm --force --volumes "$1") || true
|
||||
cleanup_volume "$1-${suffix}-data"
|
||||
@ -125,6 +138,7 @@ END
|
||||
--volume $SSL_CERT:/usr/share/elasticsearch/config/certs/testnode.crt
|
||||
--volume $SSL_KEY:/usr/share/elasticsearch/config/certs/testnode.key
|
||||
--volume $SSL_CA:/usr/share/elasticsearch/config/certs/ca.crt
|
||||
--volume $SSL_CA_PEM:/usr/share/elasticsearch/config/certs/ca.pem
|
||||
END
|
||||
))
|
||||
fi
|
||||
@ -134,6 +148,11 @@ if [[ "$ELASTICSEARCH_VERSION" != *oss* ]]; then
|
||||
url="https://elastic:$ELASTIC_PASSWORD@$NODE_NAME"
|
||||
fi
|
||||
|
||||
cert_validation_flags="--insecure"
|
||||
if [[ "$NODE_NAME" == "instance" ]]; then
|
||||
cert_validation_flags="--cacert /usr/share/elasticsearch/config/certs/ca.pem --resolve ${NODE_NAME}:443:127.0.0.1"
|
||||
fi
|
||||
|
||||
echo -e "\033[34;1mINFO:\033[0m Starting container $NODE_NAME \033[0m"
|
||||
set -x
|
||||
docker run \
|
||||
@ -146,7 +165,7 @@ docker run \
|
||||
--ulimit nofile=65536:65536 \
|
||||
--ulimit memlock=-1:-1 \
|
||||
--detach="$DETACH" \
|
||||
--health-cmd="curl --silent --insecure --fail $url:9200/_cluster/health || exit 1" \
|
||||
--health-cmd="curl $cert_validation_flags --fail $url:9200/_cluster/health || exit 1" \
|
||||
--health-interval=2s \
|
||||
--health-retries=20 \
|
||||
--health-timeout=2s \
|
||||
@ -155,14 +174,19 @@ docker run \
|
||||
set +x
|
||||
|
||||
if [[ "$DETACH" == "true" ]]; then
|
||||
until [[ "$(docker inspect -f "{{.State.Health.Status}}" ${NODE_NAME})" != "starting" ]]; do
|
||||
sleep 2;
|
||||
until ! container_running "$NODE_NAME" || (container_running "$NODE_NAME" && [[ "$(docker inspect -f "{{.State.Health.Status}}" ${NODE_NAME})" != "starting" ]]); do
|
||||
echo ""
|
||||
docker inspect -f "{{range .State.Health.Log}}{{.Output}}{{end}}" ${NODE_NAME}
|
||||
echo -e "\033[34;1mINFO:\033[0m waiting for node $NODE_NAME to be up\033[0m"
|
||||
sleep 2;
|
||||
done;
|
||||
# Always show the node getting started logs, this is very useful both on CI as well as while developing
|
||||
docker logs "$NODE_NAME"
|
||||
if [[ "$(docker inspect -f "{{.State.Health.Status}}" ${NODE_NAME})" != "healthy" ]]; then
|
||||
|
||||
# Always show logs if the container is running, this is very useful both on CI as well as while developing
|
||||
if container_running $NODE_NAME; then
|
||||
docker logs $NODE_NAME
|
||||
fi
|
||||
|
||||
if ! container_running $NODE_NAME || [[ "$(docker inspect -f "{{.State.Health.Status}}" ${NODE_NAME})" != "healthy" ]]; then
|
||||
cleanup 1
|
||||
echo
|
||||
echo -e "\033[31;1mERROR:\033[0m Failed to start ${ELASTICSEARCH_VERSION} in detached mode beyond health checks\033[0m"
|
||||
|
||||
61
.ci/run-repository.sh
Executable file
61
.ci/run-repository.sh
Executable file
@ -0,0 +1,61 @@
|
||||
#!/usr/bin/env bash
|
||||
# parameters are available to this script
|
||||
|
||||
# ELASTICSEARCH_VERSION -- version e.g Major.Minor.Patch(-Prelease)
|
||||
# ELASTICSEARCH_CONTAINER -- the docker moniker as a reference to know which docker image distribution is used
|
||||
# ELASTICSEARCH_URL -- The url at which elasticsearch is reachable
|
||||
# NETWORK_NAME -- The docker network name
|
||||
# NODE_NAME -- The docker container name also used as Elasticsearch node name
|
||||
# NODE_JS_VERSION -- node js version (defined in test-matrix.yml, a default is hardcoded here)
|
||||
|
||||
NODE_JS_VERSION=${NODE_JS_VERSION-12}
|
||||
|
||||
echo -e "\033[34;1mINFO:\033[0m URL ${ELASTICSEARCH_URL}\033[0m"
|
||||
echo -e "\033[34;1mINFO:\033[0m VERSION ${ELASTICSEARCH_VERSION}\033[0m"
|
||||
echo -e "\033[34;1mINFO:\033[0m CONTAINER ${ELASTICSEARCH_CONTAINER}\033[0m"
|
||||
echo -e "\033[34;1mINFO:\033[0m TEST_SUITE ${TEST_SUITE}\033[0m"
|
||||
echo -e "\033[34;1mINFO:\033[0m NODE_JS_VERSION ${NODE_JS_VERSION}\033[0m"
|
||||
|
||||
echo -e "\033[1m>>>>> Build docker container >>>>>>>>>>>>>>>>>>>>>>>>>>>>>\033[0m"
|
||||
|
||||
set -eo pipefail
|
||||
|
||||
set +x
|
||||
export VAULT_TOKEN=$(vault write -field=token auth/approle/login role_id="$VAULT_ROLE_ID" secret_id="$VAULT_SECRET_ID")
|
||||
export CODECOV_TOKEN=$(vault read -field=token secret/clients-ci/elasticsearch-js/codecov)
|
||||
unset VAULT_ROLE_ID VAULT_SECRET_ID VAULT_TOKEN
|
||||
set -x
|
||||
|
||||
docker build \
|
||||
--file .ci/Dockerfile \
|
||||
--tag elastic/elasticsearch-js \
|
||||
--build-arg NODE_JS_VERSION=${NODE_JS_VERSION} \
|
||||
.
|
||||
|
||||
echo -e "\033[1m>>>>> NPM run ci >>>>>>>>>>>>>>>>>>>>>>>>>>>>>\033[0m"
|
||||
|
||||
repo=$(realpath $(dirname $(realpath -s $0))/../)
|
||||
|
||||
if [[ $TEST_SUITE != "xpack" ]]; then
|
||||
docker run \
|
||||
--network=${NETWORK_NAME} \
|
||||
--env "TEST_ES_SERVER=${ELASTICSEARCH_URL}" \
|
||||
--env "CODECOV_TOKEN" \
|
||||
--volume $repo:/usr/src/app \
|
||||
--volume /usr/src/app/node_modules \
|
||||
--name elasticsearch-js \
|
||||
--rm \
|
||||
elastic/elasticsearch-js \
|
||||
npm run ci
|
||||
else
|
||||
docker run \
|
||||
--network=${NETWORK_NAME} \
|
||||
--env "TEST_ES_SERVER=${ELASTICSEARCH_URL}" \
|
||||
--env "CODECOV_TOKEN" \
|
||||
--volume $repo:/usr/src/app \
|
||||
--volume /usr/src/app/node_modules \
|
||||
--name elasticsearch-js \
|
||||
--rm \
|
||||
elastic/elasticsearch-js \
|
||||
npm run test:integration
|
||||
fi
|
||||
@ -1,59 +1,57 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
#
|
||||
# Runs the client tests via Docker with the expectation that the required
|
||||
# environment variables have already been exported before running this script.
|
||||
#
|
||||
# The required environment variables include:
|
||||
#
|
||||
# - $ELASTICSEARCH_VERSION
|
||||
# - $NODE_JS_VERSION
|
||||
# - $TEST_SUITE
|
||||
#
|
||||
# Version 1.0
|
||||
# - Moved to .ci folder and seperated out `run-repository.sh`
|
||||
|
||||
set -eo pipefail
|
||||
if [[ -z $ELASTICSEARCH_VERSION ]]; then
|
||||
echo -e "\033[31;1mERROR:\033[0m Required environment variable [ELASTICSEARCH_VERSION] not set\033[0m"
|
||||
exit 1
|
||||
fi
|
||||
set -euxo pipefail
|
||||
|
||||
set +x
|
||||
export VAULT_TOKEN=$(vault write -field=token auth/approle/login role_id="$VAULT_ROLE_ID" secret_id="$VAULT_SECRET_ID")
|
||||
export CODECOV_TOKEN=$(vault read -field=token secret/clients-ci/elasticsearch-js/codecov)
|
||||
unset VAULT_ROLE_ID VAULT_SECRET_ID VAULT_TOKEN
|
||||
set -x
|
||||
|
||||
docker build \
|
||||
--file .ci/Dockerfile \
|
||||
--tag elastic/elasticsearch-js \
|
||||
--build-arg NODE_JS_VERSION=${NODE_JS_VERSION} \
|
||||
.
|
||||
TEST_SUITE=${TEST_SUITE-oss}
|
||||
NODE_NAME=instance
|
||||
|
||||
NODE_NAME="es1"
|
||||
repo=$(pwd)
|
||||
testnodecrt="/.ci/certs/testnode.crt"
|
||||
testnodekey="/.ci/certs/testnode.key"
|
||||
cacrt="/.ci/certs/ca.crt"
|
||||
|
||||
elasticsearch_image="elasticsearch"
|
||||
elasticsearch_url="https://elastic:changeme@${NODE_NAME}:9200"
|
||||
elasticsearch_image=elasticsearch
|
||||
elasticsearch_url=https://elastic:changeme@${NODE_NAME}:9200
|
||||
if [[ $TEST_SUITE != "xpack" ]]; then
|
||||
elasticsearch_image="elasticsearch-oss"
|
||||
elasticsearch_url="http://${NODE_NAME}:9200"
|
||||
elasticsearch_image=elasticsearch-${TEST_SUITE}
|
||||
elasticsearch_url=http://${NODE_NAME}:9200
|
||||
fi
|
||||
|
||||
ELASTICSEARCH_VERSION="${elasticsearch_image}:${ELASTICSEARCH_VERSION}" \
|
||||
NODE_NAME="${NODE_NAME}" \
|
||||
NETWORK_NAME="esnet" \
|
||||
function cleanup {
|
||||
status=$?
|
||||
set +x
|
||||
ELASTICSEARCH_VERSION=${elasticsearch_image}:${ELASTICSEARCH_VERSION} \
|
||||
NODE_NAME=${NODE_NAME} \
|
||||
NETWORK_NAME=elasticsearch \
|
||||
CLEANUP=true \
|
||||
bash ./.ci/run-elasticsearch.sh
|
||||
# Report status and exit
|
||||
if [[ "$status" == "0" ]]; then
|
||||
echo -e "\n\033[32;1mSUCCESS run-tests\033[0m"
|
||||
exit 0
|
||||
else
|
||||
echo -e "\n\033[31;1mFAILURE during run-tests\033[0m"
|
||||
exit ${status}
|
||||
fi
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
echo -e "\033[1m>>>>> Start [$ELASTICSEARCH_VERSION container] >>>>>>>>>>>>>>>>>>>>>>>>>>>>>\033[0m"
|
||||
|
||||
ELASTICSEARCH_VERSION=${elasticsearch_image}:${ELASTICSEARCH_VERSION} \
|
||||
NODE_NAME=${NODE_NAME} \
|
||||
NETWORK_NAME=elasticsearch \
|
||||
DETACH=true \
|
||||
SSL_CERT="${repo}${testnodecrt}" \
|
||||
SSL_KEY="${repo}${testnodekey}" \
|
||||
SSL_CA="${repo}${cacrt}" \
|
||||
bash .ci/run-elasticsearch.sh
|
||||
|
||||
docker run \
|
||||
--network=esnet \
|
||||
--env "TEST_ES_SERVER=${elasticsearch_url}" \
|
||||
--env "CODECOV_TOKEN" \
|
||||
--volume $repo:/usr/src/app \
|
||||
--volume /usr/src/app/node_modules \
|
||||
--name elasticsearch-js \
|
||||
--rm \
|
||||
elastic/elasticsearch-js \
|
||||
npm run ci
|
||||
echo -e "\033[1m>>>>> Repository specific tests >>>>>>>>>>>>>>>>>>>>>>>>>>>>>\033[0m"
|
||||
|
||||
ELASTICSEARCH_CONTAINER=${elasticsearch_image}:${ELASTICSEARCH_VERSION} \
|
||||
NETWORK_NAME=elasticsearch \
|
||||
NODE_NAME=${NODE_NAME} \
|
||||
ELASTICSEARCH_URL=${elasticsearch_url} \
|
||||
bash .ci/run-repository.sh
|
||||
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@ -45,6 +45,9 @@ jspm_packages
|
||||
# vim swap files
|
||||
*.swp
|
||||
|
||||
#Jetbrains editor folder
|
||||
.idea
|
||||
|
||||
package-lock.json
|
||||
|
||||
# elasticsearch repo or binary files
|
||||
|
||||
@ -1,19 +1,25 @@
|
||||
[[auth-reference]]
|
||||
== Authentication
|
||||
|
||||
This document contains code snippets to show you how to connect to various Elasticsearch providers.
|
||||
This document contains code snippets to show you how to connect to various {es}
|
||||
providers.
|
||||
|
||||
|
||||
=== Elastic Cloud
|
||||
|
||||
If you are using https://www.elastic.co/cloud[Elastic Cloud], the client offers a easy way to connect to it via the `cloud` option. +
|
||||
You must pass the Cloud ID that you can find in the cloud console, then your username and password inside the `auth` option.
|
||||
If you are using https://www.elastic.co/cloud[Elastic Cloud], the client offers
|
||||
an easy way to connect to it via the `cloud` option. You must pass the Cloud ID
|
||||
that you can find in the cloud console, then your username and password inside
|
||||
the `auth` option.
|
||||
|
||||
NOTE: When connecting to Elastic Cloud, the client will automatically enable both request and response compression by default, since it yields significant throughput improvements. +
|
||||
Moreover, the client will also set the ssl option `secureProtocol` to `TLSv1_2_method` unless specified otherwise.
|
||||
You can still override this option by configuring them.
|
||||
NOTE: When connecting to Elastic Cloud, the client will automatically enable
|
||||
both request and response compression by default, since it yields significant
|
||||
throughput improvements. Moreover, the client will also set the ssl option
|
||||
`secureProtocol` to `TLSv1_2_method` unless specified otherwise. You can still
|
||||
override this option by configuring them.
|
||||
|
||||
IMPORTANT: Do not enable sniffing when using Elastic Cloud, since the nodes are behind a load balancer, Elastic Cloud will take care of everything for you.
|
||||
IMPORTANT: Do not enable sniffing when using Elastic Cloud, since the nodes are
|
||||
behind a load balancer, Elastic Cloud will take care of everything for you.
|
||||
|
||||
[source,js]
|
||||
----
|
||||
@ -29,9 +35,11 @@ const client = new Client({
|
||||
})
|
||||
----
|
||||
|
||||
|
||||
=== Basic authentication
|
||||
|
||||
You can provide your credentials by passing the `username` and `password` parameters via the `auth` option.
|
||||
You can provide your credentials by passing the `username` and `password`
|
||||
parameters via the `auth` option.
|
||||
|
||||
[source,js]
|
||||
----
|
||||
@ -45,6 +53,7 @@ const client = new Client({
|
||||
})
|
||||
----
|
||||
|
||||
|
||||
Otherwise, you can provide your credentials in the node(s) URL.
|
||||
|
||||
[source,js]
|
||||
@ -55,10 +64,15 @@ 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] 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].
|
||||
You can use the
|
||||
https://www.elastic.co/guide/en/elasticsearch/reference/7.x/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].
|
||||
|
||||
[source,js]
|
||||
----
|
||||
@ -88,7 +102,14 @@ const client = new Client({
|
||||
|
||||
=== SSL configuration
|
||||
|
||||
Without any additional configuration you can specify `https://` node urls, but the certificates used to sign these requests will not verified (`rejectUnauthorized: false`). To turn on certificate verification you must specify an `ssl` object either in the top level config or in each host config object and set `rejectUnauthorized: true`. The ssl config object can contain many of the same configuration options that https://nodejs.org/api/tls.html#tls_tls_connect_options_callback[tls.connect()] accepts.
|
||||
Without any additional configuration you can specify `https://` node urls, but
|
||||
the certificates used to sign these requests will not verified
|
||||
(`rejectUnauthorized: false`). To turn on certificate verification, you must
|
||||
specify an `ssl` object either in the top level config or in each host config
|
||||
object and set `rejectUnauthorized: true`. The ssl config object can contain
|
||||
many of the same configuration options that
|
||||
https://nodejs.org/api/tls.html#tls_tls_connect_options_callback[tls.connect()]
|
||||
accepts.
|
||||
|
||||
[source,js]
|
||||
----
|
||||
|
||||
@ -1,27 +1,44 @@
|
||||
[[breaking-changes]]
|
||||
== Breaking changes coming from the old client
|
||||
|
||||
If you were already using the previous version of this client --i.e. the one you used to install with `npm install elasticsearch`-- you will encounter some breaking changes.
|
||||
If you were already using the previous version of this client – the one you used
|
||||
to install with `npm install elasticsearch` – you will encounter some breaking
|
||||
changes.
|
||||
|
||||
|
||||
=== Don’t panic!
|
||||
|
||||
Every breaking change was carefully weighed, and each is justified. Furthermore, the new codebase has been rewritten with modern JavaScript and has been carefully designed to be easy to maintain.
|
||||
Every breaking change was carefully weighed, and each is justified. Furthermore,
|
||||
the new codebase has been rewritten with modern JavaScript and has been
|
||||
carefully designed to be easy to maintain.
|
||||
|
||||
|
||||
=== Breaking changes
|
||||
|
||||
* Minimum supported version of Node.js is `v8`.
|
||||
|
||||
* Everything has been rewritten using ES6 classes to help users extend the defaults more easily.
|
||||
* Everything has been rewritten using ES6 classes to help users extend the
|
||||
defaults more easily.
|
||||
|
||||
* There is no longer an integrated logger. The client now is an event emitter that emits the following events: `request`, `response`, and `error`.
|
||||
* There is no longer an integrated logger. The client now is an event emitter
|
||||
that emits the following events: `request`, `response`, and `error`.
|
||||
|
||||
* The code is no longer shipped with all the versions of the API, but only that of the package’s major version, This means that if you are using Elasticsearch `v6`, you will be required to install `@elastic/elasticsearch@6`, and so on.
|
||||
* The code is no longer shipped with all the versions of the API, but only that
|
||||
of the package’s major version. This means that if you are using {es} `v6`, you
|
||||
are required to install `@elastic/elasticsearch@6`, and so on.
|
||||
|
||||
* The internals are completely different, so if you used to tweak them a lot, you will need to refactor your code. The public API should be almost the same.
|
||||
* The internals are completely different, so if you used to tweak them a lot,
|
||||
you will need to refactor your code. The public API should be almost the same.
|
||||
|
||||
* No more browser support, for that will be distributed via another module, `@elastic/elasticsearch-browser`. This module is intended for Node.js only.
|
||||
* There is no longer browser support, for that will be distributed via another
|
||||
module: `@elastic/elasticsearch-browser`. This module is intended for Node.js
|
||||
only.
|
||||
|
||||
* The returned value of an API call will no longer be the `body`, `statusCode`,
|
||||
and `headers` for callbacks, and only the `body` for promises. The new returned
|
||||
value will be a unique object containing the `body`, `statusCode`, `headers`,
|
||||
`warnings`, and `meta`, for both callback and promises.
|
||||
|
||||
* The returned value of an API call will no longer be the `body`, `statusCode`, and `headers` for callbacks and just the `body` for promises. The new returned value will be a unique object containing the `body`, `statusCode`, `headers`, `warnings`, and `meta`, for both callback and promises.
|
||||
|
||||
[source,js]
|
||||
----
|
||||
@ -53,14 +70,20 @@ client.search({
|
||||
----
|
||||
|
||||
|
||||
* Errors: there is no longer a custom error class for every HTTP status code (such as `BadRequest` or `NotFound`). There is instead a single `ResponseError`. Each error class has been renamed, and now each is suffixed with `Error` at the end.
|
||||
* Errors: there is no longer a custom error class for every HTTP status code
|
||||
(such as `BadRequest` or `NotFound`). There is instead a single `ResponseError`.
|
||||
Every error class has been renamed, and now each is suffixed with `Error` at the
|
||||
end.
|
||||
|
||||
* Errors that have been removed: `RequestTypeError`, `Generic`, and all the status code specific errors (such as `BadRequest` or `NotFound`).
|
||||
* Removed errors: `RequestTypeError`, `Generic`, and all the status code
|
||||
specific errors (such as `BadRequest` or `NotFound`).
|
||||
|
||||
* Errors that have been added: `ConfigurationError` (in case of bad configurations) and `ResponseError`, which contains all the data you may need to handle the specific error, such as `statusCode`, `headers`, `body`, and `message`.
|
||||
* Added errors: `ConfigurationError` (in case of bad configurations) and
|
||||
`ResponseError` that contains all the data you may need to handle the specific
|
||||
error, such as `statusCode`, `headers`, `body`, and `message`.
|
||||
|
||||
|
||||
* Errors that has been renamed:
|
||||
* Renamed errors:
|
||||
|
||||
** `RequestTimeout` (408 statusCode) => `TimeoutError`
|
||||
** `ConnectionFault` => `ConnectionError`
|
||||
@ -68,9 +91,12 @@ client.search({
|
||||
** `Serialization` => `SerializationError`
|
||||
** `Serialization` => `DeserializationError`
|
||||
|
||||
* You must specify the port number in the configuration. In the previous version you can specify the host and port in a variety of ways, with the new client there is only one via the `node` parameter.
|
||||
* You must specify the port number in the configuration. In the previous
|
||||
version, you can specify the host and port in a variety of ways. With the new
|
||||
client, there is only one way to do it, via the `node` parameter.
|
||||
|
||||
* The `plugins` option has been removed, if you want to extend the client now you should use the `client.extend` API.
|
||||
* The `plugins` option has been removed. If you want to extend the client now,
|
||||
you should use the `client.extend` API.
|
||||
|
||||
[source,js]
|
||||
----
|
||||
@ -84,7 +110,10 @@ const client = new Client({ ... })
|
||||
client.extend(...)
|
||||
----
|
||||
|
||||
* There is a clear distinction between the API related parameters and the client related configurations, the parameters `ignore`, `headers`, `requestTimeout` and `maxRetries` are no longer part of the API object, and you should specify them in a second option object.
|
||||
* There is a clear distinction between the API related parameters and the client
|
||||
related configurations. The parameters `ignore`, `headers`, `requestTimeout` and
|
||||
`maxRetries` are no longer part of the API object and you need to specify them
|
||||
in a second option object.
|
||||
|
||||
[source,js]
|
||||
----
|
||||
@ -121,7 +150,11 @@ client.search({
|
||||
})
|
||||
----
|
||||
|
||||
* The `transport.request` method will no longer accept the `query` key, but the `querystring` key instead (which can be a string or an object), furthermore, you need to send a bulk-like request, instead of the `body` key, you should use the `bulkBody` key. Also in this method, the client specific parameters should be passed as a second object.
|
||||
* The `transport.request` method no longer accepts the `query` key. Use the
|
||||
`querystring` key instead (which can be a string or an object). You also
|
||||
need to send a bulk-like request instead of the `body` key, use the `bulkBody`
|
||||
key. In this method, the client specific parameters should be passed as a second
|
||||
object.
|
||||
|
||||
[source,js]
|
||||
----
|
||||
@ -168,7 +201,8 @@ client.transport.request({
|
||||
|
||||
=== Talk is cheap. Show me the code.
|
||||
|
||||
Following you will find a snippet of code with the old client, followed by the same code logic, but with the new client.
|
||||
You can find a code snippet with the old client below followed by the same code
|
||||
logic but with the new client.
|
||||
|
||||
[source,js]
|
||||
----
|
||||
|
||||
@ -1,14 +1,23 @@
|
||||
[[child-client]]
|
||||
== Creating a child client
|
||||
|
||||
There are some use cases where you may need multiple instances of the client. You can easily do that by calling `new Client()` as many times as you need, but you will lose all the benefits of using one single client, such as the long living connections and the connection pool handling. +
|
||||
To avoid this problem the client offers a `child` API, which returns a new client instance that shares the connection pool with the parent client. +
|
||||
There are some use cases where you may need multiple instances of the client.
|
||||
You can easily do that by calling `new Client()` as many times as you need, but
|
||||
you will lose all the benefits of using one single client, such as the long
|
||||
living connections and the connection pool handling. To avoid this problem the
|
||||
client offers a `child` API, which returns a new client instance that shares the
|
||||
connection pool with the parent client.
|
||||
|
||||
NOTE: The event emitter is shared between the parent and the child(ren), and if you extend the parent client, the child client will have the same extensions, while if the child client adds an extension, the parent client will not be extended.
|
||||
NOTE: The event emitter is shared between the parent and the child(ren). If you
|
||||
extend the parent client, the child client will have the same extensions, while
|
||||
if the child client adds an extension, the parent client will not be extended.
|
||||
|
||||
You can pass to the `child` every client option you would pass to a normal client, but the connection pool specific options (`ssl`, `agent`, `pingTimeout`, `Connection`, and `resurrectStrategy`).
|
||||
You can pass to the `child` every client option you would pass to a normal
|
||||
client, but the connection pool specific options (`ssl`, `agent`, `pingTimeout`,
|
||||
`Connection`, and `resurrectStrategy`).
|
||||
|
||||
CAUTION: If you call `close` in any of the parent/child clients, every client will be closed.
|
||||
CAUTION: If you call `close` in any of the parent/child clients, every client
|
||||
will be closed.
|
||||
|
||||
[source,js]
|
||||
----
|
||||
|
||||
@ -251,7 +251,7 @@ use the usual client code. In such cases, call `super.method`:
|
||||
class MyTransport extends Transport {
|
||||
request (params, options, callback) {
|
||||
// your code
|
||||
super.request(params, options, callback)
|
||||
return super.request(params, options, callback)
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
[[as_stream_examples]]
|
||||
== asStream
|
||||
|
||||
Instead of getting the parsed body back, you will get the raw Node.js stream of data.
|
||||
Instead of getting the parsed body back, you will get the raw Node.js stream of
|
||||
data.
|
||||
|
||||
[source,js]
|
||||
----
|
||||
@ -76,7 +77,8 @@ async function run () {
|
||||
run().catch(console.log)
|
||||
----
|
||||
|
||||
TIP: This can be useful if you need to pipe the Elasticsearch's response to a proxy, or send it directly to another source.
|
||||
TIP: This can be useful if you need to pipe the {es}'s response to a proxy, or
|
||||
send it directly to another source.
|
||||
|
||||
[source,js]
|
||||
----
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
[[bulk_examples]]
|
||||
== Bulk
|
||||
|
||||
The `bulk` API makes it possible to perform many index/delete operations in a single API call. +
|
||||
This can greatly increase the indexing speed.
|
||||
The `bulk` API makes it possible to perform many index/delete operations in a
|
||||
single API call. This can greatly increase the indexing speed.
|
||||
|
||||
[source,js]
|
||||
----
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
[[get_examples]]
|
||||
== Get
|
||||
|
||||
The get API allows to get a typed JSON document from the index based on its id. +
|
||||
The following example gets a JSON document from an index called `game-of-thrones`, under a type called `_doc`, with id valued `'1'`.
|
||||
The get API allows to get a typed JSON document from the index based on its id.
|
||||
The following example gets a JSON document from an index called
|
||||
`game-of-thrones`, under a type called `_doc`, with id valued `'1'`.
|
||||
|
||||
[source,js]
|
||||
---------
|
||||
|
||||
@ -7,6 +7,10 @@ Following you can find some examples on how to use the client.
|
||||
* Executing a <<bulk_examples,bulk>> request;
|
||||
* Executing a <<exists_examples,exists>> request;
|
||||
* Executing a <<get_examples,get>> request;
|
||||
* Executing a <<sql_query_examples,sql.query>> request;
|
||||
* Executing a <<update_examples,update>> request;
|
||||
* Executing a <<update_by_query_examples,update by query>> request;
|
||||
* Executing a <<reindex_examples,reindex>> request;
|
||||
* Use of the <<ignore_examples,ignore>> parameter;
|
||||
* Executing a <<msearch_examples,msearch>> request;
|
||||
* How do I <<scroll_examples,scroll>>?
|
||||
@ -26,3 +30,7 @@ include::search.asciidoc[]
|
||||
include::suggest.asciidoc[]
|
||||
include::transport.request.asciidoc[]
|
||||
include::typescript.asciidoc[]
|
||||
include::sql.query.asciidoc[]
|
||||
include::update.asciidoc[]
|
||||
include::update_by_query.asciidoc[]
|
||||
include::reindex.asciidoc[]
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
[[msearch_examples]]
|
||||
== MSearch
|
||||
|
||||
The multi search API allows to execute several search requests within the same API.
|
||||
The multi search API allows to execute several search requests within the same
|
||||
API.
|
||||
|
||||
[source,js]
|
||||
----
|
||||
|
||||
75
docs/examples/reindex.asciidoc
Normal file
75
docs/examples/reindex.asciidoc
Normal file
@ -0,0 +1,75 @@
|
||||
[[reindex_examples]]
|
||||
== Reindex
|
||||
|
||||
The `reindex` API extracts the document source from the source index and indexes the documents into the destination index. You can copy all documents to the destination index, reindex a subset of the documents or update the source before to reindex it.
|
||||
|
||||
In the following example we have a `game-of-thrones` index which contains different quotes of various characters, we want to create a new index only for the house Stark and remove the `house` field from the document source.
|
||||
|
||||
[source,js]
|
||||
----
|
||||
'use strict'
|
||||
|
||||
const { Client } = require('@elastic/elasticsearch')
|
||||
const client = new Client({ node: 'http://localhost:9200' })
|
||||
|
||||
async function run () {
|
||||
await client.index({
|
||||
index: 'game-of-thrones',
|
||||
body: {
|
||||
character: 'Ned Stark',
|
||||
quote: 'Winter is coming.',
|
||||
house: 'stark'
|
||||
}
|
||||
})
|
||||
|
||||
await client.index({
|
||||
index: 'game-of-thrones',
|
||||
body: {
|
||||
character: 'Arya Stark',
|
||||
quote: 'A girl is Arya Stark of Winterfell. And I\'m going home.',
|
||||
house: 'stark'
|
||||
}
|
||||
})
|
||||
|
||||
await client.index({
|
||||
index: 'game-of-thrones',
|
||||
refresh: true,
|
||||
body: {
|
||||
character: 'Tyrion Lannister',
|
||||
quote: 'A Lannister always pays his debts.',
|
||||
house: 'lannister'
|
||||
}
|
||||
})
|
||||
|
||||
await client.reindex({
|
||||
waitForCompletion: true,
|
||||
refresh: true,
|
||||
body: {
|
||||
source: {
|
||||
index: 'game-of-thrones',
|
||||
query: {
|
||||
match: { character: 'stark' }
|
||||
}
|
||||
},
|
||||
dest: {
|
||||
index: 'stark-index'
|
||||
},
|
||||
script: {
|
||||
lang: 'painless',
|
||||
source: 'ctx._source.remove("house")'
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const { body } = await client.search({
|
||||
index: 'stark-index',
|
||||
body: {
|
||||
query: { match_all: {} }
|
||||
}
|
||||
})
|
||||
|
||||
console.log(body.hits.hits)
|
||||
}
|
||||
|
||||
run().catch(console.log)
|
||||
----
|
||||
@ -1,13 +1,23 @@
|
||||
[[scroll_examples]]
|
||||
== Scroll
|
||||
|
||||
While a search request returns a single “page” of results, the scroll API can be used to retrieve large numbers of results (or even all results) from a single search request, in much the same way as you would use a cursor on a traditional database.
|
||||
While a search request returns a single “page” of results, the scroll API can be
|
||||
used to retrieve large numbers of results (or even all results) from a single
|
||||
search request, in much the same way as you would use a cursor on a traditional
|
||||
database.
|
||||
|
||||
Scrolling is not intended for real time user requests, but rather for processing large amounts of data, e.g. in order to reindex the contents of one index into a new index with a different configuration.
|
||||
Scrolling is not intended for real time user requests, but rather for processing
|
||||
large amounts of data, e.g. in order to reindex the contents of one index into a
|
||||
new index with a different configuration.
|
||||
|
||||
NOTE: The results that are returned from a scroll request reflect the state of the index at the time that the initial search request was made, like a snapshot in time. Subsequent changes to documents (index, update or delete) will only affect later search requests.
|
||||
NOTE: The results that are returned from a scroll request reflect the state of
|
||||
the index at the time that the initial search request was made, like a snapshot
|
||||
in time. Subsequent changes to documents (index, update or delete) will only
|
||||
affect later search requests.
|
||||
|
||||
In order to use scrolling, the initial search request should specify the scroll parameter in the query string, which tells Elasticsearch how long it should keep the “search context” alive.
|
||||
In order to use scrolling, the initial search request should specify the scroll
|
||||
parameter in the query string, which tells Elasticsearch how long it should keep
|
||||
the “search context” alive.
|
||||
|
||||
[source,js]
|
||||
----
|
||||
@ -100,7 +110,8 @@ async function run () {
|
||||
run().catch(console.log)
|
||||
----
|
||||
|
||||
Another cool usage of the `scroll` API can be done with Node.js ≥ 10, by using async iteration!
|
||||
Another cool usage of the `scroll` API can be done with Node.js ≥ 10, by using
|
||||
async iteration!
|
||||
|
||||
[source,js]
|
||||
----
|
||||
|
||||
@ -1,8 +1,11 @@
|
||||
[[search_examples]]
|
||||
== Search
|
||||
|
||||
The `search` API allows you to execute a search query and get back search hits that match the query. +
|
||||
The query can either be provided using a simple https://www.elastic.co/guide/en/elasticsearch/reference/6.6/search-uri-request.html[query string as a parameter], or using a https://www.elastic.co/guide/en/elasticsearch/reference/6.6/search-request-body.html[request body].
|
||||
The `search` API allows you to execute a search query and get back search hits
|
||||
that match the query. The query can either be provided using a simple
|
||||
https://www.elastic.co/guide/en/elasticsearch/reference/6.6/search-uri-request.html[query string as a parameter],
|
||||
or using a
|
||||
https://www.elastic.co/guide/en/elasticsearch/reference/6.6/search-request-body.html[request body].
|
||||
|
||||
[source,js]
|
||||
----
|
||||
|
||||
64
docs/examples/sql.asciidoc
Normal file
64
docs/examples/sql.asciidoc
Normal file
@ -0,0 +1,64 @@
|
||||
[[sql_examples]]
|
||||
== SQL
|
||||
|
||||
Elasticsearch SQL is an X-Pack component that allows SQL-like queries to be executed in real-time against Elasticsearch. Whether using the REST interface, command-line or JDBC, any client can use SQL to search and aggregate data natively inside Elasticsearch. One can think of Elasticsearch SQL as a translator, one that understands both SQL and Elasticsearch and makes it easy to read and process data in real-time, at scale by leveraging Elasticsearch capabilities.
|
||||
|
||||
In the following example we will search all the documents that has the field `house` equals to `stark`, log the result with the tabular view and then manipulate the result to obtain an object easy to navigate.
|
||||
|
||||
[source,js]
|
||||
----
|
||||
'use strict'
|
||||
|
||||
const { Client } = require('@elastic/elasticsearch')
|
||||
const client = new Client({ node: 'http://localhost:9200' })
|
||||
|
||||
async function run () {
|
||||
await client.index({
|
||||
index: 'game-of-thrones',
|
||||
body: {
|
||||
character: 'Ned Stark',
|
||||
quote: 'Winter is coming.',
|
||||
house: 'stark'
|
||||
}
|
||||
})
|
||||
|
||||
await client.index({
|
||||
index: 'game-of-thrones',
|
||||
body: {
|
||||
character: 'Arya Stark',
|
||||
quote: 'A girl is Arya Stark of Winterfell. And I\'m going home.',
|
||||
house: 'stark'
|
||||
}
|
||||
})
|
||||
|
||||
await client.index({
|
||||
index: 'game-of-thrones',
|
||||
refresh: true,
|
||||
body: {
|
||||
character: 'Tyrion Lannister',
|
||||
quote: 'A Lannister always pays his debts.',
|
||||
house: 'lannister'
|
||||
}
|
||||
})
|
||||
|
||||
const { body } = await client.sql.query({
|
||||
body: {
|
||||
query: "SELECT * FROM \"game-of-thrones\" WHERE house='stark'"
|
||||
}
|
||||
})
|
||||
|
||||
console.log(body)
|
||||
|
||||
const data = body.rows.map(row => {
|
||||
const obj = {}
|
||||
for (var i = 0; i < row.length; i++) {
|
||||
obj[body.columns[i].name] = row[i]
|
||||
}
|
||||
return obj
|
||||
})
|
||||
|
||||
console.log(data)
|
||||
}
|
||||
|
||||
run().catch(console.log)
|
||||
----
|
||||
64
docs/examples/sql.query.asciidoc
Normal file
64
docs/examples/sql.query.asciidoc
Normal file
@ -0,0 +1,64 @@
|
||||
[[sql_query_examples]]
|
||||
== SQL
|
||||
|
||||
Elasticsearch SQL is an X-Pack component that allows SQL-like queries to be executed in real-time against Elasticsearch. Whether using the REST interface, command-line or JDBC, any client can use SQL to search and aggregate data natively inside Elasticsearch. One can think of Elasticsearch SQL as a translator, one that understands both SQL and Elasticsearch and makes it easy to read and process data in real-time, at scale by leveraging Elasticsearch capabilities.
|
||||
|
||||
In the following example we will search all the documents that has the field `house` equals to `stark`, log the result with the tabular view and then manipulate the result to obtain an object easy to navigate.
|
||||
|
||||
[source,js]
|
||||
----
|
||||
'use strict'
|
||||
|
||||
const { Client } = require('@elastic/elasticsearch')
|
||||
const client = new Client({ node: 'http://localhost:9200' })
|
||||
|
||||
async function run () {
|
||||
await client.index({
|
||||
index: 'game-of-thrones',
|
||||
body: {
|
||||
character: 'Ned Stark',
|
||||
quote: 'Winter is coming.',
|
||||
house: 'stark'
|
||||
}
|
||||
})
|
||||
|
||||
await client.index({
|
||||
index: 'game-of-thrones',
|
||||
body: {
|
||||
character: 'Arya Stark',
|
||||
quote: 'A girl is Arya Stark of Winterfell. And I\'m going home.',
|
||||
house: 'stark'
|
||||
}
|
||||
})
|
||||
|
||||
await client.index({
|
||||
index: 'game-of-thrones',
|
||||
refresh: true,
|
||||
body: {
|
||||
character: 'Tyrion Lannister',
|
||||
quote: 'A Lannister always pays his debts.',
|
||||
house: 'lannister'
|
||||
}
|
||||
})
|
||||
|
||||
const { body } = await client.sql.query({
|
||||
body: {
|
||||
query: "SELECT * FROM \"game-of-thrones\" WHERE house='stark'"
|
||||
}
|
||||
})
|
||||
|
||||
console.log(body)
|
||||
|
||||
const data = body.rows.map(row => {
|
||||
const obj = {}
|
||||
for (var i = 0; i < row.length; i++) {
|
||||
obj[body.columns[i].name] = row[i]
|
||||
}
|
||||
return obj
|
||||
})
|
||||
|
||||
console.log(data)
|
||||
}
|
||||
|
||||
run().catch(console.log)
|
||||
----
|
||||
@ -1,10 +1,11 @@
|
||||
[[suggest_examples]]
|
||||
== Suggest
|
||||
|
||||
The suggest feature suggests similar looking terms based on a provided text by using a suggester. _Parts of the suggest feature are still under development._
|
||||
The suggest feature suggests similar looking terms based on a provided text by
|
||||
using a suggester. _Parts of the suggest feature are still under development._
|
||||
|
||||
The suggest request part is defined alongside the query part in a `search` request. +
|
||||
If the query part is left out, only suggestions are returned.
|
||||
The suggest request part is defined alongside the query part in a `search`
|
||||
request. If the query part is left out, only suggestions are returned.
|
||||
|
||||
[source,js]
|
||||
----
|
||||
|
||||
@ -1,12 +1,19 @@
|
||||
[[transport_request_examples]]
|
||||
== transport.request
|
||||
|
||||
It can happen that you need to communicate with Elasticsearch by using an API that is not supported by the client, to mitigate this issue you can directly call `client.transport.request`, which is the internal utility that the client uses to communicate with Elasticsearch when you use an API method.
|
||||
It can happen that you need to communicate with {es} by using an API that is not
|
||||
supported by the client, to mitigate this issue you can directly call
|
||||
`client.transport.request`, which is the internal utility that the client uses
|
||||
to communicate with {es} when you use an API method.
|
||||
|
||||
NOTE: When using the `transport.request` method you must provide all the parameters needed to perform an HTTP call, such as `method`, `path`, `querystring`, and `body`.
|
||||
NOTE: When using the `transport.request` method you must provide all the
|
||||
parameters needed to perform an HTTP call, such as `method`, `path`,
|
||||
`querystring`, and `body`.
|
||||
|
||||
|
||||
TIP: If you find yourself use this method too often, take in consideration the use of `client.extend`, which will make your code look cleaner and easier to maintain.
|
||||
TIP: If you find yourself use this method too often, take in consideration the
|
||||
use of `client.extend`, which will make your code look cleaner and easier to
|
||||
maintain.
|
||||
|
||||
[source,js]
|
||||
----
|
||||
|
||||
@ -1,9 +1,11 @@
|
||||
[[typescript_examples]]
|
||||
== Typescript
|
||||
|
||||
The client offers a first-class support for TypeScript, since it ships the type definitions for every exposed API.
|
||||
The client offers a first-class support for TypeScript, since it ships the type
|
||||
definitions for every exposed API.
|
||||
|
||||
NOTE: If you are using TypeScript you will be required to use _snake_case_ style to define the API parameters instead of _camelCase_.
|
||||
NOTE: If you are using TypeScript you will be required to use _snake_case_ style
|
||||
to define the API parameters instead of _camelCase_.
|
||||
|
||||
[source,ts]
|
||||
----
|
||||
|
||||
59
docs/examples/update-by-query.asciidoc
Normal file
59
docs/examples/update-by-query.asciidoc
Normal file
@ -0,0 +1,59 @@
|
||||
[[update_by_query_examples]]
|
||||
== Update By Query
|
||||
|
||||
The simplest usage of _update_by_query just performs an update on every document in the index without changing the source. This is useful to pick up a new property or some other online mapping change.
|
||||
|
||||
[source,js]
|
||||
---------
|
||||
'use strict'
|
||||
|
||||
const { Client } = require('@elastic/elasticsearch')
|
||||
const client = new Client({ node: 'http://localhost:9200' })
|
||||
|
||||
async function run () {
|
||||
await client.index({
|
||||
index: 'game-of-thrones',
|
||||
body: {
|
||||
character: 'Ned Stark',
|
||||
quote: 'Winter is coming.'
|
||||
}
|
||||
})
|
||||
|
||||
await client.index({
|
||||
index: 'game-of-thrones',
|
||||
refresh: true,
|
||||
body: {
|
||||
character: 'Arya Stark',
|
||||
quote: 'A girl is Arya Stark of Winterfell. And I\'m going home.'
|
||||
}
|
||||
})
|
||||
|
||||
await client.updateByQuery({
|
||||
index: 'game-of-thrones',
|
||||
refresh: true,
|
||||
body: {
|
||||
script: {
|
||||
lang: 'painless',
|
||||
source: 'ctx._source["house"] = "stark"'
|
||||
},
|
||||
query: {
|
||||
match: {
|
||||
character: 'stark'
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const { body } = await client.search({
|
||||
index: 'game-of-thrones',
|
||||
body: {
|
||||
query: { match_all: {} }
|
||||
}
|
||||
})
|
||||
|
||||
console.log(body.hits.hits)
|
||||
}
|
||||
|
||||
run().catch(console.log)
|
||||
|
||||
---------
|
||||
92
docs/examples/update.asciidoc
Normal file
92
docs/examples/update.asciidoc
Normal file
@ -0,0 +1,92 @@
|
||||
[[update_examples]]
|
||||
== Update
|
||||
|
||||
The update API allows updates of a specific document using the given script. +
|
||||
In the following example, we will index a document that also tracks how many times a character has said the given quote, and then we will update the `times` field.
|
||||
|
||||
[source,js]
|
||||
---------
|
||||
'use strict'
|
||||
|
||||
const { Client } = require('@elastic/elasticsearch')
|
||||
const client = new Client({ node: 'http://localhost:9200' })
|
||||
|
||||
async function run () {
|
||||
await client.index({
|
||||
index: 'game-of-thrones',
|
||||
id: '1',
|
||||
body: {
|
||||
character: 'Ned Stark',
|
||||
quote: 'Winter is coming.',
|
||||
times: 0
|
||||
}
|
||||
})
|
||||
|
||||
await client.update({
|
||||
index: 'game-of-thrones',
|
||||
id: '1',
|
||||
body: {
|
||||
script: {
|
||||
lang: 'painless',
|
||||
source: 'ctx._source.times++'
|
||||
// you can also use parameters
|
||||
// source: 'ctx._source.times += params.count',
|
||||
// params: { count: 1 }
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const { body } = await client.get({
|
||||
index: 'game-of-thrones',
|
||||
id: '1'
|
||||
})
|
||||
|
||||
console.log(body)
|
||||
}
|
||||
|
||||
run().catch(console.log)
|
||||
|
||||
---------
|
||||
|
||||
With the update API, you can also run a partial update of a document.
|
||||
|
||||
[source,js]
|
||||
---------
|
||||
'use strict'
|
||||
|
||||
const { Client } = require('@elastic/elasticsearch')
|
||||
const client = new Client({ node: 'http://localhost:9200' })
|
||||
|
||||
async function run () {
|
||||
await client.index({
|
||||
index: 'game-of-thrones',
|
||||
id: '1',
|
||||
body: {
|
||||
character: 'Ned Stark',
|
||||
quote: 'Winter is coming.',
|
||||
isAlive: true
|
||||
}
|
||||
})
|
||||
|
||||
await client.update({
|
||||
index: 'game-of-thrones',
|
||||
id: '1',
|
||||
body: {
|
||||
doc: {
|
||||
isAlive: false
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const { body } = await client.get({
|
||||
index: 'game-of-thrones',
|
||||
id: '1'
|
||||
})
|
||||
|
||||
console.log(body)
|
||||
}
|
||||
|
||||
run().catch(console.log)
|
||||
|
||||
|
||||
---------
|
||||
59
docs/examples/update_by_query.asciidoc
Normal file
59
docs/examples/update_by_query.asciidoc
Normal file
@ -0,0 +1,59 @@
|
||||
[[update_by_query_examples]]
|
||||
== Update By Query
|
||||
|
||||
The simplest usage of _update_by_query just performs an update on every document in the index without changing the source. This is useful to pick up a new property or some other online mapping change.
|
||||
|
||||
[source,js]
|
||||
---------
|
||||
'use strict'
|
||||
|
||||
const { Client } = require('@elastic/elasticsearch')
|
||||
const client = new Client({ node: 'http://localhost:9200' })
|
||||
|
||||
async function run () {
|
||||
await client.index({
|
||||
index: 'game-of-thrones',
|
||||
body: {
|
||||
character: 'Ned Stark',
|
||||
quote: 'Winter is coming.'
|
||||
}
|
||||
})
|
||||
|
||||
await client.index({
|
||||
index: 'game-of-thrones',
|
||||
refresh: true,
|
||||
body: {
|
||||
character: 'Arya Stark',
|
||||
quote: 'A girl is Arya Stark of Winterfell. And I\'m going home.'
|
||||
}
|
||||
})
|
||||
|
||||
await client.updateByQuery({
|
||||
index: 'game-of-thrones',
|
||||
refresh: true,
|
||||
body: {
|
||||
script: {
|
||||
lang: 'painless',
|
||||
source: 'ctx._source["house"] = "stark"'
|
||||
},
|
||||
query: {
|
||||
match: {
|
||||
character: 'stark'
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const { body } = await client.search({
|
||||
index: 'game-of-thrones',
|
||||
body: {
|
||||
query: { match_all: {} }
|
||||
}
|
||||
})
|
||||
|
||||
console.log(body.hits.hits)
|
||||
}
|
||||
|
||||
run().catch(console.log)
|
||||
|
||||
---------
|
||||
@ -1,10 +1,12 @@
|
||||
[[extend-client]]
|
||||
== Extend the client
|
||||
|
||||
Sometimes you need to reuse the same logic, or you want to build a custom API to allow you simplify your code. +
|
||||
The easiest way to achieve that is by extending the client.
|
||||
Sometimes you need to reuse the same logic, or you want to build a custom API to
|
||||
allow you simplify your code. The easiest way to achieve that is by extending
|
||||
the client.
|
||||
|
||||
NOTE: If you want to override existing methods, you should specify the `{ force: true }` option.
|
||||
NOTE: If you want to override existing methods, you should specify the
|
||||
`{ force: true }` option.
|
||||
|
||||
[source,js]
|
||||
----
|
||||
|
||||
@ -1,15 +1,22 @@
|
||||
[[observability]]
|
||||
== Observability
|
||||
|
||||
The client does not provide a default logger, but instead it offers an event emitter interfaces to hook into internal events, such as `request` and `response`.
|
||||
The client does not provide a default logger, but instead it offers an event
|
||||
emitter interfaces to hook into internal events, such as `request` and
|
||||
`response`.
|
||||
|
||||
Correlating those events can be quite hard, especially if your applications have a large codebase with many events happening at the same time.
|
||||
Correlating those events can be quite hard, especially if your applications have
|
||||
a large codebase with many events happening at the same time.
|
||||
|
||||
To help you with this, the client offers you a correlation id system and other features, let's see them in action.
|
||||
To help you with this, the client offers you a correlation id system and other
|
||||
features. Let's see them in action.
|
||||
|
||||
=== Events
|
||||
The client is an event emitter, this means that you can listen for its event and add additional logic to your code, without need to change the client internals or your normal usage. +
|
||||
You can find the events names by access the `events` key of the client.
|
||||
|
||||
The client is an event emitter, this means that you can listen for its event and
|
||||
add additional logic to your code, without need to change the client internals
|
||||
or your normal usage. You can find the events names by access the `events` key
|
||||
of the client.
|
||||
|
||||
[source,js]
|
||||
----
|
||||
@ -17,7 +24,9 @@ const { events } = require('@elastic/elasticsearch')
|
||||
console.log(events)
|
||||
----
|
||||
|
||||
The event emitter functionality can be useful if you want to log every request, response and error that is happening during the use of the client.
|
||||
|
||||
The event emitter functionality can be useful if you want to log every request,
|
||||
response and error that is happening during the use of the client.
|
||||
|
||||
[source,js]
|
||||
----
|
||||
@ -34,11 +43,12 @@ client.on('response', (err, result) => {
|
||||
})
|
||||
----
|
||||
|
||||
|
||||
The client emits the following events:
|
||||
[cols=2*]
|
||||
|===
|
||||
|`request`
|
||||
a|Emitted before sending the actual request to Elasticsearch _(emitted multiple times in case of retries)_.
|
||||
a|Emitted before sending the actual request to {es} _(emitted multiple times in case of retries)_.
|
||||
[source,js]
|
||||
----
|
||||
client.on('request', (err, result) => {
|
||||
@ -47,7 +57,7 @@ client.on('request', (err, result) => {
|
||||
----
|
||||
|
||||
|`response`
|
||||
a|Emitted once Elasticsearch response has been received and parsed.
|
||||
a|Emitted once {es} response has been received and parsed.
|
||||
[source,js]
|
||||
----
|
||||
client.on('response', (err, result) => {
|
||||
@ -76,6 +86,7 @@ client.on('resurrect', (err, result) => {
|
||||
|===
|
||||
|
||||
The values of `result` in `request`, `response` and `sniff` will be:
|
||||
|
||||
[source,ts]
|
||||
----
|
||||
body: any;
|
||||
@ -100,7 +111,9 @@ meta: {
|
||||
};
|
||||
----
|
||||
|
||||
|
||||
While the `result` value in `resurrect` will be:
|
||||
|
||||
[source,ts]
|
||||
----
|
||||
strategy: string;
|
||||
@ -112,8 +125,13 @@ request: {
|
||||
};
|
||||
----
|
||||
|
||||
|
||||
=== Correlation id
|
||||
Correlating events can be quite hard, especially if there are many events at the same time. The client offers you an automatic (and configurable) system to help you handle this problem.
|
||||
|
||||
Correlating events can be quite hard, especially if there are many events at the
|
||||
same time. The client offers you an automatic (and configurable) system to help
|
||||
you handle this problem.
|
||||
|
||||
[source,js]
|
||||
----
|
||||
const { Client } = require('@elastic/elasticsearch')
|
||||
@ -141,7 +159,10 @@ client.search({
|
||||
})
|
||||
----
|
||||
|
||||
By default the id is an incremental integer, but you can easily configure that with the `generateRequestId` option:
|
||||
|
||||
By default the id is an incremental integer, but you can easily configure that
|
||||
with the `generateRequestId` option:
|
||||
|
||||
[source,js]
|
||||
----
|
||||
const { Client } = require('@elastic/elasticsearch')
|
||||
@ -156,7 +177,9 @@ const client = new Client({
|
||||
})
|
||||
----
|
||||
|
||||
|
||||
You can also specify a custom id per request:
|
||||
|
||||
[source,js]
|
||||
----
|
||||
client.search({
|
||||
@ -169,8 +192,12 @@ client.search({
|
||||
})
|
||||
----
|
||||
|
||||
|
||||
=== Context object
|
||||
Sometimes, you might need to make some custom data available in your events, you can do that via the `context` option of a request:
|
||||
|
||||
Sometimes, you might need to make some custom data available in your events, you
|
||||
can do that via the `context` option of a request:
|
||||
|
||||
[source,js]
|
||||
----
|
||||
const { Client } = require('@elastic/elasticsearch')
|
||||
@ -202,8 +229,14 @@ client.search({
|
||||
})
|
||||
----
|
||||
|
||||
|
||||
=== Client name
|
||||
If you are using multiple instances of the client or if you are using multiple child clients _(which is the recommended way to have multiple instances of the client)_, you might need to recognize which client you are using, the `name` options will help you in this regard:
|
||||
|
||||
If you are using multiple instances of the client or if you are using multiple
|
||||
child clients _(which is the recommended way to have multiple instances of the
|
||||
client)_, you might need to recognize which client you are using. The `name`
|
||||
options will help you in this regard.
|
||||
|
||||
[source,js]
|
||||
----
|
||||
const { Client } = require('@elastic/elasticsearch')
|
||||
@ -249,11 +282,19 @@ child.search({
|
||||
})
|
||||
----
|
||||
|
||||
=== X-Opaque-Id support
|
||||
To improve the overall observability, the client offers an easy way to configure the `X-Opaque-Id` header. If you set the `X-Opaque-Id` in a specific request, this will allow you to discover this identifier in the https://www.elastic.co/guide/en/elasticsearch/reference/master/logging.html#deprecation-logging[deprecation logs], help you with https://www.elastic.co/guide/en/elasticsearch/reference/master/index-modules-slowlog.html#_identifying_search_slow_log_origin[identifying search slow log origin] as well as https://www.elastic.co/guide/en/elasticsearch/reference/master/tasks.html#_identifying_running_tasks[identifying running tasks].
|
||||
|
||||
The `X-Opaque-Id` should be configured in each request, for doing that you can use the `opaqueId` option, as you can see in the following example. +
|
||||
The resulting header will be `{ 'X-Opaque-Id': 'my-search' }`.
|
||||
=== X-Opaque-Id support
|
||||
|
||||
To improve the overall observability, the client offers an easy way to configure
|
||||
the `X-Opaque-Id` header. If you set the `X-Opaque-Id` in a specific request,
|
||||
this will allow you to discover this identifier in the
|
||||
https://www.elastic.co/guide/en/elasticsearch/reference/master/logging.html#deprecation-logging[deprecation logs],
|
||||
help you with https://www.elastic.co/guide/en/elasticsearch/reference/master/index-modules-slowlog.html#_identifying_search_slow_log_origin[identifying search slow log origin]
|
||||
as well as https://www.elastic.co/guide/en/elasticsearch/reference/master/tasks.html#_identifying_running_tasks[identifying running tasks].
|
||||
|
||||
The `X-Opaque-Id` should be configured in each request, for doing that you can
|
||||
use the `opaqueId` option, as you can see in the following example. The
|
||||
resulting header will be `{ 'X-Opaque-Id': 'my-search' }`.
|
||||
|
||||
[source,js]
|
||||
----
|
||||
@ -272,8 +313,13 @@ client.search({
|
||||
})
|
||||
----
|
||||
|
||||
Sometimes it may be useful to prefix all the `X-Opaque-Id` headers with a specific string, in case you need to identify a specific client or server. For doing this, the client offers a top-level configuration option: `opaqueIdPrefix`. +
|
||||
In the following example, the resulting header will be `{ 'X-Opaque-Id': 'proxy-client::my-search' }`.
|
||||
|
||||
Sometimes it may be useful to prefix all the `X-Opaque-Id` headers with a
|
||||
specific string, in case you need to identify a specific client or server. For
|
||||
doing this, the client offers a top-level configuration option:
|
||||
`opaqueIdPrefix`. In the following example, the resulting header will be
|
||||
`{ 'X-Opaque-Id': 'proxy-client::my-search' }`.
|
||||
|
||||
[source,js]
|
||||
----
|
||||
const { Client } = require('@elastic/elasticsearch')
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,12 +1,19 @@
|
||||
[[typescript]]
|
||||
== TypeScript support
|
||||
|
||||
The client offers a first-class support for TypeScript, since it ships the type definitions for every exposed API.
|
||||
The client offers a first-class support for TypeScript, since it ships the type
|
||||
definitions for every exposed API.
|
||||
|
||||
NOTE: If you are using TypeScript you will be required to use _snake_case_ style to define the API parameters instead of _camelCase_.
|
||||
NOTE: If you are using TypeScript you will be required to use _snake_case_ style
|
||||
to define the API parameters instead of _camelCase_.
|
||||
|
||||
Other than the types for the surface API, the client offers the types for every request method, via the `RequestParams`, if you need the types for a search request for instance, you can access them via `RequestParams.Search`.
|
||||
Every API that supports a body, accepts a https://www.typescriptlang.org/docs/handbook/generics.html[generics] which represents the type of the request body, if you don't configure anything, it will default to `any`.
|
||||
Other than the types for the surface API, the client offers the types for every
|
||||
request method, via the `RequestParams`, if you need the types for a search
|
||||
request for instance, you can access them via `RequestParams.Search`.
|
||||
Every API that supports a body, accepts a
|
||||
https://www.typescriptlang.org/docs/handbook/generics.html[generics] which
|
||||
represents the type of the request body, if you don't configure anything, it
|
||||
will default to `any`.
|
||||
|
||||
For example:
|
||||
|
||||
@ -40,7 +47,9 @@ const searchParams: RequestParams.Search = {
|
||||
}
|
||||
----
|
||||
|
||||
You can find the type definiton of a response in `ApiResponse`, which accepts a generics as well if you want to specify the body type, otherwise it defaults to `any`.
|
||||
You can find the type definiton of a response in `ApiResponse`, which accepts a
|
||||
generics as well if you want to specify the body type, otherwise it defaults to
|
||||
`any`.
|
||||
|
||||
[source,ts]
|
||||
----
|
||||
|
||||
4
index.d.ts
vendored
4
index.d.ts
vendored
@ -101,8 +101,8 @@ interface ClientOptions {
|
||||
cloud?: {
|
||||
id: string;
|
||||
// TODO: remove username and password here in 8
|
||||
username: string;
|
||||
password: string;
|
||||
username?: string;
|
||||
password?: string;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
2
lib/Transport.d.ts
vendored
2
lib/Transport.d.ts
vendored
@ -81,7 +81,7 @@ export interface TransportRequestParams {
|
||||
}
|
||||
|
||||
export interface TransportRequestOptions {
|
||||
ignore?: [number];
|
||||
ignore?: number[];
|
||||
requestTimeout?: number | string;
|
||||
maxRetries?: number;
|
||||
asStream?: boolean;
|
||||
|
||||
@ -109,7 +109,7 @@ class Transport {
|
||||
if (meta.aborted === true) return
|
||||
meta.connection = this.getConnection({ requestId: meta.request.id })
|
||||
if (meta.connection === null) {
|
||||
return callback(new NoLivingConnectionsError('There are not living connections'), result)
|
||||
return callback(new NoLivingConnectionsError('There are no living connections'), result)
|
||||
}
|
||||
|
||||
// TODO: make this assignment FAST
|
||||
@ -130,15 +130,17 @@ class Transport {
|
||||
return callback(err, result)
|
||||
}
|
||||
}
|
||||
headers['Content-Type'] = headers['Content-Type'] || 'application/json'
|
||||
|
||||
if (compression === 'gzip') {
|
||||
if (isStream(params.body) === false) {
|
||||
params.body = intoStream(params.body).pipe(createGzip())
|
||||
} else {
|
||||
params.body = params.body.pipe(createGzip())
|
||||
if (params.body !== '') {
|
||||
headers['Content-Type'] = headers['Content-Type'] || 'application/json'
|
||||
if (compression === 'gzip') {
|
||||
if (isStream(params.body) === false) {
|
||||
params.body = intoStream(params.body).pipe(createGzip())
|
||||
} else {
|
||||
params.body = params.body.pipe(createGzip())
|
||||
}
|
||||
headers['Content-Encoding'] = compression
|
||||
}
|
||||
headers['Content-Encoding'] = compression
|
||||
}
|
||||
|
||||
if (isStream(params.body) === false) {
|
||||
|
||||
@ -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": "7.5.0",
|
||||
"version": "7.5.1",
|
||||
"keywords": [
|
||||
"elasticsearch",
|
||||
"elastic",
|
||||
@ -19,7 +19,7 @@
|
||||
"test": "npm run lint && npm run test:unit && npm run test:behavior && npm run test:types",
|
||||
"test:unit": "tap test/unit/*.test.js -t 300 --no-coverage",
|
||||
"test:behavior": "tap test/behavior/*.test.js -t 300 --no-coverage",
|
||||
"test:integration": "tap test/integration/index.js -T --no-coverage",
|
||||
"test:integration": "node test/integration/index.js",
|
||||
"test:types": "tsc --project ./test/types/tsconfig.json",
|
||||
"test:coverage": "nyc tap test/unit/*.test.js test/behavior/*.test.js -t 300 && nyc report --reporter=text-lcov > coverage.lcov && codecov",
|
||||
"lint": "standard",
|
||||
@ -44,6 +44,7 @@
|
||||
"dedent": "^0.7.0",
|
||||
"deepmerge": "^4.0.0",
|
||||
"dezalgo": "^1.0.3",
|
||||
"fast-deep-equal": "^3.1.1",
|
||||
"js-yaml": "^3.13.1",
|
||||
"license-checker": "^25.0.1",
|
||||
"lolex": "^4.0.1",
|
||||
|
||||
@ -4,8 +4,14 @@
|
||||
|
||||
'use strict'
|
||||
|
||||
const { readdirSync } = require('fs')
|
||||
const { join } = require('path')
|
||||
const dedent = require('dedent')
|
||||
|
||||
const codeExamples = readdirSync(join(__dirname, '..', '..', 'docs', 'examples'))
|
||||
.map(file => file.slice(0, -9))
|
||||
.filter(api => api !== 'index')
|
||||
|
||||
function generateDocs (common, spec) {
|
||||
var doc = dedent`
|
||||
[[api-reference]]
|
||||
@ -67,7 +73,7 @@ function commonParameters (spec) {
|
||||
=== Common parameters
|
||||
Parameters that are accepted by all API endpoints.
|
||||
|
||||
link:{ref}/common-options.html[Reference]
|
||||
link:{ref}/common-options.html[Documentation]
|
||||
[cols=2*]
|
||||
|===\n`
|
||||
Object.keys(spec.params).forEach(key => {
|
||||
@ -170,7 +176,10 @@ function generateApiDoc (spec) {
|
||||
client.${camelify(name)}(${codeParameters.length > 0 ? `{\n ${codeParameters}\n}` : ''})
|
||||
----\n`
|
||||
if (documentationUrl) {
|
||||
doc += `link:${documentationUrl}[Reference]\n`
|
||||
doc += `link:${documentationUrl}[Documentation] +\n`
|
||||
}
|
||||
if (codeExamples.includes(name)) {
|
||||
doc += `{jsclient}/${name.replace(/\./g, '_')}_examples.html[Code Example] +\n`
|
||||
}
|
||||
|
||||
if (params.length !== 0) {
|
||||
|
||||
@ -8,16 +8,20 @@ const { readFileSync, accessSync, mkdirSync, readdirSync, statSync } = require('
|
||||
const { join, sep } = require('path')
|
||||
const yaml = require('js-yaml')
|
||||
const Git = require('simple-git')
|
||||
const tap = require('tap')
|
||||
const { Client } = require('../../index')
|
||||
const TestRunner = require('./test-runner')
|
||||
const build = require('./test-runner')
|
||||
const { sleep } = require('./helper')
|
||||
const ms = require('ms')
|
||||
|
||||
const esRepo = 'https://github.com/elastic/elasticsearch.git'
|
||||
const esFolder = join(__dirname, '..', '..', 'elasticsearch')
|
||||
const yamlFolder = join(esFolder, 'rest-api-spec', 'src', 'main', 'resources', 'rest-api-spec', 'test')
|
||||
const xPackYamlFolder = join(esFolder, 'x-pack', 'plugin', 'src', 'test', 'resources', 'rest-api-spec', 'test')
|
||||
|
||||
const MAX_API_TIME = 1000 * 90
|
||||
const MAX_FILE_TIME = 1000 * 30
|
||||
const MAX_TEST_TIME = 1000 * 2
|
||||
|
||||
const ossSkips = {
|
||||
'cat.indices/10_basic.yml': ['Test cat indices output for closed index (pre 7.2.0)'],
|
||||
'cluster.health/10_basic.yml': ['cluster health with closed index (pre 7.2.0)'],
|
||||
@ -68,235 +72,250 @@ const xPackBlackList = {
|
||||
'xpack/15_basic.yml': ['*']
|
||||
}
|
||||
|
||||
class Runner {
|
||||
constructor (opts = {}) {
|
||||
const options = { node: opts.node }
|
||||
if (opts.isXPack) {
|
||||
options.ssl = {
|
||||
ca: readFileSync(join(__dirname, '..', '..', '.ci', 'certs', 'ca.crt'), 'utf8'),
|
||||
rejectUnauthorized: false
|
||||
}
|
||||
}
|
||||
this.client = new Client(options)
|
||||
console.log('Loading yaml suite')
|
||||
}
|
||||
|
||||
async waitCluster (client, times = 0) {
|
||||
try {
|
||||
await client.cluster.health({ waitForStatus: 'green', timeout: '50s' })
|
||||
} catch (err) {
|
||||
if (++times < 10) {
|
||||
await sleep(5000)
|
||||
return this.waitCluster(client, times)
|
||||
}
|
||||
console.error(err)
|
||||
process.exit(1)
|
||||
function runner (opts = {}) {
|
||||
const options = { node: opts.node }
|
||||
if (opts.isXPack) {
|
||||
options.ssl = {
|
||||
ca: readFileSync(join(__dirname, '..', '..', '.ci', 'certs', 'ca.crt'), 'utf8'),
|
||||
rejectUnauthorized: false
|
||||
}
|
||||
}
|
||||
const client = new Client(options)
|
||||
log('Loading yaml suite')
|
||||
start({ client, isXPack: opts.isXPack })
|
||||
.catch(console.log)
|
||||
}
|
||||
|
||||
async start ({ isXPack }) {
|
||||
const { client } = this
|
||||
const parse = this.parse.bind(this)
|
||||
async function waitCluster (client, times = 0) {
|
||||
try {
|
||||
await client.cluster.health({ waitForStatus: 'green', timeout: '50s' })
|
||||
} catch (err) {
|
||||
if (++times < 10) {
|
||||
await sleep(5000)
|
||||
return waitCluster(client, times)
|
||||
}
|
||||
console.error(err)
|
||||
process.exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
console.log('Waiting for Elasticsearch')
|
||||
await this.waitCluster(client)
|
||||
async function start ({ client, isXPack }) {
|
||||
log('Waiting for Elasticsearch')
|
||||
await waitCluster(client)
|
||||
|
||||
const { body } = await client.info()
|
||||
const { number: version, build_hash: sha } = body.version
|
||||
const { body } = await client.info()
|
||||
const { number: version, build_hash: sha } = body.version
|
||||
|
||||
console.log(`Checking out sha ${sha}...`)
|
||||
await this.withSHA(sha)
|
||||
log(`Checking out sha ${sha}...`)
|
||||
await withSHA(sha)
|
||||
|
||||
console.log(`Testing ${isXPack ? 'XPack' : 'oss'} api...`)
|
||||
log(`Testing ${isXPack ? 'XPack' : 'oss'} api...`)
|
||||
|
||||
const folders = []
|
||||
.concat(getAllFiles(yamlFolder))
|
||||
.concat(isXPack ? getAllFiles(xPackYamlFolder) : [])
|
||||
.filter(t => !/(README|TODO)/g.test(t))
|
||||
// we cluster the array based on the folder names,
|
||||
// to provide a better test log output
|
||||
.reduce((arr, file) => {
|
||||
const path = file.slice(file.indexOf('/rest-api-spec/test'), file.lastIndexOf('/'))
|
||||
var inserted = false
|
||||
for (var i = 0; i < arr.length; i++) {
|
||||
if (arr[i][0].includes(path)) {
|
||||
inserted = true
|
||||
arr[i].push(file)
|
||||
break
|
||||
const folders = getAllFiles(isXPack ? xPackYamlFolder : yamlFolder)
|
||||
.filter(t => !/(README|TODO)/g.test(t))
|
||||
// we cluster the array based on the folder names,
|
||||
// to provide a better test log output
|
||||
.reduce((arr, file) => {
|
||||
const path = file.slice(file.indexOf('/rest-api-spec/test'), file.lastIndexOf('/'))
|
||||
var inserted = false
|
||||
for (var i = 0; i < arr.length; i++) {
|
||||
if (arr[i][0].includes(path)) {
|
||||
inserted = true
|
||||
arr[i].push(file)
|
||||
break
|
||||
}
|
||||
}
|
||||
if (!inserted) arr.push([file])
|
||||
return arr
|
||||
}, [])
|
||||
|
||||
const totalTime = now()
|
||||
for (const folder of folders) {
|
||||
// pretty name
|
||||
const apiName = folder[0].slice(
|
||||
folder[0].indexOf(`${sep}rest-api-spec${sep}test`) + 19,
|
||||
folder[0].lastIndexOf(sep)
|
||||
)
|
||||
|
||||
log('Testing ' + apiName.slice(1))
|
||||
const apiTime = now()
|
||||
|
||||
for (const file of folder) {
|
||||
const testRunner = build({
|
||||
client,
|
||||
version,
|
||||
isXPack: file.includes('x-pack')
|
||||
})
|
||||
const fileTime = now()
|
||||
const data = readFileSync(file, 'utf8')
|
||||
// get the test yaml (as object), some file has multiple yaml documents inside,
|
||||
// every document is separated by '---', so we split on the separator
|
||||
// and then we remove the empty strings, finally we parse them
|
||||
const tests = data
|
||||
.split('\n---\n')
|
||||
.map(s => s.trim())
|
||||
.filter(Boolean)
|
||||
.map(parse)
|
||||
|
||||
// get setup and teardown if present
|
||||
var setupTest = null
|
||||
var teardownTest = null
|
||||
for (const test of tests) {
|
||||
if (test.setup) setupTest = test.setup
|
||||
if (test.teardown) teardownTest = test.teardown
|
||||
}
|
||||
|
||||
const cleanPath = file.slice(file.lastIndexOf(apiName))
|
||||
log(' ' + cleanPath)
|
||||
|
||||
for (const test of tests) {
|
||||
const testTime = now()
|
||||
const name = Object.keys(test)[0]
|
||||
if (name === 'setup' || name === 'teardown') continue
|
||||
if (shouldSkip(isXPack, file, name)) continue
|
||||
log(' - ' + name)
|
||||
try {
|
||||
await testRunner.run(setupTest, test[name], teardownTest)
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
process.exit(1)
|
||||
}
|
||||
const totalTestTime = now() - testTime
|
||||
if (totalTestTime > MAX_TEST_TIME) {
|
||||
log(' took too long: ' + ms(totalTestTime))
|
||||
} else {
|
||||
log(' took: ' + ms(totalTestTime))
|
||||
}
|
||||
}
|
||||
const totalFileTime = now() - fileTime
|
||||
if (totalFileTime > MAX_FILE_TIME) {
|
||||
log(` ${cleanPath} took too long: ` + ms(totalFileTime))
|
||||
} else {
|
||||
log(` ${cleanPath} took: ` + ms(totalFileTime))
|
||||
}
|
||||
}
|
||||
const totalApiTime = now() - apiTime
|
||||
if (totalApiTime > MAX_API_TIME) {
|
||||
log(`${apiName} took too long: ` + ms(totalApiTime))
|
||||
} else {
|
||||
log(`${apiName} took: ` + ms(totalApiTime))
|
||||
}
|
||||
}
|
||||
log(`Total testing time: ${ms(now() - totalTime)}`)
|
||||
}
|
||||
|
||||
function log (text) {
|
||||
process.stdout.write(text + '\n')
|
||||
}
|
||||
|
||||
function now () {
|
||||
var ts = process.hrtime()
|
||||
return (ts[0] * 1e3) + (ts[1] / 1e6)
|
||||
}
|
||||
|
||||
function parse (data) {
|
||||
try {
|
||||
var doc = yaml.safeLoad(data)
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
return
|
||||
}
|
||||
return doc
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the elasticsearch repository to the given sha.
|
||||
* If the repository is not present in `esFolder` it will
|
||||
* clone the repository and the checkout the sha.
|
||||
* If the repository is already present but it cannot checkout to
|
||||
* the given sha, it will perform a pull and then try again.
|
||||
* @param {string} sha
|
||||
* @param {function} callback
|
||||
*/
|
||||
function withSHA (sha) {
|
||||
return new Promise((resolve, reject) => {
|
||||
_withSHA(err => err ? reject(err) : resolve())
|
||||
})
|
||||
|
||||
function _withSHA (callback) {
|
||||
var fresh = false
|
||||
var retry = 0
|
||||
|
||||
if (!pathExist(esFolder)) {
|
||||
if (!createFolder(esFolder)) {
|
||||
return callback(new Error('Failed folder creation'))
|
||||
}
|
||||
fresh = true
|
||||
}
|
||||
|
||||
const git = Git(esFolder)
|
||||
|
||||
if (fresh) {
|
||||
clone(checkout)
|
||||
} else {
|
||||
checkout()
|
||||
}
|
||||
|
||||
function checkout () {
|
||||
log(`Checking out sha '${sha}'`)
|
||||
git.checkout(sha, err => {
|
||||
if (err) {
|
||||
if (retry++ > 0) {
|
||||
return callback(err)
|
||||
}
|
||||
return pull(checkout)
|
||||
}
|
||||
if (!inserted) arr.push([file])
|
||||
return arr
|
||||
}, [])
|
||||
|
||||
for (const folder of folders) {
|
||||
// pretty name
|
||||
const apiName = folder[0].slice(
|
||||
folder[0].indexOf(`${sep}rest-api-spec${sep}test`) + 19,
|
||||
folder[0].lastIndexOf(sep)
|
||||
)
|
||||
|
||||
tap.test(`Testing ${apiName}`, { bail: true, timeout: 0 }, t => {
|
||||
for (const file of folder) {
|
||||
const data = readFileSync(file, 'utf8')
|
||||
// get the test yaml (as object), some file has multiple yaml documents inside,
|
||||
// every document is separated by '---', so we split on the separator
|
||||
// and then we remove the empty strings, finally we parse them
|
||||
const tests = data
|
||||
.split('\n---\n')
|
||||
.map(s => s.trim())
|
||||
.filter(Boolean)
|
||||
.map(parse)
|
||||
|
||||
t.test(
|
||||
file.slice(file.lastIndexOf(apiName)),
|
||||
testFile(file, tests)
|
||||
)
|
||||
}
|
||||
t.end()
|
||||
callback()
|
||||
})
|
||||
}
|
||||
|
||||
function testFile (file, tests) {
|
||||
return t => {
|
||||
// get setup and teardown if present
|
||||
var setupTest = null
|
||||
var teardownTest = null
|
||||
for (const test of tests) {
|
||||
if (test.setup) setupTest = test.setup
|
||||
if (test.teardown) teardownTest = test.teardown
|
||||
function pull (cb) {
|
||||
log('Pulling elasticsearch repository...')
|
||||
git.pull(err => {
|
||||
if (err) {
|
||||
return callback(err)
|
||||
}
|
||||
|
||||
tests.forEach(test => {
|
||||
const name = Object.keys(test)[0]
|
||||
if (name === 'setup' || name === 'teardown') return
|
||||
if (shouldSkip(t, isXPack, file, name)) return
|
||||
|
||||
// create a subtest for the specific folder + test file + test name
|
||||
t.test(name, async t => {
|
||||
const testRunner = new TestRunner({
|
||||
client,
|
||||
version,
|
||||
tap: t,
|
||||
isXPack: file.includes('x-pack')
|
||||
})
|
||||
await testRunner.run(setupTest, test[name], teardownTest)
|
||||
})
|
||||
})
|
||||
t.end()
|
||||
}
|
||||
cb()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
parse (data) {
|
||||
try {
|
||||
var doc = yaml.safeLoad(data)
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
return
|
||||
}
|
||||
return doc
|
||||
}
|
||||
|
||||
getTest (folder) {
|
||||
const tests = readdirSync(folder)
|
||||
return tests.filter(t => !/(README|TODO)/g.test(t))
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the elasticsearch repository to the given sha.
|
||||
* If the repository is not present in `esFolder` it will
|
||||
* clone the repository and the checkout the sha.
|
||||
* If the repository is already present but it cannot checkout to
|
||||
* the given sha, it will perform a pull and then try again.
|
||||
* @param {string} sha
|
||||
* @param {function} callback
|
||||
*/
|
||||
withSHA (sha) {
|
||||
return new Promise((resolve, reject) => {
|
||||
_withSHA.call(this, err => err ? reject(err) : resolve())
|
||||
})
|
||||
|
||||
function _withSHA (callback) {
|
||||
var fresh = false
|
||||
var retry = 0
|
||||
|
||||
if (!this.pathExist(esFolder)) {
|
||||
if (!this.createFolder(esFolder)) {
|
||||
return callback(new Error('Failed folder creation'))
|
||||
function clone (cb) {
|
||||
log('Cloning elasticsearch repository...')
|
||||
git.clone(esRepo, esFolder, err => {
|
||||
if (err) {
|
||||
return callback(err)
|
||||
}
|
||||
fresh = true
|
||||
}
|
||||
|
||||
const git = Git(esFolder)
|
||||
|
||||
if (fresh) {
|
||||
clone(checkout)
|
||||
} else {
|
||||
checkout()
|
||||
}
|
||||
|
||||
function checkout () {
|
||||
console.log(`Checking out sha '${sha}'`)
|
||||
git.checkout(sha, err => {
|
||||
if (err) {
|
||||
if (retry++ > 0) {
|
||||
return callback(err)
|
||||
}
|
||||
return pull(checkout)
|
||||
}
|
||||
callback()
|
||||
})
|
||||
}
|
||||
|
||||
function pull (cb) {
|
||||
console.log('Pulling elasticsearch repository...')
|
||||
git.pull(err => {
|
||||
if (err) {
|
||||
return callback(err)
|
||||
}
|
||||
cb()
|
||||
})
|
||||
}
|
||||
|
||||
function clone (cb) {
|
||||
console.log('Cloning elasticsearch repository...')
|
||||
git.clone(esRepo, esFolder, err => {
|
||||
if (err) {
|
||||
return callback(err)
|
||||
}
|
||||
cb()
|
||||
})
|
||||
}
|
||||
cb()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the given path exists
|
||||
* @param {string} path
|
||||
* @returns {boolean} true if exists, false if not
|
||||
*/
|
||||
pathExist (path) {
|
||||
try {
|
||||
accessSync(path)
|
||||
return true
|
||||
} catch (err) {
|
||||
return false
|
||||
}
|
||||
/**
|
||||
* Checks if the given path exists
|
||||
* @param {string} path
|
||||
* @returns {boolean} true if exists, false if not
|
||||
*/
|
||||
function pathExist (path) {
|
||||
try {
|
||||
accessSync(path)
|
||||
return true
|
||||
} catch (err) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the given folder
|
||||
* @param {string} name
|
||||
* @returns {boolean} true on success, false on failure
|
||||
*/
|
||||
createFolder (name) {
|
||||
try {
|
||||
mkdirSync(name)
|
||||
return true
|
||||
} catch (err) {
|
||||
return false
|
||||
}
|
||||
/**
|
||||
* Creates the given folder
|
||||
* @param {string} name
|
||||
* @returns {boolean} true on success, false on failure
|
||||
*/
|
||||
function createFolder (name) {
|
||||
try {
|
||||
mkdirSync(name)
|
||||
return true
|
||||
} catch (err) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
@ -306,18 +325,17 @@ if (require.main === module) {
|
||||
node,
|
||||
isXPack: node.indexOf('@') > -1
|
||||
}
|
||||
const runner = new Runner(opts)
|
||||
runner.start(opts).catch(console.log)
|
||||
runner(opts)
|
||||
}
|
||||
|
||||
const shouldSkip = (t, isXPack, file, name) => {
|
||||
const shouldSkip = (isXPack, file, name) => {
|
||||
var list = Object.keys(ossSkips)
|
||||
for (var i = 0; i < list.length; i++) {
|
||||
const ossTest = ossSkips[list[i]]
|
||||
for (var j = 0; j < ossTest.length; j++) {
|
||||
if (file.endsWith(list[i]) && (name === ossTest[j] || ossTest[j] === '*')) {
|
||||
const testName = file.slice(file.indexOf(`${sep}elasticsearch${sep}`)) + ' / ' + name
|
||||
t.comment(`Skipping test ${testName} because is blacklisted in the oss test`)
|
||||
log(`Skipping test ${testName} because is blacklisted in the oss test`)
|
||||
return true
|
||||
}
|
||||
}
|
||||
@ -330,7 +348,7 @@ const shouldSkip = (t, isXPack, file, name) => {
|
||||
for (j = 0; j < platTest.length; j++) {
|
||||
if (file.endsWith(list[i]) && (name === platTest[j] || platTest[j] === '*')) {
|
||||
const testName = file.slice(file.indexOf(`${sep}elasticsearch${sep}`)) + ' / ' + name
|
||||
t.comment(`Skipping test ${testName} because is blacklisted in the XPack test`)
|
||||
log(`Skipping test ${testName} because is blacklisted in the XPack test`)
|
||||
return true
|
||||
}
|
||||
}
|
||||
@ -347,4 +365,4 @@ const getAllFiles = dir =>
|
||||
return isDirectory ? [...files, ...getAllFiles(name)] : [...files, name]
|
||||
}, [])
|
||||
|
||||
module.exports = Runner
|
||||
module.exports = runner
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -34,7 +34,6 @@ test('Should emit a request event when a request is performed', t => {
|
||||
body: '',
|
||||
querystring: 'q=foo%3Abar',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Content-Length': '0'
|
||||
}
|
||||
},
|
||||
@ -86,7 +85,6 @@ test('Should emit a response event in case of a successful response', t => {
|
||||
body: '',
|
||||
querystring: 'q=foo%3Abar',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Content-Length': '0'
|
||||
}
|
||||
},
|
||||
@ -136,7 +134,6 @@ test('Should emit a response event with the error set', t => {
|
||||
body: '',
|
||||
querystring: 'q=foo%3Abar',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Content-Length': '0'
|
||||
}
|
||||
},
|
||||
|
||||
@ -1813,6 +1813,55 @@ test('Compress request', t => {
|
||||
}
|
||||
})
|
||||
|
||||
t.test('Should skip the compression for empty strings/null/undefined', t => {
|
||||
t.plan(9)
|
||||
|
||||
function handler (req, res) {
|
||||
t.strictEqual(req.headers['content-encoding'], undefined)
|
||||
t.strictEqual(req.headers['content-type'], undefined)
|
||||
res.end()
|
||||
}
|
||||
|
||||
buildServer(handler, ({ port }, server) => {
|
||||
const pool = new ConnectionPool({ Connection })
|
||||
pool.addConnection(`http://localhost:${port}`)
|
||||
|
||||
const transport = new Transport({
|
||||
emit: () => {},
|
||||
connectionPool: pool,
|
||||
serializer: new Serializer(),
|
||||
maxRetries: 3,
|
||||
compression: 'gzip',
|
||||
requestTimeout: 30000,
|
||||
sniffInterval: false,
|
||||
sniffOnStart: false
|
||||
})
|
||||
|
||||
transport.request({
|
||||
method: 'DELETE',
|
||||
path: '/hello',
|
||||
body: ''
|
||||
}, (err, { body }) => {
|
||||
t.error(err)
|
||||
transport.request({
|
||||
method: 'GET',
|
||||
path: '/hello',
|
||||
body: null
|
||||
}, (err, { body }) => {
|
||||
t.error(err)
|
||||
transport.request({
|
||||
method: 'GET',
|
||||
path: '/hello',
|
||||
body: undefined
|
||||
}, (err, { body }) => {
|
||||
t.error(err)
|
||||
server.stop()
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user