Compare commits

..

18 Commits

Author SHA1 Message Date
1e103baec1 Bump version to 8.16.0 (#2464) 2024-11-14 11:50:48 -06:00
7d5f622506 Backport ts-standard upgrade (#2462) 2024-11-13 11:15:54 -06:00
8377b58af3 [Backport 8.16] Address feedback and add clarity (#2452)
Co-authored-by: Marci W <333176+marciw@users.noreply.github.com>
2024-11-12 11:07:38 -06:00
c150efbd21 Auto-generated code for 8.16 (#2442) 2024-11-11 11:04:42 -06:00
e7663aabde [Backport 8.16] Add changelog for 8.15.2 (#2446)
Co-authored-by: Josh Mock <joshua.mock@elastic.co>
2024-11-11 09:52:14 -06:00
c9615dc0ef [Backport 8.16] Add _id to the result of helpers.search (#2435)
Co-authored-by: Rami <72725910+ramikg@users.noreply.github.com>
2024-11-06 12:28:18 -06:00
bb5fb24d73 [Backport 8.16] Add streaming support to Arrow helper (#2430) 2024-11-04 16:20:06 -06:00
38358e20ab Auto-generated code for 8.16 (#2427) 2024-11-04 09:57:37 -06:00
9479d82644 Auto-generated code for 8.16 (#2410)
Co-authored-by: Josh Mock <joshua.mock@elastic.co>
2024-10-28 15:11:56 -05:00
18df52feb4 [Backport 8.16] Skip flaky test (#2419)
Co-authored-by: Josh Mock <joshua.mock@elastic.co>
2024-10-28 11:57:58 -05:00
f72f9e9a5a [Backport 8.16] Don't generate coverage during standard unit test run (#2406)
Co-authored-by: Josh Mock <joshua.mock@elastic.co>
2024-10-24 12:07:48 -05:00
c4151ceb35 [Backport 8.16] Upgrade tap to latest (#2401)
Co-authored-by: Josh Mock <joshua.mock@elastic.co>
2024-10-24 11:38:29 -05:00
f3aedc7ad0 Manual backport of #2375 (#2397) 2024-10-23 08:47:50 -05:00
586c42161d [Backport 8.16] Respect disablePrototypePoisoningProtection option (#2395)
Co-authored-by: Josh Mock <joshua.mock@elastic.co>
2024-10-22 15:03:23 -05:00
9947b0e365 [Backport 8.16] Add doc about timeout best practices (#2393)
Co-authored-by: Josh Mock <joshua.mock@elastic.co>
2024-10-22 15:01:15 -05:00
52b7264b45 [Backport 8.16] Update changelog for 8.15.1 (#2392)
Co-authored-by: Josh Mock <joshua.mock@elastic.co>
2024-10-22 15:00:57 -05:00
fceebae8ae Auto-generated code for 8.x (#2372) 2024-10-14 11:18:55 -05:00
e45ed28c05 Auto-generated code for 8.x (#2369) 2024-09-30 13:41:23 -05:00
195 changed files with 28305 additions and 5784 deletions

View File

@ -25,7 +25,7 @@ steps:
provider: "gcp"
image: family/core-ubuntu-2204
plugins:
- junit-annotate#v2.6.0:
- junit-annotate#v2.4.1:
artifacts: "junit-output/junit-*.xml"
job-uuid-file-pattern: "junit-(.*).xml"
fail-build-on-error: true

View File

@ -5,4 +5,3 @@ elasticsearch
.git
lib
junit-output
.tap

14
.github/make.sh vendored
View File

@ -65,7 +65,7 @@ codegen)
if [ -v "$VERSION" ] || [[ -z "$VERSION" ]]; then
# fall back to branch name or `main` if no VERSION is set
branch_name=$(git rev-parse --abbrev-ref HEAD)
if [[ "$branch_name" =~ ^[0-9]+\.([0-9]+|x) ]]; then
if [[ "$branch_name" =~ ^[0-9]+\.[0-9]+ ]]; then
echo -e "\033[36;1mTARGET: codegen -> No VERSION argument found, using branch name: \`$branch_name\`\033[0m"
VERSION="$branch_name"
else
@ -150,7 +150,7 @@ if [[ -z "${BUILDKITE+x}" ]] && [[ -z "${CI+x}" ]] && [[ -z "${GITHUB_ACTIONS+x}
-u "$(id -u):$(id -g)" \
--volume "$repo:/usr/src/elasticsearch-js" \
--volume /usr/src/elasticsearch-js/node_modules \
--volume "$(realpath "$repo/../elastic-client-generator-js"):/usr/src/elastic-client-generator-js" \
--volume "$(realpath $repo/../elastic-client-generator-js):/usr/src/elastic-client-generator-js" \
--env "WORKFLOW=$WORKFLOW" \
--name make-elasticsearch-js \
--rm \
@ -159,14 +159,6 @@ if [[ -z "${BUILDKITE+x}" ]] && [[ -z "${CI+x}" ]] && [[ -z "${GITHUB_ACTIONS+x}
node .buildkite/make.mjs --task $TASK ${TASK_ARGS[*]}"
else
echo -e "\033[34;1mINFO: Running in CI mode"
# determine branch to clone
GENERATOR_BRANCH="main"
if [[ "$VERSION" == 8.* ]]; then
GENERATOR_BRANCH="8.x"
fi
echo -e "\033[34;1mINFO: Generator branch: $GENERATOR_BRANCH"
docker run \
--volume "$repo:/usr/src/elasticsearch-js" \
--volume /usr/src/elasticsearch-js/node_modules \
@ -176,7 +168,7 @@ else
--rm \
$product \
/bin/bash -c "cd /usr/src && \
git clone --branch $GENERATOR_BRANCH https://$CLIENTS_GITHUB_TOKEN@github.com/elastic/elastic-client-generator-js.git && \
git clone https://$CLIENTS_GITHUB_TOKEN@github.com/elastic/elastic-client-generator-js.git && \
mkdir -p /usr/src/elastic-client-generator-js/output && \
cd /usr/src/elasticsearch-js && \
node .buildkite/make.mjs --task $TASK ${TASK_ARGS[*]}"

26
.github/stale.yml vendored Normal file
View File

@ -0,0 +1,26 @@
# Number of days of inactivity before an issue becomes stale
daysUntilStale: 15
# Number of days of inactivity before a stale issue is closed
daysUntilClose: 7
# Issues with these labels will never be considered stale
exemptLabels:
- "discussion"
- "feature request"
- "bug"
- "todo"
- "good first issue"
# Label to use when marking an issue as stale
staleLabel: stale
# Comment to post when marking an issue as stale. Set to `false` to disable
markComment: |
We understand that this might be important for you, but this issue has been automatically marked as stale because it has not had recent activity either from our end or yours.
It will be closed if no further activity occurs, please write a comment if you would like to keep this going.
Note: in the past months we have built a new client, that has just landed in master. If you want to open an issue or a pr for the legacy client, you should do that in https://github.com/elastic/elasticsearch-js-legacy
# Comment to post when closing a stale issue. Set to `false` to disable
closeComment: false

View File

@ -11,10 +11,10 @@ jobs:
outputs:
src-only: "${{ steps.changes.outputs.src-only }}"
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- uses: actions/checkout@v4
with:
persist-credentials: false
- uses: dorny/paths-filter/@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2
- uses: dorny/paths-filter/@v3.0.2
id: changes
with:
filters: |
@ -32,16 +32,16 @@ jobs:
strategy:
fail-fast: false
matrix:
node-version: [18.x, 20.x, 22.x, 23.x]
node-version: [18.x, 20.x, 22.x]
os: [ubuntu-latest, windows-latest, macOS-latest]
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- uses: actions/checkout@v4
with:
persist-credentials: false
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
@ -57,21 +57,17 @@ jobs:
run: |
npm run test:unit
- name: ECMAScript module test
run: |
npm run test:esm
license:
name: License check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- uses: actions/checkout@v4
with:
persist-credentials: false
- name: Use Node.js
uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4
uses: actions/setup-node@v4
with:
node-version: 22.x
@ -96,12 +92,12 @@ jobs:
os: [ubuntu-latest, windows-latest, macOS-latest]
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- uses: actions/checkout@v4
with:
persist-credentials: false
- name: Use Bun
uses: oven-sh/setup-bun@4bc047ad259df6fc24a6c9b0f9a0cb08cf17fbe5 # v2
uses: oven-sh/setup-bun@v2
- name: Install
run: |
@ -115,6 +111,12 @@ jobs:
run: |
bun run test:unit-bun
- name: ECMAScript module test
run: |
bun run test:esm
auto-approve:
name: Auto-approve
needs: [test, license]
runs-on: ubuntu-latest
permissions:
pull-requests: write
if: github.actor == 'elasticmachine'
steps:
- uses: hmarr/auto-approve-action@v4

View File

@ -12,29 +12,26 @@ jobs:
contents: write
id-token: write
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- uses: actions/checkout@v4
with:
persist-credentials: false
ref: ${{ github.event.inputs.branch }}
- uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4
- uses: actions/setup-node@v4
with:
node-version: "22.x"
registry-url: "https://registry.npmjs.org"
- run: npm install -g npm
- run: npm install
- run: npm test
- run: npm publish --provenance --access public --tag alpha
- run: npm publish --provenance --access public
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
- name: Publish version on GitHub
run: |
- run: |
version=$(jq -r .version package.json)
gh release create \
-n "This is a 9.0.0 pre-release alpha. Changes may not be stable." \
--latest=false \
--prerelease \
-n "[Changelog](https://www.elastic.co/guide/en/elasticsearch/client/javascript-api/$BRANCH_NAME/changelog-client.html)" \
--target "$BRANCH_NAME" \
--title "v$version" \
-t "v$version" \
"v$version"
env:
BRANCH_NAME: ${{ github.event.inputs.branch }}

View File

@ -26,14 +26,14 @@ jobs:
)
)
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- uses: actions/checkout@v4
with:
persist-credentials: false
repository: elastic/elasticsearch-js
ref: main
path: stack
fetch-depth: 0
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- uses: actions/checkout@v4
with:
persist-credentials: false
repository: elastic/elasticsearch-serverless-js
@ -42,7 +42,7 @@ jobs:
- name: Apply patch from stack to serverless
id: apply-patch
run: $GITHUB_WORKSPACE/stack/.github/workflows/serverless-patch.sh
- uses: peter-evans/create-pull-request@67ccf781d68cd99b580ae25a5c18a1cc84ffff1f # v7
- uses: peter-evans/create-pull-request@v6
with:
token: ${{ secrets.GH_TOKEN }}
path: serverless

View File

@ -1,21 +1,21 @@
---
name: "Close stale issues and PRs"
name: 'Close stale issues and PRs'
on:
schedule:
- cron: "30 1 * * *"
- cron: '30 1 * * *'
jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@28ca1036281a5e5922ead5184a1bbf96e5fc984e # v9
- uses: actions/stale@v8
with:
stale-issue-label: stale
stale-pr-label: stale
days-before-stale: 90
days-before-close: 14
exempt-issue-labels: "good first issue,tracking"
exempt-issue-labels: 'good first issue'
close-issue-label: closed-stale
close-pr-label: closed-stale
stale-issue-message: "This issue is stale because it has been open 90 days with no activity. Remove the `stale` label, or leave a comment, or this will be closed in 14 days."
stale-pr-message: "This pull request is stale because it has been open 90 days with no activity. Remove the `stale` label, or leave a comment, or this will be closed in 14 days."
stale-issue-message: 'This issue is stale because it has been open 90 days with no activity. Remove the `stale` label, or leave a comment, or this will be closed in 14 days.'
stale-pr-message: 'This pull request is stale because it has been open 90 days with no activity. Remove the `stale` label, or leave a comment, or this will be closed in 14 days.'

1
.gitignore vendored
View File

@ -67,4 +67,3 @@ junit-output
bun.lockb
test-results
processinfo
.tap

View File

@ -73,4 +73,3 @@ CONTRIBUTING.md
src
bun.lockb
.tap

View File

@ -42,9 +42,6 @@ spec:
main:
branch: "main"
cronline: "@daily"
8_x:
branch: "8.x"
cronline: "@daily"
8_14:
branch: "8.16"
branch: "8.14"
cronline: "@daily"

View File

@ -1,67 +1,6 @@
[[changelog-client]]
== Release notes
[discrete]
=== 9.0.0
[discrete]
==== Breaking changes
[discrete]
===== Drop support for deprecated `body` parameter
In 8.0, the top-level `body` parameter that was available on all API functions <<remove-body-key,was deprecated>>. In 9.0 this property is completely removed.
[discrete]
=== 8.17.0
[discrete]
==== Features
[discrete]
===== Support for Elasticsearch `v8.17`
You can find all the API changes
https://www.elastic.co/guide/en/elasticsearch/reference/8.17/release-notes-8.17.0.html[here].
[discrete]
=== 8.16.3
[discrete]
==== Fixes
[discrete]
===== Improved support for Elasticsearch `v8.16`
Updated TypeScript types based on fixes and improvements to the Elasticsearch specification.
[discrete]
=== 8.16.2
[discrete]
==== Fixes
[discrete]
===== Improved support for Elasticsearch `v8.16`
Updated TypeScript types based on fixes and improvements to the Elasticsearch specification.
[discrete]
===== Drop testing artifacts from npm package
Tap, the unit testing tool used by this project, was recently upgraded and started writing to a `.tap` directory. Since tests are run prior to an `npm publish` in CI, this directory was being included in the published package and bloating its size.
[discrete]
=== 8.16.1
[discrete]
==== Fixes
[discrete]
===== Fix ECMAScript imports
Fixed package configuration to correctly support native ECMAScript `import` syntax.
[discrete]
=== 8.16.0
@ -87,27 +26,11 @@ The ES|QL helper can now return results as an Apache Arrow `Table` or `RecordBat
The client's `disablePrototypePoisoningProtection` option was set to `true` by default, but when it was set to any other value it was ignored, making it impossible to enable prototype poisoning protection without providing a custom serializer implementation.
[discrete]
=== 8.15.3
[discrete]
==== Fixes
[discrete]
===== Improved support for Elasticsearch `v8.15`
Updated TypeScript types based on fixes and improvements to the Elasticsearch specification.
[discrete]
===== Drop testing artifacts from npm package
Tap, the unit testing tool, was recently upgraded and started writing to a `.tap` directory. Since tests are run prior to an `npm publish` in CI, this directory was being included in the published package and bloating its size.
[discrete]
=== 8.15.2
[discrete]
==== Fixes
==== Features
[discrete]
===== Improved support for Elasticsearch `v8.15`
@ -118,7 +41,7 @@ Updated TypeScript types based on fixes and improvements to the Elasticsearch sp
=== 8.15.1
[discrete]
==== Fixes
==== Features
[discrete]
===== Improved support for Elasticsearch `v8.15`
@ -679,7 +602,6 @@ ac.abort()
----
[discrete]
[[remove-body-key]]
===== Remove the body key from the request
*Breaking: Yes* | *Migration effort: Small*

View File

@ -1,46 +0,0 @@
// This file is autogenerated, DO NOT EDIT
// Use `node scripts/generate-docs-examples.js` to generate the docs examples
[source, js]
----
const response = await client.search({
index: "retrievers_example",
retriever: {
rrf: {
retrievers: [
{
standard: {
query: {
range: {
year: {
gt: 2023,
},
},
},
},
},
{
standard: {
query: {
term: {
topic: "elastic",
},
},
},
},
],
rank_window_size: 10,
rank_constant: 1,
},
},
_source: false,
aggs: {
topics: {
terms: {
field: "topic",
},
},
},
});
console.log(response);
----

View File

@ -1,18 +0,0 @@
// This file is autogenerated, DO NOT EDIT
// Use `node scripts/generate-docs-examples.js` to generate the docs examples
[source, js]
----
const response = await client.inference.put({
task_type: "rerank",
inference_id: "my-rerank-model",
inference_config: {
service: "cohere",
service_settings: {
model_id: "rerank-english-v3.0",
api_key: "{{COHERE_API_KEY}}",
},
},
});
console.log(response);
----

View File

@ -1,49 +0,0 @@
// This file is autogenerated, DO NOT EDIT
// Use `node scripts/generate-docs-examples.js` to generate the docs examples
[source, js]
----
const response = await client.search({
index: "retrievers_example_nested",
retriever: {
rrf: {
retrievers: [
{
standard: {
query: {
nested: {
path: "nested_field",
inner_hits: {
name: "nested_vector",
_source: false,
fields: ["nested_field.paragraph_id"],
},
query: {
knn: {
field: "nested_field.nested_vector",
query_vector: [1, 0, 0.5],
k: 10,
},
},
},
},
},
},
{
standard: {
query: {
term: {
topic: "ai",
},
},
},
},
],
rank_window_size: 10,
rank_constant: 1,
},
},
_source: ["topic"],
});
console.log(response);
----

View File

@ -3,12 +3,8 @@
[source, js]
----
const response = await client.transport.request({
method: "POST",
path: "/_query/async",
querystring: {
format: "json",
},
const response = await client.esql.asyncQuery({
format: "json",
body: {
query:
"\n FROM my-index-000001,cluster_one:my-index-000001,cluster_two:my-index*\n | STATS COUNT(http.response.status_code) BY user.id\n | LIMIT 2\n ",

View File

@ -1,57 +0,0 @@
// This file is autogenerated, DO NOT EDIT
// Use `node scripts/generate-docs-examples.js` to generate the docs examples
[source, js]
----
const response = await client.search({
index: "retrievers_example",
retriever: {
rrf: {
retrievers: [
{
standard: {
query: {
term: {
topic: "elastic",
},
},
},
},
{
rrf: {
retrievers: [
{
standard: {
query: {
query_string: {
query:
"(information retrieval) OR (artificial intelligence)",
default_field: "text",
},
},
},
},
{
knn: {
field: "vector",
query_vector: [0.23, 0.67, 0.89],
k: 3,
num_candidates: 5,
},
},
],
rank_window_size: 10,
rank_constant: 1,
},
},
],
rank_window_size: 10,
rank_constant: 1,
},
},
_source: false,
size: 1,
explain: true,
});
console.log(response);
----

View File

@ -3,9 +3,8 @@
[source, js]
----
const response = await client.transport.request({
method: "POST",
path: "/_application/search_application/my-app/_render_query",
const response = await client.searchApplication.renderQuery({
name: "my-app",
body: {
params: {
query_string: "my first query",

View File

@ -11,7 +11,7 @@ const response = await client.searchApplication.put({
script: {
lang: "mustache",
source:
'\n {\n "query": {\n "bool": {\n "must": [\n {{#query}}\n {{/query}}\n ],\n "filter": {{#toJson}}_es_filters{{/toJson}}\n }\n },\n "_source": {\n "includes": ["title", "plot"]\n },\n "highlight": {\n "fields": {\n "title": { "fragment_size": 0 },\n "plot": { "fragment_size": 200 }\n }\n },\n "aggs": {{#toJson}}_es_aggs{{/toJson}},\n "from": {{from}},\n "size": {{size}},\n "sort": {{#toJson}}_es_sort_fields{{/toJson}}\n }\n ',
'\n {\n "query": {\n "bool": {\n "must": [\n {{#query}}\n \n {{/query}}\n ],\n "filter": {{#toJson}}_es_filters{{/toJson}}\n }\n },\n "_source": {\n "includes": ["title", "plot"]\n },\n "highlight": {\n "fields": {\n "title": { "fragment_size": 0 },\n "plot": { "fragment_size": 200 }\n }\n },\n "aggs": {{#toJson}}_es_aggs{{/toJson}},\n "from": {{from}},\n "size": {{size}},\n "sort": {{#toJson}}_es_sort_fields{{/toJson}}\n }\n ',
params: {
query: "",
_es_filters: {},

View File

@ -1,27 +0,0 @@
// This file is autogenerated, DO NOT EDIT
// Use `node scripts/generate-docs-examples.js` to generate the docs examples
[source, js]
----
const response = await client.search({
retriever: {
text_similarity_reranker: {
retriever: {
standard: {
query: {
match: {
text: "How often does the moon hide the sun?",
},
},
},
},
field: "text",
inference_id: "elastic-rerank",
inference_text: "How often does the moon hide the sun?",
rank_window_size: 100,
min_score: 0.5,
},
},
});
console.log(response);
----

View File

@ -8,6 +8,11 @@ const response = await client.search({
query: {
bool: {
must: [
{
term: {
"category.keyword": "Main Course",
},
},
{
term: {
tags: "vegetarian",
@ -22,11 +27,6 @@ const response = await client.search({
},
],
should: [
{
term: {
category: "Main Course",
},
},
{
multi_match: {
query: "curry spicy",

View File

@ -3,9 +3,7 @@
[source, js]
----
const response = await client.transport.request({
method: "POST",
path: "/_ingest/_simulate",
const response = await client.simulate.ingest({
body: {
docs: [
{

View File

@ -5,7 +5,7 @@
----
const response = await client.transport.request({
method: "DELETE",
path: "/_ingest/ip_location/database/my-database-id",
path: "/_ingest/geoip/database/my-database-id",
});
console.log(response);
----

View File

@ -9,6 +9,7 @@ const response = await client.indices.create({
properties: {
inference_field: {
type: "semantic_text",
inference_id: "my-elser-endpoint",
},
},
},

View File

@ -0,0 +1,28 @@
// This file is autogenerated, DO NOT EDIT
// Use `node scripts/generate-docs-examples.js` to generate the docs examples
[source, js]
----
const response = await client.esql.query({
format: "txt",
query:
"\n FROM library\n | SORT page_count DESC\n | KEEP name, author\n | LOOKUP era ON author\n | LIMIT 5\n ",
tables: {
era: {
author: {
keyword: [
"Frank Herbert",
"Peter F. Hamilton",
"Vernor Vinge",
"Alastair Reynolds",
"James S.A. Corey",
],
},
era: {
keyword: ["The New Wave", "Diamond", "Diamond", "Diamond", "Hadron"],
},
},
},
});
console.log(response);
----

View File

@ -3,9 +3,7 @@
[source, js]
----
const response = await client.transport.request({
method: "POST",
path: "/_security/oidc/logout",
const response = await client.security.oidcLogout({
body: {
token:
"dGhpcyBpcyBub3QgYSByZWFsIHRva2VuIGJ1dCBpdCBpcyBvbmx5IHRlc3QgZGF0YS4gZG8gbm90IHRyeSB0byByZWFkIHRva2VuIQ==",

View File

@ -3,12 +3,10 @@
[source, js]
----
const response = await client.transport.request({
method: "GET",
path: "/_query/async/FmNJRUZ1YWZCU3dHY1BIOUhaenVSRkEaaXFlZ3h4c1RTWFNocDdnY2FSaERnUTozNDE&#x3D;",
querystring: {
wait_for_completion_timeout: "30s",
},
const response = await client.esql.asyncQueryGet({
id: "FmNJRUZ1YWZCU3dHY1BIOUhaenVSRkEaaXFlZ3h4c1RTWFNocDdnY2FSaERnUTozNDE=",
wait_for_completion_timeout: "30s",
body: null,
});
console.log(response);
----

View File

@ -0,0 +1,16 @@
// This file is autogenerated, DO NOT EDIT
// Use `node scripts/generate-docs-examples.js` to generate the docs examples
[source, js]
----
const response = await client.cluster.putSettings({
persistent: {
"cluster.indices.close.enable": false,
"indices.recovery.max_bytes_per_sec": "50mb",
},
transient: {
"*": null,
},
});
console.log(response);
----

View File

@ -45,7 +45,7 @@ console.log(response);
const response1 = await client.indices.putIndexTemplate({
name: 2,
index_patterns: ["k9s*"],
index_patterns: ["k8s*"],
composed_of: ["destination_template"],
data_stream: {},
});

View File

@ -1,23 +0,0 @@
// This file is autogenerated, DO NOT EDIT
// Use `node scripts/generate-docs-examples.js` to generate the docs examples
[source, js]
----
const response = await client.inference.put({
task_type: "rerank",
inference_id: "my-elastic-rerank",
inference_config: {
service: "elasticsearch",
service_settings: {
model_id: ".rerank-v1",
num_threads: 1,
adaptive_allocations: {
enabled: true,
min_number_of_allocations: 1,
max_number_of_allocations: 4,
},
},
},
});
console.log(response);
----

View File

@ -5,7 +5,7 @@
----
const response = await client.transport.request({
method: "GET",
path: "/_ingest/ip_location/database/my-database-id",
path: "/_ingest/geoip/database/my-database-id",
});
console.log(response);
----

View File

@ -3,9 +3,7 @@
[source, js]
----
const response = await client.transport.request({
method: "POST",
path: "/_query/async",
const response = await client.esql.asyncQuery({
body: {
query:
"\n FROM library\n | EVAL year = DATE_TRUNC(1 YEARS, release_date)\n | STATS MAX(page_count) BY year\n | SORT year\n | LIMIT 5\n ",

View File

@ -5,7 +5,7 @@
----
const response = await client.ingest.putPipeline({
id: "geoip",
description: "Add ip geolocation info",
description: "Add geoip info",
processors: [
{
geoip: {

View File

@ -3,9 +3,9 @@
[source, js]
----
const response = await client.transport.request({
method: "GET",
path: "/_query/async/FkpMRkJGS1gzVDRlM3g4ZzMyRGlLbkEaTXlJZHdNT09TU2VTZVBoNDM3cFZMUToxMDM&#x3D;",
const response = await client.esql.asyncQueryGet({
id: "FkpMRkJGS1gzVDRlM3g4ZzMyRGlLbkEaTXlJZHdNT09TU2VTZVBoNDM3cFZMUToxMDM=",
body: null,
});
console.log(response);
----

View File

@ -4,7 +4,7 @@
[source, js]
----
const response = await client.security.putRole({
name: "only_remote_access_role",
name: "role_with_remote_indices",
remote_indices: [
{
clusters: ["my_remote"],
@ -12,12 +12,6 @@ const response = await client.security.putRole({
privileges: ["read", "read_cross_cluster", "view_index_metadata"],
},
],
remote_cluster: [
{
clusters: ["my_remote"],
privileges: ["monitor_stats"],
},
],
});
console.log(response);
----

View File

@ -3,9 +3,9 @@
[source, js]
----
const response = await client.transport.request({
method: "POST",
path: "/_inference/completion/openai-completion/_stream",
const response = await client.inference.streamInference({
task_type: "completion",
inference_id: "openai-completion",
body: {
input: "What is Elastic?",
},

View File

@ -3,8 +3,8 @@
[source, js]
----
const response = await client.indices.rollover({
alias: "datastream",
const response = await client.cluster.getSettings({
flat_settings: "true",
});
console.log(response);
----

View File

@ -5,7 +5,7 @@
----
const response = await client.transport.request({
method: "DELETE",
path: "/_ingest/ip_location/database/example-database-id",
path: "/_ingest/geoip/database/example-database-id",
});
console.log(response);
----

View File

@ -1,18 +0,0 @@
// This file is autogenerated, DO NOT EDIT
// Use `node scripts/generate-docs-examples.js` to generate the docs examples
[source, js]
----
const response = await client.ingest.simulate({
id: "query_helper_pipeline",
docs: [
{
_source: {
content:
"artificial intelligence in medicine articles published in the last 12 months",
},
},
],
});
console.log(response);
----

View File

@ -3,9 +3,7 @@
[source, js]
----
const response = await client.transport.request({
method: "POST",
path: "/_security/oidc/prepare",
const response = await client.security.oidcPrepareAuthentication({
body: {
realm: "oidc1",
state: "lGYK0EcSLjqH6pkT5EVZjC6eIW5YCGgywj2sxROO",

View File

@ -4,7 +4,7 @@
[source, js]
----
const response = await client.indices.create({
index: "my-index-000003",
index: "my-index-000002",
mappings: {
properties: {
inference_field: {

View File

@ -3,12 +3,8 @@
[source, js]
----
const response = await client.transport.request({
method: "POST",
path: "/_query/async",
querystring: {
format: "json",
},
const response = await client.esql.asyncQuery({
format: "json",
body: {
query:
"\n FROM cluster_one:my-index*,cluster_two:logs*\n | STATS COUNT(http.response.status_code) BY user.id\n | LIMIT 2\n ",

View File

@ -11,7 +11,7 @@ const response = await client.searchApplication.put({
script: {
lang: "mustache",
source:
'\n {\n "query": {\n "bool": {\n "must": [\n {{#query}}\n {{/query}}\n ],\n "filter": {{#toJson}}_es_filters{{/toJson}}\n }\n },\n "_source": {\n "includes": ["title", "plot"]\n },\n "aggs": {{#toJson}}_es_aggs{{/toJson}},\n "from": {{from}},\n "size": {{size}},\n "sort": {{#toJson}}_es_sort_fields{{/toJson}}\n }\n ',
'\n {\n "query": {\n "bool": {\n "must": [\n {{#query}}\n \n {{/query}}\n ],\n "filter": {{#toJson}}_es_filters{{/toJson}}\n }\n },\n "_source": {\n "includes": ["title", "plot"]\n },\n "aggs": {{#toJson}}_es_aggs{{/toJson}},\n "from": {{from}},\n "size": {{size}},\n "sort": {{#toJson}}_es_sort_fields{{/toJson}}\n }\n ',
params: {
query: "",
_es_filters: {},

View File

@ -3,9 +3,7 @@
[source, js]
----
const response = await client.transport.request({
method: "POST",
path: "/_security/api_key/_bulk_update",
const response = await client.security.bulkUpdateApiKeys({
body: {
ids: ["VuaCfGcBCdbkQm-e5aOx", "H3_AhoIBA9hmeQJdg7ij"],
},

View File

@ -1,44 +0,0 @@
// This file is autogenerated, DO NOT EDIT
// Use `node scripts/generate-docs-examples.js` to generate the docs examples
[source, js]
----
const response = await client.search({
index: "retrievers_example",
retriever: {
rrf: {
retrievers: [
{
knn: {
field: "vector",
query_vector: [0.23, 0.67, 0.89],
k: 3,
num_candidates: 5,
},
},
{
text_similarity_reranker: {
retriever: {
standard: {
query: {
term: {
topic: "ai",
},
},
},
},
field: "text",
inference_id: "my-rerank-model",
inference_text:
"Can I use generative AI to identify user intent and improve search relevance?",
},
},
],
rank_window_size: 10,
rank_constant: 1,
},
},
_source: false,
});
console.log(response);
----

View File

@ -3,9 +3,7 @@
[source, js]
----
const response = await client.transport.request({
method: "POST",
path: "/_text_structure/find_message_structure",
const response = await client.textStructure.findMessageStructure({
body: {
messages: [
"[2024-03-05T10:52:36,256][INFO ][o.a.l.u.VectorUtilPanamaProvider] [laptop] Java vector incubator API enabled; uses preferredBitSize=128",

View File

@ -1,46 +0,0 @@
// This file is autogenerated, DO NOT EDIT
// Use `node scripts/generate-docs-examples.js` to generate the docs examples
[source, js]
----
const response = await client.search({
index: "retrievers_example",
retriever: {
text_similarity_reranker: {
retriever: {
rrf: {
retrievers: [
{
standard: {
query: {
query_string: {
query:
"(information retrieval) OR (artificial intelligence)",
default_field: "text",
},
},
},
},
{
knn: {
field: "vector",
query_vector: [0.23, 0.67, 0.89],
k: 3,
num_candidates: 5,
},
},
],
rank_window_size: 10,
rank_constant: 1,
},
},
field: "text",
inference_id: "my-rerank-model",
inference_text:
"What are the state of the art applications of AI in information retrieval?",
},
},
_source: false,
});
console.log(response);
----

View File

@ -1,23 +0,0 @@
// This file is autogenerated, DO NOT EDIT
// Use `node scripts/generate-docs-examples.js` to generate the docs examples
[source, js]
----
const response = await client.inference.put({
task_type: "rerank",
inference_id: "my-elastic-rerank",
inference_config: {
service: "elasticsearch",
service_settings: {
model_id: ".rerank-v1",
num_threads: 1,
adaptive_allocations: {
enabled: true,
min_number_of_allocations: 1,
max_number_of_allocations: 10,
},
},
},
});
console.log(response);
----

View File

@ -12,7 +12,7 @@ const response = await client.inference.put({
adaptive_allocations: {
enabled: true,
min_number_of_allocations: 1,
max_number_of_allocations: 4,
max_number_of_allocations: 10,
},
num_threads: 1,
model_id: ".elser_model_2",

View File

@ -1,34 +0,0 @@
// This file is autogenerated, DO NOT EDIT
// Use `node scripts/generate-docs-examples.js` to generate the docs examples
[source, js]
----
const response = await client.ingest.putPipeline({
id: "ip_location",
description: "Add ip geolocation info",
processors: [
{
ip_location: {
field: "ip",
},
},
],
});
console.log(response);
const response1 = await client.index({
index: "my-index-000001",
id: "my_id",
pipeline: "ip_location",
document: {
ip: "80.231.5.0",
},
});
console.log(response1);
const response2 = await client.get({
index: "my-index-000001",
id: "my_id",
});
console.log(response2);
----

View File

@ -3,9 +3,7 @@
[source, js]
----
const response = await client.transport.request({
method: "POST",
path: "/_security/api_key/_bulk_update",
const response = await client.security.bulkUpdateApiKeys({
body: {
ids: ["VuaCfGcBCdbkQm-e5aOx", "H3_AhoIBA9hmeQJdg7ij"],
role_descriptors: {

View File

@ -1,13 +0,0 @@
// This file is autogenerated, DO NOT EDIT
// Use `node scripts/generate-docs-examples.js` to generate the docs examples
[source, js]
----
const response = await client.connector.put({
connector_id: "my-{service-name-stub}-connector",
index_name: "my-elasticsearch-index",
name: "Content synced from {service-name}",
service_type: "{service-name-stub}",
});
console.log(response);
----

View File

@ -1,32 +0,0 @@
// This file is autogenerated, DO NOT EDIT
// Use `node scripts/generate-docs-examples.js` to generate the docs examples
[source, js]
----
const response = await client.ingest.putPipeline({
id: "query_helper_pipeline",
processors: [
{
script: {
source:
"ctx.prompt = 'Please generate an elasticsearch search query on index `articles_index` for the following natural language query. Dates are in the field `@timestamp`, document types are in the field `type` (options are `news`, `publication`), categories in the field `category` and can be multiple (options are `medicine`, `pharmaceuticals`, `technology`), and document names are in the field `title` which should use a fuzzy match. Ignore fields which cannot be determined from the natural language query context: ' + ctx.content",
},
},
{
inference: {
model_id: "openai_chat_completions",
input_output: {
input_field: "prompt",
output_field: "query",
},
},
},
{
remove: {
field: "prompt",
},
},
],
});
console.log(response);
----

View File

@ -1,18 +0,0 @@
// This file is autogenerated, DO NOT EDIT
// Use `node scripts/generate-docs-examples.js` to generate the docs examples
[source, js]
----
const response = await client.indices.create({
index: "my-index-000002",
mappings: {
properties: {
inference_field: {
type: "semantic_text",
inference_id: "my-openai-endpoint",
},
},
},
});
console.log(response);
----

View File

@ -1,37 +0,0 @@
// This file is autogenerated, DO NOT EDIT
// Use `node scripts/generate-docs-examples.js` to generate the docs examples
[source, js]
----
const response = await client.search({
index: "retrievers_example",
retriever: {
rrf: {
retrievers: [
{
standard: {
query: {
query_string: {
query: "(information retrieval) OR (artificial intelligence)",
default_field: "text",
},
},
},
},
{
knn: {
field: "vector",
query_vector: [0.23, 0.67, 0.89],
k: 3,
num_candidates: 5,
},
},
],
rank_window_size: 10,
rank_constant: 1,
},
},
_source: false,
});
console.log(response);
----

View File

@ -5,11 +5,11 @@
----
const response = await client.transport.request({
method: "PUT",
path: "/_ingest/ip_location/database/my-database-1",
path: "/_ingest/geoip/database/my-database-id",
body: {
name: "GeoIP2-Domain",
maxmind: {
account_id: "1234567",
account_id: "1025402",
},
},
});

View File

@ -3,9 +3,9 @@
[source, js]
----
const response = await client.transport.request({
method: "POST",
path: "/_application/analytics/my_analytics_collection/event/search_click",
const response = await client.searchApplication.postBehavioralAnalyticsEvent({
collection_name: "my_analytics_collection",
event_type: "search_click",
body: {
session: {
id: "1797ca95-91c9-4e2e-b1bd-9c38e6f386a9",

View File

@ -3,9 +3,7 @@
[source, js]
----
const response = await client.transport.request({
method: "POST",
path: "/_security/oidc/authenticate",
const response = await client.security.oidcAuthenticate({
body: {
redirect_uri:
"https://oidc-kibana.elastic.co:5603/api/security/oidc/callback?code=jtI3Ntt8v3_XvcLzCFGq&state=4dbrihtIAt3wBTwo6DxK-vdk-sSyDBV8Yf0AjdkdT5I",

View File

@ -3,9 +3,8 @@
[source, js]
----
const response = await client.transport.request({
method: "POST",
path: "/_application/search_application/my_search_application/_render_query",
const response = await client.searchApplication.renderQuery({
name: "my_search_application",
body: {
params: {
query_string: "rock climbing",

View File

@ -1,34 +0,0 @@
// This file is autogenerated, DO NOT EDIT
// Use `node scripts/generate-docs-examples.js` to generate the docs examples
[source, js]
----
const response = await client.ingest.putPipeline({
id: "ip_location",
description: "Add ip geolocation info",
processors: [
{
ip_location: {
field: "ip",
},
},
],
});
console.log(response);
const response1 = await client.index({
index: "my-index-000001",
id: "my_id",
pipeline: "ip_location",
document: {
ip: "89.160.20.128",
},
});
console.log(response1);
const response2 = await client.get({
index: "my-index-000001",
id: "my_id",
});
console.log(response2);
----

View File

@ -1,37 +0,0 @@
// This file is autogenerated, DO NOT EDIT
// Use `node scripts/generate-docs-examples.js` to generate the docs examples
[source, js]
----
const response = await client.search({
index: "retrievers_example",
retriever: {
text_similarity_reranker: {
retriever: {
text_similarity_reranker: {
retriever: {
knn: {
field: "vector",
query_vector: [0.23, 0.67, 0.89],
k: 3,
num_candidates: 5,
},
},
rank_window_size: 100,
field: "text",
inference_id: "my-rerank-model",
inference_text:
"What are the state of the art applications of AI in information retrieval?",
},
},
rank_window_size: 10,
field: "text",
inference_id: "my-other-more-expensive-rerank-model",
inference_text:
"Applications of Large Language Models in technology and their impact on user satisfaction",
},
},
_source: false,
});
console.log(response);
----

View File

@ -1,14 +0,0 @@
// This file is autogenerated, DO NOT EDIT
// Use `node scripts/generate-docs-examples.js` to generate the docs examples
[source, js]
----
const response = await client.connector.put({
connector_id: "my-{service-name-stub}-connector",
index_name: "my-elasticsearch-index",
name: "Content synced from {service-name}",
service_type: "{service-name-stub}",
is_native: true,
});
console.log(response);
----

View File

@ -3,9 +3,9 @@
[source, js]
----
const response = await client.transport.request({
method: "POST",
path: "/_application/search_application/my_search_application/_render_query",
const response = await client.searchApplication.renderQuery({
name: "my_search_application",
body: null,
});
console.log(response);
----

View File

@ -5,7 +5,7 @@
----
const response = await client.ingest.putPipeline({
id: "geoip",
description: "Add ip geolocation info",
description: "Add geoip info",
processors: [
{
geoip: {

View File

@ -208,13 +208,10 @@ const response = await client.bulk({
});
console.log(response);
const response1 = await client.transport.request({
method: "GET",
path: "/_text_structure/find_field_structure",
querystring: {
index: "test-logs",
field: "message",
},
const response1 = await client.textStructure.findFieldStructure({
index: "test-logs",
field: "message",
body: null,
});
console.log(response1);
----

View File

@ -1,45 +0,0 @@
// This file is autogenerated, DO NOT EDIT
// Use `node scripts/generate-docs-examples.js` to generate the docs examples
[source, js]
----
const response = await client.search({
index: "retrievers_example",
retriever: {
rrf: {
retrievers: [
{
standard: {
query: {
query_string: {
query: "(information retrieval) OR (artificial intelligence)",
default_field: "text",
},
},
},
},
{
knn: {
field: "vector",
query_vector: [0.23, 0.67, 0.89],
k: 3,
num_candidates: 5,
},
},
],
rank_window_size: 10,
rank_constant: 1,
},
},
highlight: {
fields: {
text: {
fragment_size: 150,
number_of_fragments: 3,
},
},
},
_source: false,
});
console.log(response);
----

View File

@ -3,9 +3,7 @@
[source, js]
----
const response = await client.transport.request({
method: "POST",
path: "/_ingest/_simulate",
const response = await client.simulate.ingest({
body: {
docs: [
{

View File

@ -1,83 +0,0 @@
// This file is autogenerated, DO NOT EDIT
// Use `node scripts/generate-docs-examples.js` to generate the docs examples
[source, js]
----
const response = await client.indices.create({
index: "retrievers_example_nested",
mappings: {
properties: {
nested_field: {
type: "nested",
properties: {
paragraph_id: {
type: "keyword",
},
nested_vector: {
type: "dense_vector",
dims: 3,
similarity: "l2_norm",
index: true,
},
},
},
topic: {
type: "keyword",
},
},
},
});
console.log(response);
const response1 = await client.index({
index: "retrievers_example_nested",
id: 1,
document: {
nested_field: [
{
paragraph_id: "1a",
nested_vector: [-1.12, -0.59, 0.78],
},
{
paragraph_id: "1b",
nested_vector: [-0.12, 1.56, 0.42],
},
{
paragraph_id: "1c",
nested_vector: [1, -1, 0],
},
],
topic: ["ai"],
},
});
console.log(response1);
const response2 = await client.index({
index: "retrievers_example_nested",
id: 2,
document: {
nested_field: [
{
paragraph_id: "2a",
nested_vector: [0.23, 1.24, 0.65],
},
],
topic: ["information_retrieval"],
},
});
console.log(response2);
const response3 = await client.index({
index: "retrievers_example_nested",
id: 3,
document: {
topic: ["ai"],
},
});
console.log(response3);
const response4 = await client.indices.refresh({
index: "retrievers_example_nested",
});
console.log(response4);
----

View File

@ -16,7 +16,7 @@ const response = await client.search({
},
},
field: "text",
inference_id: "my-elastic-rerank",
inference_id: "my-cohere-rerank-model",
inference_text: "How often does the moon hide the sun?",
rank_window_size: 100,
min_score: 0.5,

View File

@ -3,9 +3,7 @@
[source, js]
----
const response = await client.transport.request({
method: "POST",
path: "/_security/api_key/_bulk_update",
const response = await client.security.bulkUpdateApiKeys({
body: {
ids: ["VuaCfGcBCdbkQm-e5aOx", "H3_AhoIBA9hmeQJdg7ij"],
role_descriptors: {},

View File

@ -9,6 +9,7 @@ const response = await client.indices.create({
properties: {
content: {
type: "semantic_text",
inference_id: "my-elser-endpoint",
},
},
},

View File

@ -3,9 +3,7 @@
[source, js]
----
const response = await client.transport.request({
method: "POST",
path: "/_ingest/_simulate",
const response = await client.simulate.ingest({
body: {
docs: [
{

View File

@ -0,0 +1,11 @@
// This file is autogenerated, DO NOT EDIT
// Use `node scripts/generate-docs-examples.js` to generate the docs examples
[source, js]
----
const response = await client.cluster.getSettings({
flat_settings: "true",
filter_path: "transient",
});
console.log(response);
----

View File

@ -1,24 +0,0 @@
// This file is autogenerated, DO NOT EDIT
// Use `node scripts/generate-docs-examples.js` to generate the docs examples
[source, js]
----
const response = await client.indices.create({
index: "test-index",
query: {
semantic: {
field: "my_semantic_field",
},
},
highlight: {
fields: {
my_semantic_field: {
type: "semantic",
number_of_fragments: 2,
order: "score",
},
},
},
});
console.log(response);
----

View File

@ -3,9 +3,7 @@
[source, js]
----
const response = await client.transport.request({
method: "POST",
path: "/_security/oidc/prepare",
const response = await client.security.oidcPrepareAuthentication({
body: {
iss: "http://127.0.0.1:8080",
login_hint: "this_is_an_opaque_string",

View File

@ -5,7 +5,7 @@
----
const response = await client.ingest.putPipeline({
id: "geoip",
description: "Add ip geolocation info",
description: "Add geoip info",
processors: [
{
geoip: {

View File

@ -1,154 +0,0 @@
// This file is autogenerated, DO NOT EDIT
// Use `node scripts/generate-docs-examples.js` to generate the docs examples
[source, js]
----
const response = await client.indices.putIndexTemplate({
name: "datastream_template",
index_patterns: ["datastream*"],
data_stream: {},
template: {
lifecycle: {
downsampling: [
{
after: "1m",
fixed_interval: "1h",
},
],
},
settings: {
index: {
mode: "time_series",
},
},
mappings: {
properties: {
"@timestamp": {
type: "date",
},
kubernetes: {
properties: {
container: {
properties: {
cpu: {
properties: {
usage: {
properties: {
core: {
properties: {
ns: {
type: "long",
},
},
},
limit: {
properties: {
pct: {
type: "float",
},
},
},
nanocores: {
type: "long",
time_series_metric: "gauge",
},
node: {
properties: {
pct: {
type: "float",
},
},
},
},
},
},
},
memory: {
properties: {
available: {
properties: {
bytes: {
type: "long",
time_series_metric: "gauge",
},
},
},
majorpagefaults: {
type: "long",
},
pagefaults: {
type: "long",
time_series_metric: "gauge",
},
rss: {
properties: {
bytes: {
type: "long",
time_series_metric: "gauge",
},
},
},
usage: {
properties: {
bytes: {
type: "long",
time_series_metric: "gauge",
},
limit: {
properties: {
pct: {
type: "float",
},
},
},
node: {
properties: {
pct: {
type: "float",
},
},
},
},
},
workingset: {
properties: {
bytes: {
type: "long",
time_series_metric: "gauge",
},
},
},
},
},
name: {
type: "keyword",
},
start_time: {
type: "date",
},
},
},
host: {
type: "keyword",
time_series_dimension: true,
},
namespace: {
type: "keyword",
time_series_dimension: true,
},
node: {
type: "keyword",
time_series_dimension: true,
},
pod: {
type: "keyword",
time_series_dimension: true,
},
},
},
},
},
},
});
console.log(response);
----

View File

@ -1,15 +0,0 @@
// This file is autogenerated, DO NOT EDIT
// Use `node scripts/generate-docs-examples.js` to generate the docs examples
[source, js]
----
const response = await client.transport.request({
method: "PUT",
path: "/_ingest/ip_location/database/my-database-2",
body: {
name: "standard_location",
ipinfo: {},
},
});
console.log(response);
----

View File

@ -3,9 +3,6 @@
[source, js]
----
const response = await client.transport.request({
method: "GET",
path: "/_security/settings",
});
const response = await client.security.getSettings();
console.log(response);
----

View File

@ -1,16 +0,0 @@
// This file is autogenerated, DO NOT EDIT
// Use `node scripts/generate-docs-examples.js` to generate the docs examples
[source, js]
----
const response = await client.transport.request({
method: "POST",
path: "/_query_rules/my-ruleset/_test",
body: {
match_criteria: {
query_string: "puggles",
},
},
});
console.log(response);
----

View File

@ -3,9 +3,7 @@
[source, js]
----
const response = await client.transport.request({
method: "POST",
path: "/_security/oidc/prepare",
const response = await client.security.oidcPrepareAuthentication({
body: {
realm: "oidc1",
},

View File

@ -1,36 +0,0 @@
// This file is autogenerated, DO NOT EDIT
// Use `node scripts/generate-docs-examples.js` to generate the docs examples
[source, js]
----
const response = await client.ingest.putPipeline({
id: "ip_location",
description: "Add ip geolocation info",
processors: [
{
ip_location: {
field: "ip",
target_field: "geo",
database_file: "GeoLite2-Country.mmdb",
},
},
],
});
console.log(response);
const response1 = await client.index({
index: "my-index-000001",
id: "my_id",
pipeline: "ip_location",
document: {
ip: "89.160.20.128",
},
});
console.log(response1);
const response2 = await client.get({
index: "my-index-000001",
id: "my_id",
});
console.log(response2);
----

View File

@ -3,9 +3,7 @@
[source, js]
----
const response = await client.transport.request({
method: "POST",
path: "/_ingest/_simulate",
const response = await client.simulate.ingest({
body: {
docs: [
{

View File

@ -1,44 +0,0 @@
// This file is autogenerated, DO NOT EDIT
// Use `node scripts/generate-docs-examples.js` to generate the docs examples
[source, js]
----
const response = await client.search({
index: "retrievers_example",
retriever: {
rrf: {
retrievers: [
{
standard: {
query: {
query_string: {
query: "(information retrieval) OR (artificial intelligence)",
default_field: "text",
},
},
},
},
{
knn: {
field: "vector",
query_vector: [0.23, 0.67, 0.89],
k: 3,
num_candidates: 5,
},
},
],
rank_window_size: 10,
rank_constant: 1,
},
},
collapse: {
field: "year",
inner_hits: {
name: "topic related documents",
_source: ["year"],
},
},
_source: false,
});
console.log(response);
----

View File

@ -1,94 +0,0 @@
// This file is autogenerated, DO NOT EDIT
// Use `node scripts/generate-docs-examples.js` to generate the docs examples
[source, js]
----
const response = await client.indices.create({
index: "retrievers_example",
mappings: {
properties: {
vector: {
type: "dense_vector",
dims: 3,
similarity: "l2_norm",
index: true,
},
text: {
type: "text",
},
year: {
type: "integer",
},
topic: {
type: "keyword",
},
},
},
});
console.log(response);
const response1 = await client.index({
index: "retrievers_example",
id: 1,
document: {
vector: [0.23, 0.67, 0.89],
text: "Large language models are revolutionizing information retrieval by boosting search precision, deepening contextual understanding, and reshaping user experiences in data-rich environments.",
year: 2024,
topic: ["llm", "ai", "information_retrieval"],
},
});
console.log(response1);
const response2 = await client.index({
index: "retrievers_example",
id: 2,
document: {
vector: [0.12, 0.56, 0.78],
text: "Artificial intelligence is transforming medicine, from advancing diagnostics and tailoring treatment plans to empowering predictive patient care for improved health outcomes.",
year: 2023,
topic: ["ai", "medicine"],
},
});
console.log(response2);
const response3 = await client.index({
index: "retrievers_example",
id: 3,
document: {
vector: [0.45, 0.32, 0.91],
text: "AI is redefining security by enabling advanced threat detection, proactive risk analysis, and dynamic defenses against increasingly sophisticated cyber threats.",
year: 2024,
topic: ["ai", "security"],
},
});
console.log(response3);
const response4 = await client.index({
index: "retrievers_example",
id: 4,
document: {
vector: [0.34, 0.21, 0.98],
text: "Elastic introduces Elastic AI Assistant, the open, generative AI sidekick powered by ESRE to democratize cybersecurity and enable users of every skill level.",
year: 2023,
topic: ["ai", "elastic", "assistant"],
},
});
console.log(response4);
const response5 = await client.index({
index: "retrievers_example",
id: 5,
document: {
vector: [0.11, 0.65, 0.47],
text: "Learn how to spin up a deployment of our hosted Elasticsearch Service and use Elastic Observability to gain deeper insight into the behavior of your applications and systems.",
year: 2024,
topic: ["documentation", "observability", "elastic"],
},
});
console.log(response5);
const response6 = await client.indices.refresh({
index: "retrievers_example",
});
console.log(response6);
----

View File

@ -3,9 +3,7 @@
[source, js]
----
const response = await client.transport.request({
method: "POST",
path: "/_connector/_secret",
const response = await client.connector.secretPost({
body: {
value: "encoded_api_key",
},

File diff suppressed because it is too large Load Diff

View File

@ -1,20 +1,17 @@
{
"name": "@elastic/elasticsearch",
"version": "9.0.0-alpha.1",
"versionCanary": "9.0.0-canary.0",
"version": "8.16.0",
"versionCanary": "8.16.0-canary.0",
"description": "The official Elasticsearch client for Node.js",
"main": "./index.js",
"types": "index.d.ts",
"exports": {
"require": "./index.js",
"import": "./index.js",
"types": "./index.d.ts"
"require": "./index.js"
},
"scripts": {
"test": "npm run build && npm run lint && tap",
"test:unit": "npm run build && tap",
"test:unit-bun": "bun run build && bunx tap",
"test:esm": "npm run build && cd test/esm/ && npm install && node test-import.mjs",
"test:coverage-100": "npm run build && tap --coverage --100",
"test:coverage-report": "npm run build && tap --coverage && nyc report --reporter=text-lcov > coverage.lcov",
"test:coverage-ui": "npm run build && tap --coverage --coverage-report=html",
@ -56,38 +53,38 @@
"node": ">=18"
},
"devDependencies": {
"@elastic/request-converter": "8.17.0",
"@sinonjs/fake-timers": "github:sinonjs/fake-timers#48f089f",
"@types/debug": "4.1.12",
"@types/ms": "0.7.34",
"@types/node": "22.10.1",
"@types/sinonjs__fake-timers": "8.1.5",
"@types/split2": "4.2.3",
"@types/stoppable": "1.1.3",
"@types/tap": "15.0.12",
"chai": "5.1.2",
"cross-zip": "4.0.1",
"desm": "1.3.1",
"into-stream": "8.0.1",
"js-yaml": "4.1.0",
"license-checker": "25.0.1",
"minimist": "1.2.8",
"ms": "2.1.3",
"node-abort-controller": "3.1.1",
"node-fetch": "2.7.0",
"ora": "5.4.1",
"proxy": "1.0.2",
"rimraf": "3.0.2",
"semver": "7.6.3",
"split2": "4.2.0",
"stoppable": "1.1.0",
"tap": "21.0.1",
"ts-node": "10.9.2",
"ts-standard": "12.0.2",
"typescript": "5.7.2",
"workq": "3.0.0",
"xmlbuilder2": "3.1.1",
"zx": "7.2.3"
"@elastic/request-converter": "^8.16.1",
"@sinonjs/fake-timers": "github:sinonjs/fake-timers#0bfffc1",
"@types/debug": "^4.1.7",
"@types/ms": "^0.7.31",
"@types/node": "^18.19.55",
"@types/sinonjs__fake-timers": "^8.1.2",
"@types/split2": "^3.2.1",
"@types/stoppable": "^1.1.1",
"@types/tap": "^15.0.7",
"chai": "^4.3.7",
"cross-zip": "^4.0.0",
"desm": "^1.2.0",
"into-stream": "^7.0.0",
"js-yaml": "^4.1.0",
"license-checker": "^25.0.1",
"minimist": "^1.2.6",
"ms": "^2.1.3",
"node-abort-controller": "^3.0.1",
"node-fetch": "^2.6.7",
"ora": "^5.4.1",
"proxy": "^1.0.2",
"rimraf": "^3.0.2",
"semver": "^7.3.7",
"split2": "^4.1.0",
"stoppable": "^1.1.0",
"tap": "^21.0.1",
"ts-node": "^10.7.0",
"ts-standard": "^12.0.2",
"typescript": "^4.6.4",
"workq": "^3.0.0",
"xmlbuilder2": "^3.0.2",
"zx": "^7.2.2"
},
"dependencies": {
"@elastic/transport": "^8.9.1",

View File

@ -1,29 +0,0 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
"local>elastic/renovate-config"
],
"schedule": [
"* * * * 0"
],
"packageRules": [
{
"matchDepTypes": [
"devDependencies"
],
"automerge": true,
"labels": [
"backport 8.x"
]
},
{
"matchPackageNames": [
"node"
],
"matchManagers": [
"dockerfile"
],
"enabled": false
}
]
}

View File

@ -1,54 +0,0 @@
import { Client } from '../..'
const client = new Client({
node: 'http://localhost:9200',
auth: { username: 'elastic', password: 'changeme' }
})
async function doThings () {
// should get fixed by codemod
await client.closePointInTime({
body: {
id: 'foobar'
}
})
await client.asyncSearch.get({
// @ts-expect-error should get fixed by codemod
body: {
id: 'foo'
}
})
// @ts-expect-error should get fixed by codemod
await client.create({
id: 'foo',
body: { index: 'my-index' }
})
await client.watcher.putWatch({
id: 'foo',
active: true
})
const body = { id: 'foo' }
// @ts-expect-error should get fixed by codemod
await client.asyncSearch.get({ body })
await client.asyncSearch.get(body)
const request = { body }
// @ts-expect-error should get fixed by codemod
await client.asyncSearch.get(request)
const request2 = body
await client.closePointInTime(request2)
// some non-client calls
const x = Math.random()
console.log(x)
console.log({ body: 'foo' })
}
doThings()
.then(() => console.log('done'))
.catch(() => console.error('uh oh'))

View File

@ -1,172 +0,0 @@
/*
* 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.
*/
import ts from 'typescript'
import path from 'node:path'
import minimist from 'minimist'
const apis = [
'asyncSearch',
'autoscaling',
'bulk',
'capabilities',
'cat',
'ccr',
'clearScroll',
'closePointInTime',
'cluster',
'connector',
'count',
'create',
'danglingIndices',
'delete',
'deleteByQuery',
'deleteByQueryRethrottle',
'deleteScript',
'enrich',
'eql',
'esql',
'exists',
'existsSource',
'explain',
'features',
'fieldCaps',
]
/**
* Detects whether a node is a `Client` instance identifier
* @remarks Uses duck-typing by checking that several Elasticsearch APIs exist as members on the identifier
*/
function isClient(node: ts.Identifier) {
const type = checker.getTypeAtLocation(node)
const properties = type.getProperties().map(prop => prop.escapedName.toString())
for (const api of apis) {
if (!properties.includes(api)) return false
}
return true
}
/**
* Returns true if the call expression node is running a client API function, otherwise false
*/
function isClientExpression(node: ts.CallExpression): boolean {
let flag = false
function visitIdentifiers(node: ts.Node) {
if (ts.isIdentifier(node) && isClient(node)) {
flag = true
return
}
ts.forEachChild(node, visitIdentifiers)
}
visitIdentifiers(node)
return flag
}
/**
* Returns an array of all call expressions to `Client` functions
*/
function collectClientCallExpressions(node: ts.SourceFile): ts.CallExpression[] {
const clientExpressions: ts.CallExpression[] = []
// recurse through all child nodes looking for `Client` call expressions
function collect(node: ts.Node) {
if (ts.isCallExpression(node)) {
// look for client identifier
if (isClientExpression(node)) {
clientExpressions.push(node)
}
}
ts.forEachChild(node, collect)
}
ts.forEachChild(node, collect)
return clientExpressions
}
function fixBodyProp(sourceFile: ts.SourceFile, node: ts.Node) {
if (ts.isObjectLiteralExpression(node)) {
// @ts-expect-error need to cast `prop` to a more specific type
const prop = node.properties.find(prop => prop.name.escapedText === 'body')
if (prop != null) {
console.log('// needs fix:')
console.log(sourceFile?.text.slice(node.pos, node.end))
// TODO: fix { body: value }
// TODO: fix { body: { ... } }
// TODO: fix { body }
}
} else if (ts.isIdentifier(node)) {
// @ts-expect-error
if (node.flowNode.antecedent?.node != null) {
// @ts-expect-error
fixBodyProp(sourceFile, node.flowNode.antecedent.node)
} else {
// console.log('uh oh')
// console.log(sourceFile?.text.slice(node.pos, node.end))
}
} else {
// @ts-expect-error
if (node.flowNode?.antecedent?.node != null) {
// console.log('two')
// @ts-expect-error
fixBodyProp(sourceFile, node.flowNode.antecedent.node)
} else {
// console.log('something else')
// console.log(node.kind)
// console.log(sourceFile?.text.slice(node.pos, node.end))
}
}
return false
}
function lookForBodyProp(sourceFile: ts.SourceFile, node: ts.CallExpression) {
if (node.arguments.length === 0) return
const first = node.arguments[0]
fixBodyProp(sourceFile, first)
}
// build TS project from provided file names
const args = minimist(process.argv.slice(2))
const cwd = process.cwd()
const files = args._.map(file => path.join(cwd, file))
const program = ts.createProgram(files, {})
const checker = program.getTypeChecker()
let processed = 0
program.getSourceFiles().forEach(sourceFile => {
if (program.isSourceFileFromExternalLibrary(sourceFile)) return
const { fileName } = sourceFile
try {
// get all `Client` call expressions
const exprs = collectClientCallExpressions(sourceFile)
if (exprs.length > 0) {
console.log(`found ${exprs.length} Client expressions in ${fileName}`)
}
// for each call expression, get the first function argument, determine if it's an object and whether it has a `body` key
exprs.forEach(expr => lookForBodyProp(sourceFile, expr))
} catch (e) {
// continue
console.error(`Could not process ${fileName}: ${e}`)
}
processed++
})
console.log(`Done scanning ${processed} files`)

143
scripts/generate.js Normal file
View File

@ -0,0 +1,143 @@
/*
* 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 { join } = require('path')
const { readdirSync, writeFileSync, readFileSync } = require('fs')
const minimist = require('minimist')
const ora = require('ora')
const rimraf = require('rimraf')
const standard = require('standard')
const downloadArtifacts = require('./download-artifacts')
const {
generate,
genFactory,
generateDocs,
generateRequestTypes
} = require('./utils')
start(minimist(process.argv.slice(2), {
string: ['version', 'hash']
}))
function start (opts) {
if (opts.version == null) {
console.error('Missing version parameter')
process.exit(1)
}
const packageFolder = join(__dirname, '..', 'api')
const apiOutputFolder = join(packageFolder, 'api')
const mainOutputFile = join(packageFolder, 'index.js')
const docOutputFile = join(__dirname, '..', 'docs', 'reference.asciidoc')
const typeDefFile = join(__dirname, '..', 'index.d.ts')
const requestParamsOutputFile = join(packageFolder, 'requestParams.d.ts')
let log
downloadArtifacts({ version: opts.version, hash: opts.hash })
.then(onArtifactsDownloaded)
.catch(err => {
console.log(err)
process.exit(1)
})
function onArtifactsDownloaded () {
log = ora('Generating APIs').start()
log.text = 'Cleaning API folder...'
rimraf.sync(join(apiOutputFolder, '*.js'))
const allSpec = readdirSync(downloadArtifacts.locations.specFolder)
.filter(file => file !== '_common.json')
.filter(file => !file.includes('deprecated'))
.sort()
.map(file => require(join(downloadArtifacts.locations.specFolder, file)))
const namespaces = namespacify(readdirSync(downloadArtifacts.locations.specFolder))
for (const namespace in namespaces) {
if (namespace === '_common') continue
const code = generate(namespace, namespaces[namespace], downloadArtifacts.locations.specFolder, opts.version)
const filePath = join(apiOutputFolder, `${namespace}.js`)
writeFileSync(filePath, code, { encoding: 'utf8' })
}
writeFileSync(
requestParamsOutputFile,
generateRequestTypes(opts.version, allSpec),
{ encoding: 'utf8' }
)
const { fn: factory, types } = genFactory(apiOutputFolder, downloadArtifacts.locations.specFolder, namespaces)
writeFileSync(
mainOutputFile,
factory,
{ encoding: 'utf8' }
)
const oldTypeDefString = readFileSync(typeDefFile, 'utf8')
const start = oldTypeDefString.indexOf('/* GENERATED */')
const end = oldTypeDefString.indexOf('/* /GENERATED */')
const newTypeDefString = oldTypeDefString.slice(0, start + 15) + '\n' + types + '\n ' + oldTypeDefString.slice(end)
writeFileSync(
typeDefFile,
newTypeDefString,
{ encoding: 'utf8' }
)
lintFiles(log, () => {
log.text = 'Generating documentation'
writeFileSync(
docOutputFile,
generateDocs(require(join(downloadArtifacts.locations.specFolder, '_common.json')), allSpec),
{ encoding: 'utf8' }
)
log.succeed('Done!')
})
}
function lintFiles (log, cb) {
log.text = 'Linting...'
const files = [join(packageFolder, '*.js'), join(apiOutputFolder, '*.js')]
standard.lintFiles(files, { fix: true }, err => {
if (err) {
return log.fail(err.message)
}
cb()
})
}
function namespacify (apis) {
return apis
.map(api => api.slice(0, -5))
.filter(api => api !== '_common')
.filter(api => !api.includes('deprecated'))
.reduce((acc, val) => {
if (val.includes('.')) {
val = val.split('.')
acc[val[0]] = acc[val[0]] || []
acc[val[0]].push(val[1])
} else {
acc[val] = []
}
return acc
}, {})
}
}

8
scripts/kibana-docker.sh Executable file
View File

@ -0,0 +1,8 @@
#!/bin/bash
exec docker run \
--rm \
-e ELASTICSEARCH_URL="http://elasticsearch:9200" \
-p 5601:5601 \
--network=elastic \
docker.elastic.co/kibana/kibana:7.0.0-beta1

139
scripts/utils/clone-es.js Normal file
View File

@ -0,0 +1,139 @@
/*
* 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 { accessSync, mkdirSync } = require('fs')
const { join } = require('path')
const Git = require('simple-git')
const esRepo = 'https://github.com/elastic/elasticsearch.git'
const esFolder = join(__dirname, '..', '..', 'elasticsearch')
const apiFolder = join(esFolder, 'rest-api-spec', 'src', 'main', 'resources', 'rest-api-spec', 'api')
const xPackFolder = join(esFolder, 'x-pack', 'plugin', 'src', 'test', 'resources', 'rest-api-spec', 'api')
function cloneAndCheckout (opts, callback) {
const { log, tag, branch } = opts
withTag(tag, callback)
/**
* Sets the elasticsearch repository to the given tag.
* If the repository is not present in `esFolder` it will
* clone the repository and the checkout the tag.
* If the repository is already present but it cannot checkout to
* the given tag, it will perform a pull and then try again.
* @param {string} tag
* @param {function} callback
*/
function withTag (tag, callback) {
let fresh = false
let retry = 0
if (!pathExist(esFolder)) {
if (!createFolder(esFolder)) {
log.fail('Failed folder creation')
return
}
fresh = true
}
const git = Git(esFolder)
if (fresh) {
clone(checkout)
} else if (opts.branch) {
checkout(true)
} else {
checkout()
}
function checkout (alsoPull = false) {
if (branch) {
log.text = `Checking out branch '${branch}'`
} else {
log.text = `Checking out tag '${tag}'`
}
git.checkout(branch || tag, err => {
if (err) {
if (retry++ > 0) {
callback(new Error(`Cannot checkout tag '${tag}'`), { apiFolder, xPackFolder })
return
}
return pull(checkout)
}
if (alsoPull) {
return pull(checkout)
}
callback(null, { apiFolder, xPackFolder })
})
}
function pull (cb) {
log.text = 'Pulling elasticsearch repository...'
git.pull(err => {
if (err) {
callback(err, { apiFolder, xPackFolder })
return
}
cb()
})
}
function clone (cb) {
log.text = 'Cloning elasticsearch repository...'
git.clone(esRepo, esFolder, err => {
if (err) {
callback(err, { apiFolder, xPackFolder })
return
}
cb()
})
}
}
/**
* 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
*/
function createFolder (name) {
try {
mkdirSync(name)
return true
} catch (err) {
return false
}
}
}
module.exports = cloneAndCheckout

View File

@ -0,0 +1,553 @@
/*
* 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.
*/
/* eslint camelcase: 0 */
'use strict'
const { join } = require('path')
const dedent = require('dedent')
const allowedMethods = {
noBody: ['GET', 'HEAD', 'DELETE'],
body: ['POST', 'PUT', 'DELETE']
}
// if a parameter is depracted in a minor release
// we should be able to support it until the next major
const deprecatedParameters = require('./patch.json')
// list of apis that does not need any kind of validation
// because of how the url is built or the `type` handling in ES7
const noPathValidation = [
'create',
'exists',
'explain',
'get',
'get_source',
'index',
'indices.get_alias',
'indices.exists_alias',
'indices.get_field_mapping',
'indices.get_mapping',
'indices.get_settings',
'indices.put_mapping',
'indices.stats',
'delete',
'nodes.info',
'nodes.stats',
'nodes.usage',
'tasks.cancel',
'termvectors',
'update'
]
function generateNamespace (namespace, nested, specFolder, version) {
const common = require(join(specFolder, '_common.json'))
let code = dedent`
/*
* 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'
/* eslint camelcase: 0 */
/* eslint no-unused-vars: 0 */
const { handleError, snakeCaseKeys, normalizeArguments, kConfigurationError } = require('../utils')
`
if (nested.length > 0) {
let getters = ''
for (const n of nested) {
if (n.includes('_')) {
const nameSnaked = n
.replace(/\.([a-z])/g, k => k[1].toUpperCase())
.replace(/_([a-z])/g, k => k[1].toUpperCase())
getters += `${n}: { get () { return this.${nameSnaked} } },\n`
}
}
const api = generateMultiApi(version, namespace, nested, common, specFolder)
if (getters.length > 0) {
getters = `Object.defineProperties(${api.namespace}Api.prototype, {\n${getters}})`
}
code += `
const acceptedQuerystring = ${JSON.stringify(api.acceptedQuerystring)}
const snakeCase = ${JSON.stringify(api.snakeCase)}
function ${api.namespace}Api (transport, ConfigurationError) {
this.transport = transport
this[kConfigurationError] = ConfigurationError
}
${api.code}
${getters}
module.exports = ${api.namespace}Api
`
} else {
const spec = require(join(specFolder, `${namespace}.json`))
const api = generateSingleApi(version, spec, common)
code += `
const acceptedQuerystring = ${JSON.stringify(api.acceptedQuerystring)}
const snakeCase = ${JSON.stringify(api.snakeCase)}
${api.code}
module.exports = ${api.name}Api
`
}
return code
}
function generateMultiApi (version, namespace, nested, common, specFolder) {
const namespaceSnaked = namespace
.replace(/\.([a-z])/g, k => k[1].toUpperCase())
.replace(/_([a-z])/g, k => k[1].toUpperCase())
let code = ''
const snakeCase = {}
const acceptedQuerystring = []
for (const n of nested) {
const nameSnaked = n
.replace(/\.([a-z])/g, k => k[1].toUpperCase())
.replace(/_([a-z])/g, k => k[1].toUpperCase())
const spec = require(join(specFolder, `${namespace}.${n}.json`))
const api = generateSingleApi(version, spec, common)
code += `${Uppercase(namespaceSnaked)}Api.prototype.${nameSnaked} = ${api.code}\n\n`
Object.assign(snakeCase, api.snakeCase)
for (const q of api.acceptedQuerystring) {
if (!acceptedQuerystring.includes(q)) {
acceptedQuerystring.push(q)
}
}
}
return { code, snakeCase, acceptedQuerystring, namespace: Uppercase(namespaceSnaked) }
}
function generateSingleApi (version, spec, common) {
const release = version.charAt(0)
const api = Object.keys(spec)[0]
const name = api
.replace(/\.([a-z])/g, k => k[1].toUpperCase())
.replace(/_([a-z])/g, k => k[1].toUpperCase())
const { paths } = spec[api].url
const { params } = spec[api]
const acceptedQuerystring = []
const required = []
const methods = paths.reduce((acc, val) => {
for (const method of val.methods) {
if (!acc.includes(method)) acc.push(method)
}
return acc
}, [])
const parts = paths.reduce((acc, val) => {
if (!val.parts) return acc
for (const part of Object.keys(val.parts)) {
if (!acc.includes(part)) acc.push(part)
}
return acc
}, [])
// get the required parts from the url
// if the url has at least one static path,
// then there are not required parts of the url
let allParts = []
for (const path of paths) {
if (path.parts) {
allParts.push(Object.keys(path.parts))
} else {
allParts = []
break
}
}
if (allParts.length > 0) {
intersect(...allParts).forEach(r => required.push(r))
}
for (const key in params) {
if (params[key].required) {
required.push(key)
}
acceptedQuerystring.push(key)
if (deprecatedParameters[release] && deprecatedParameters[release][key]) {
acceptedQuerystring.push(deprecatedParameters[release][key])
}
}
for (const key in spec[api]) {
const k = spec[api][key]
if (k && k.required) {
required.push(key)
}
}
if (common && common.params) {
for (const key in common.params) {
acceptedQuerystring.push(key)
}
}
const code = `
function ${name}Api (params, options, callback) {
;[params, options, callback] = normalizeArguments(params, options, callback)
${genRequiredChecks()}
${genUrlValidation(paths, api)}
let { ${genQueryDenylist(false)}, ...querystring } = params
querystring = snakeCaseKeys(acceptedQuerystring, snakeCase, querystring)
let path = ''
${buildPath()}
// build request object
const request = {
method,
path,
${genBody(api, methods, spec[api].body, spec)}
querystring
}
return this.transport.request(request, options, callback)
}
`.trim() // always call trim to avoid newlines
return {
name,
code,
acceptedQuerystring: acceptedQuerystring,
snakeCase: genSnakeCaseMap(),
documentation: generateDocumentation(spec[api], api)
}
function genRequiredChecks () {
const code = required
.map(_genRequiredCheck)
.concat(_noBody())
.filter(Boolean)
if (code.length) {
code.unshift('// check required parameters')
}
return code.join('\n ')
function _genRequiredCheck (param) {
const camelCased = param[0] === '_'
? '_' + param.slice(1).replace(/_([a-z])/g, k => k[1].toUpperCase())
: param.replace(/_([a-z])/g, k => k[1].toUpperCase())
if (param === camelCased) {
const check = `
if (params['${param}'] == null) {
const err = new this[kConfigurationError]('Missing required parameter: ${param}')
return handleError(err, callback)
}
`
return check.trim()
} else {
const check = `
if (params['${param}'] == null && params['${camelCased}'] == null) {
const err = new this[kConfigurationError]('Missing required parameter: ${param} or ${camelCased}')
return handleError(err, callback)
}
`
return check.trim()
}
}
function _noBody () {
const check = `
if (params.body != null) {
const err = new this[kConfigurationError]('This API does not require a body')
return handleError(err, callback)
}
`
return spec[api].body === null ? check.trim() : ''
}
}
function genSnakeCaseMap () {
const toCamelCase = str => {
return str[0] === '_'
? '_' + str.slice(1).replace(/_([a-z])/g, k => k[1].toUpperCase())
: str.replace(/_([a-z])/g, k => k[1].toUpperCase())
}
return acceptedQuerystring.reduce((acc, val, index) => {
if (toCamelCase(val) !== val) {
acc[toCamelCase(val)] = val
}
return acc
}, {})
}
function genQueryDenylist (addQuotes = true) {
const toCamelCase = str => {
return str[0] === '_'
? '_' + str.slice(1).replace(/_([a-z])/g, k => k[1].toUpperCase())
: str.replace(/_([a-z])/g, k => k[1].toUpperCase())
}
const denylist = ['method', 'body']
parts.forEach(p => {
const camelStr = toCamelCase(p)
if (camelStr !== p) denylist.push(`${camelStr}`)
denylist.push(`${p}`)
})
return addQuotes ? denylist.map(q => `'${q}'`) : denylist
}
function buildPath () {
const toCamelCase = str => {
return str[0] === '_'
? '_' + str.slice(1).replace(/_([a-z])/g, k => k[1].toUpperCase())
: str.replace(/_([a-z])/g, k => k[1].toUpperCase())
}
const genAccessKey = str => {
const camelStr = toCamelCase(str)
return camelStr === str
? str
: `${str} || ${camelStr}`
}
const genCheck = path => {
return path
.split('/')
.filter(Boolean)
.map(p => p.startsWith('{') ? `(${genAccessKey(p.slice(1, -1))}) != null` : false)
.filter(Boolean)
.join(' && ')
}
const genPath = path => {
path = path
.split('/')
.filter(Boolean)
.map(p => p.startsWith('{') ? `encodeURIComponent(${genAccessKey(p.slice(1, -1))})` : `'${p}'`)
.join(' + \'/\' + ')
return path.length > 0 ? ('\'/\' + ' + path) : '\'/\''
}
let hasStaticPath = false
let sortedPaths = paths
// some legacy API have mutliple statis paths
// this filter removes them
.filter(p => {
if (p.path.includes('{')) return true
if (hasStaticPath === false && p.deprecated == null) {
hasStaticPath = true
return true
}
return false
})
// sort by number of parameters (desc)
.sort((a, b) => Object.keys(b.parts || {}).length - Object.keys(a.parts || {}).length)
const allDeprecated = paths.filter(path => path.deprecated != null)
if (allDeprecated.length === paths.length) sortedPaths = [paths[0]]
let code = ''
for (let i = 0; i < sortedPaths.length; i++) {
const { path, methods } = sortedPaths[i]
if (sortedPaths.length === 1) {
code += `if (method == null) method = ${generatePickMethod(methods)}
path = ${genPath(path)}
`
} else if (i === 0) {
code += `if (${genCheck(path)}) {
if (method == null) method = ${generatePickMethod(methods)}
path = ${genPath(path)}
}
`
} else if (i === sortedPaths.length - 1) {
code += ` else {
if (method == null) method = ${generatePickMethod(methods)}
path = ${genPath(path)}
}
`
} else {
code += ` else if (${genCheck(path)}) {
if (method == null) method = ${generatePickMethod(methods)}
path = ${genPath(path)}
}
`
}
}
return code
}
}
function generatePickMethod (methods) {
if (methods.length === 1) {
return `'${methods[0]}'`
}
const bodyMethod = getBodyMethod(methods)
const noBodyMethod = getNoBodyMethod(methods)
if (bodyMethod && noBodyMethod) {
return `body == null ? '${noBodyMethod}' : '${bodyMethod}'`
} else if (bodyMethod) {
return `'${bodyMethod}'`
} else {
return `'${noBodyMethod}'`
}
}
function genBody (api, methods, body, spec) {
const bodyMethod = getBodyMethod(methods)
const { content_type } = spec[api].headers
if (content_type && content_type.includes('application/x-ndjson')) {
return 'bulkBody: body,'
}
if (body === null && bodyMethod) {
return 'body: \'\','
} else if (bodyMethod) {
return 'body: body || \'\','
} else {
return 'body: null,'
}
}
function getBodyMethod (methods) {
const m = methods.filter(m => ~allowedMethods.body.indexOf(m))
if (m.length) return m[0]
return null
}
function getNoBodyMethod (methods) {
const m = methods.filter(m => ~allowedMethods.noBody.indexOf(m))
if (m.length) return m[0]
return null
}
function genUrlValidation (paths, api) {
// this api does not need url validation
if (!needsPathValidation(api)) return ''
// gets only the dynamic components of the url in an array
// then we reverse it. A parameters always require what is
// at its right in the array.
const chunks = paths
.sort((a, b) => Object.keys(a.parts || {}).length > Object.keys(b.parts || {}).length ? -1 : 1)
.slice(0, 1)
.reduce((acc, val) => val.path, '')
// .reduce((a, b) => a.path.split('/').length > b.path.split('/').length ? a.path : b.path)
.split('/')
.filter(s => s.startsWith('{'))
.map(s => s.slice(1, -1))
.reverse()
let code = ''
const len = chunks.length
chunks.forEach((chunk, index) => {
if (index === len - 1) return
const params = []
let camelCased = chunk[0] === '_'
? '_' + chunk.slice(1).replace(/_([a-z])/g, k => k[1].toUpperCase())
: chunk.replace(/_([a-z])/g, k => k[1].toUpperCase())
if (chunk === camelCased) {
code += `${index ? '} else ' : ''}if (params['${chunk}'] != null && (`
} else {
code += `${index ? '} else ' : ''}if ((params['${chunk}'] != null || params['${camelCased}'] != null) && (`
}
for (let i = index + 1; i < len; i++) {
params.push(chunks[i])
// url parts can be declared in camelCase fashion
camelCased = chunks[i][0] === '_'
? '_' + chunks[i].slice(1).replace(/_([a-z])/g, k => k[1].toUpperCase())
: chunks[i].replace(/_([a-z])/g, k => k[1].toUpperCase())
if (chunks[i] === camelCased) {
code += `params['${chunks[i]}'] == null${i === len - 1 ? '' : ' || '}`
} else {
code += `(params['${chunks[i]}'] == null && params['${camelCased}'] == null)${i === len - 1 ? '' : ' || '}`
}
}
code += `)) {
const err = new this[kConfigurationError]('Missing required parameter of the url: ${params.join(', ')}')
return handleError(err, callback)
`
})
if (chunks.length > 1) {
code += '\n}'
}
if (code.length) {
code = '// check required url components\n' + code
}
return code.trim()
}
function generateDocumentation ({ documentation }, op) {
// we use `replace(/\u00A0/g, ' ')` to remove no breaking spaces
// because some parts of the description fields are using it
if (documentation == null) return ''
let doc = '/**\n'
doc += ` * Perform a ${op} request\n`
if (documentation.description) {
doc += ` * ${documentation.description.replace(/\u00A0/g, ' ')}\n`
}
if (documentation.url) {
doc += ` * ${documentation.url}\n`
}
doc += ' */'
return doc
}
function needsPathValidation (api) {
return noPathValidation.indexOf(api) === -1
}
function intersect (first, ...rest) {
return rest.reduce((accum, current) => {
return accum.filter(x => current.indexOf(x) !== -1)
}, first)
}
function Uppercase (str) {
return str[0].toUpperCase() + str.slice(1)
}
module.exports = generateNamespace

View File

@ -0,0 +1,318 @@
/*
* 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 { 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) {
let doc = dedent`
[[api-reference]]
////////
===========================================================================================================================
|| ||
|| ||
|| ||
|| ██████╗ ███████╗ █████╗ ██████╗ ███╗ ███╗███████╗ ||
|| ██╔══██╗██╔════╝██╔══██╗██╔══██╗████╗ ████║██╔════╝ ||
|| ██████╔╝█████╗ ███████║██║ ██║██╔████╔██║█████╗ ||
|| ██╔══██╗██╔══╝ ██╔══██║██║ ██║██║╚██╔╝██║██╔══╝ ||
|| ██║ ██║███████╗██║ ██║██████╔╝██║ ╚═╝ ██║███████╗ ||
|| ╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝╚═════╝ ╚═╝ ╚═╝╚══════╝ ||
|| ||
|| ||
|| This file is autogenerated, DO NOT send pull requests that changes this file directly. ||
|| You should update the script that does the generation, which can be found in '/scripts/utils/generateDocs.js'. ||
|| ||
|| You can run the script with the following command: ||
|| node scripts/generate --branch <branch_name> ||
|| or ||
|| node scripts/generate --tag <tag_name> ||
|| ||
|| ||
|| ||
===========================================================================================================================
////////
== API Reference
This document contains the entire list of the Elasticsearch API supported by the client, both OSS and commercial. The client is entirely licensed under Apache 2.0.
Elasticsearch exposes an HTTP layer to communicate with, and the client is a library that will help you do this. Because of this reason, you will see HTTP related parameters, such as ${'`'}body${'`'} or ${'`'}headers${'`'}.
Every API can accept two objects, the first contains all the parameters that will be sent to Elasticsearch, while the second includes the request specific parameters, such as timeouts, headers, and so on.
In the first object, every parameter but the body will be sent via querystring or url parameter, depending on the API, and every unrecognized parameter will be sent as querystring.
[source,js]
----
// promise API
const result = await client.search({
index: 'my-index',
from: 20,
size: 10,
body: { foo: 'bar' }
}, {
ignore: [404],
maxRetries: 3
})
// callback API
client.search({
index: 'my-index',
from: 20,
size: 10,
body: { foo: 'bar' }
}, {
ignore: [404],
maxRetries: 3
}, (err, result) => {
if (err) console.log(err)
})
----
In this document, you will find the reference of every parameter accepted by the querystring or the url. If you also need to send the body, you can find the documentation of its format in the reference link that is present along with every endpoint.
\n\n`
doc += commonParameters(common)
spec.forEach(s => {
doc += '\n' + generateApiDoc(s)
})
return doc
}
function commonParameters (spec) {
let doc = dedent`
[discrete]
=== Common parameters
Parameters that are accepted by all API endpoints.
link:{ref}/common-options.html[Documentation]
[cols=2*]
|===\n`
Object.keys(spec.params).forEach(key => {
const name = isSnakeCased(key) && key !== camelify(key)
? '`' + key + '` or `' + camelify(key) + '`'
: '`' + key + '`'
doc += dedent`
|${name}
|${'`' + spec.params[key].type + '`'} - ${spec.params[key].description}`
if (spec.params[key].default) {
doc += ` +
_Default:_ ${'`' + spec.params[key].default + '`'}`
}
doc += '\n\n'
})
doc += dedent`
|===
`
return doc
}
function generateApiDoc (spec) {
const name = Object.keys(spec)[0]
const documentationUrl = spec[name].documentation && spec[name].documentation.url
? fixLink(name, spec[name].documentation.url)
: ''
const params = []
// url params
const urlParts = spec[name].url.paths.reduce((acc, path) => {
if (!path.parts) return acc
for (const part in path.parts) {
if (acc[part] != null) continue
acc[part] = path.parts[part]
}
return acc
}, {})
if (urlParts) {
Object.keys(urlParts).forEach(param => {
params.push({
name: param,
type: getType(urlParts[param].type, urlParts[param].options),
description: urlParts[param].description,
default: urlParts[param].default,
deprecated: !!urlParts[param].deprecated
})
})
}
// query params
const urlParams = spec[name].params
if (urlParams) {
Object.keys(urlParams).forEach(param => {
const duplicate = params.find(ele => ele.name === param)
if (duplicate) return
params.push({
name: param,
type: getType(urlParams[param].type, urlParams[param].options),
description: urlParams[param].description,
default: urlParams[param].default,
deprecated: !!urlParams[param].deprecated
})
})
}
// body params
const body = spec[name].body
if (body) {
params.push({
name: 'body',
type: 'object',
description: body.description,
default: body.default,
deprecated: !!body.deprecated
})
}
const codeParameters = params
.reduce((acc, val) => {
const code = `${val.name}: ${val.type},`
acc += acc === ''
? code
: '\n ' + code
return acc
}, '')
// remove last comma
.slice(0, -1)
const stability = spec[name].stability === 'stable'
? ''
: `*Stability:* ${spec[name].stability}`
let doc = dedent`
[discrete]
=== ${camelify(name)}
${stability}
[source,ts]
----
client.${camelify(name)}(${codeParameters.length > 0 ? `{\n ${codeParameters}\n}` : ''})
----\n`
if (documentationUrl) {
doc += `link:${documentationUrl}[Documentation] +\n`
}
if (codeExamples.includes(name)) {
doc += `{jsclient}/${name.replace(/\./g, '_')}_examples.html[Code Example] +\n`
}
if (params.length !== 0) {
doc += dedent`[cols=2*]
|===\n`
doc += params.reduce((acc, val) => {
const name = isSnakeCased(val.name) && val.name !== camelify(val.name)
? '`' + val.name + '` or `' + camelify(val.name) + '`'
: '`' + val.name + '`'
acc += dedent`
|${name}
|${'`' + val.type.replace(/\|/g, '\\|') + '`'} - ${val.description}`
if (val.default) {
acc += ` +\n_Default:_ ${'`' + val.default + '`'}`
}
if (val.deprecated) {
acc += ' +\n\nWARNING: This parameter has been deprecated.'
}
return acc + '\n\n'
}, '')
doc += dedent`
|===
`
}
doc += '\n'
return doc
}
const LINK_OVERRIDES = {
'license.delete': '{ref}/delete-license.html',
'license.get': '{ref}/get-license.html',
'license.get_basic_status': '{ref}/get-basic-status.html',
'license.get_trial_status': '{ref}/get-trial-status.html',
'license.post': '{ref}/update-license.html',
'license.post_start_basic': '{ref}/start-basic.html',
'license.post_start_trial': '{ref}/start-trial.html',
'migration.deprecations': '{ref}/migration-api-deprecation.html',
'monitoring.bulk': '{ref}/monitor-elasticsearch-cluster.html',
'ingest.delete_pipeline': '{ref}/delete-pipeline-api.html',
'ingest.get_pipeline': '{ref}/get-pipeline-api.html',
'ingest.put_pipeline': '{ref}/put-pipeline-api.html',
'ingest.simulate': '{ref}/simulate-pipeline-api.html',
'ingest.processor_grok': '{ref}/grok-processor.html#grok-processor-rest-get'
}
// Fixes bad urls in the JSON spec
function fixLink (name, str) {
/* In 6.x some API start with `xpack.` when in master they do not. We
* can safely ignore that for link generation. */
name = name.replace(/^xpack\./, '')
const override = LINK_OVERRIDES[name]
if (override) return override
if (!str) return ''
/* Replace references to the guide with the attribute {ref} because
* the json files in the Elasticsearch repo are a bit of a mess. */
str = str.replace(/^.+guide\/en\/elasticsearch\/reference\/[^/]+\/([^./]*\.html(?:#.+)?)$/, '{ref}/$1')
str = str.replace(/frozen\.html/, 'freeze-index-api.html')
str = str.replace(/ml-file-structure\.html/, 'ml-find-file-structure.html')
str = str.replace(/security-api-get-user-privileges\.html/, 'security-api-get-privileges.html')
return str
}
function getType (type, options) {
switch (type) {
case 'list':
return 'string | string[]'
case 'date':
case 'time':
case 'timeout':
return 'string'
case 'enum':
return options.map(k => `'${k}'`).join(' | ')
case 'int':
case 'double':
case 'long':
return 'number'
default:
return type
}
}
function camelify (str) {
return str[0] === '_'
? '_' + str.slice(1).replace(/_([a-z])/g, k => k[1].toUpperCase())
: str.replace(/_([a-z])/g, k => k[1].toUpperCase())
}
function isSnakeCased (str) {
return !!~str.indexOf('_')
}
module.exports = generateDocs

View File

@ -0,0 +1,299 @@
/*
* 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.
*/
/* eslint-disable no-template-curly-in-string */
/* eslint camelcase: 0 */
'use strict'
const { readdirSync } = require('fs')
const { join } = require('path')
const dedent = require('dedent')
const deepmerge = require('deepmerge')
function genFactory (folder, specFolder, namespaces) {
// get all the API files
// const apiFiles = readdirSync(folder)
const apiFiles = readdirSync(specFolder)
.filter(file => file !== '_common.json')
.filter(file => !file.includes('deprecated'))
.sort()
const types = apiFiles
.map(file => {
const name = file
.slice(0, -5)
.replace(/\.([a-z])/g, k => k[1].toUpperCase())
.replace(/_([a-z])/g, k => k[1].toUpperCase())
return file
.slice(0, -5) // remove `.json` extension
.split('.')
.reverse()
.reduce((acc, val) => {
const spec = readSpec(specFolder, file.slice(0, -5))
const isHead = isHeadMethod(spec, file.slice(0, -5))
const body = hasBody(spec, file.slice(0, -5))
const methods = acc === null ? buildMethodDefinition({ kibana: false }, val, name, body, isHead, spec) : null
const obj = {}
if (methods) {
for (const m of methods) {
obj[m.key] = m.val
}
} else {
obj[val] = acc
if (isSnakeCased(val)) {
obj[camelify(val)] = acc
}
}
return obj
}, null)
})
.reduce((acc, val) => deepmerge(acc, val), {})
const kibanaTypes = apiFiles
.map(file => {
const name = file
.slice(0, -5)
.replace(/\.([a-z])/g, k => k[1].toUpperCase())
.replace(/_([a-z])/g, k => k[1].toUpperCase())
return file
.slice(0, -5) // remove `.json` extension
.split('.')
.reverse()
.reduce((acc, val) => {
const spec = readSpec(specFolder, file.slice(0, -5))
const isHead = isHeadMethod(spec, file.slice(0, -5))
const body = hasBody(spec, file.slice(0, -5))
const methods = acc === null ? buildMethodDefinition({ kibana: true }, val, name, body, isHead, spec) : null
const obj = {}
if (methods) {
for (const m of methods) {
obj[m.key] = m.val
}
} else {
obj[camelify(val)] = acc
}
return obj
}, null)
})
.reduce((acc, val) => deepmerge(acc, val), {})
// serialize the type object
const typesStr = Object.keys(types)
.map(key => {
const line = ` ${key}: ${JSON.stringify(types[key], null, 4)}`
if (line.slice(-1) === '}') {
return line.slice(0, -1) + ' }'
}
return line
})
.join('\n')
// remove useless quotes and commas
.replace(/"/g, '')
.replace(/,$/gm, '')
const kibanaTypesStr = Object.keys(kibanaTypes)
.map(key => {
const line = ` ${key}: ${JSON.stringify(kibanaTypes[key], null, 4)}`
if (line.slice(-1) === '}') {
return line.slice(0, -1) + ' }'
}
return line
})
.join('\n')
// remove useless quotes and commas
.replace(/"/g, '')
.replace(/,$/gm, '')
let apisStr = ''
const getters = []
for (const namespace in namespaces) {
if (namespaces[namespace].length > 0) {
getters.push(`${camelify(namespace)}: {
get () {
if (this[k${toPascalCase(camelify(namespace))}] === null) {
this[k${toPascalCase(camelify(namespace))}] = new ${toPascalCase(camelify(namespace))}Api(this.transport, this[kConfigurationError])
}
return this[k${toPascalCase(camelify(namespace))}]
}
},\n`)
if (namespace.includes('_')) {
getters.push(`${namespace}: { get () { return this.${camelify(namespace)} } },\n`)
}
} else {
apisStr += `ESAPI.prototype.${camelify(namespace)} = ${camelify(namespace)}Api\n`
if (namespace.includes('_')) {
getters.push(`${namespace}: { get () { return this.${camelify(namespace)} } },\n`)
}
}
}
apisStr += '\nObject.defineProperties(ESAPI.prototype, {\n'
for (const getter of getters) {
apisStr += getter
}
apisStr += '})'
let modules = ''
let symbols = ''
let symbolsInstance = ''
for (const namespace in namespaces) {
if (namespaces[namespace].length > 0) {
modules += `const ${toPascalCase(camelify(namespace))}Api = require('./api/${namespace}')\n`
symbols += `const k${toPascalCase(camelify(namespace))} = Symbol('${toPascalCase(camelify(namespace))}')\n`
symbolsInstance += `this[k${toPascalCase(camelify(namespace))}] = null\n`
} else {
modules += `const ${camelify(namespace)}Api = require('./api/${namespace}')\n`
}
}
const fn = dedent`
/*
* 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'
${modules}
const { kConfigurationError } = require('./utils')
${symbols}
function ESAPI (opts) {
this[kConfigurationError] = opts.ConfigurationError
${symbolsInstance}
}
${apisStr}
module.exports = ESAPI
`
// new line at the end of file
return { fn: fn + '\n', types: typesStr, kibanaTypes: kibanaTypesStr }
}
// from snake_case to camelCase
function camelify (str) {
return str.replace(/_([a-z])/g, k => k[1].toUpperCase())
}
function isSnakeCased (str) {
return !!~str.indexOf('_')
}
function toPascalCase (str) {
return str[0].toUpperCase() + str.slice(1)
}
function buildMethodDefinition (opts, api, name, hasBody, isHead, spec) {
const Name = toPascalCase(name)
const { content_type } = spec[Object.keys(spec)[0]].headers
const bodyType = content_type && content_type.includes('application/x-ndjson') ? 'RequestNDBody' : 'RequestBody'
const responseType = isHead ? 'boolean' : 'Record<string, any>'
const defaultBodyType = content_type && content_type.includes('application/x-ndjson') ? 'Record<string, any>[]' : 'Record<string, any>'
if (opts.kibana) {
if (hasBody) {
return [
{ key: `${camelify(api)}<TResponse = ${responseType}, TRequestBody extends ${bodyType} = ${defaultBodyType}, TContext = Context>(params?: RequestParams.${Name}<TRequestBody>, options?: TransportRequestOptions)`, val: 'TransportRequestPromise<ApiResponse<TResponse, TContext>>' }
]
} else {
return [
{ key: `${camelify(api)}<TResponse = ${responseType}, TContext = Context>(params?: RequestParams.${Name}, options?: TransportRequestOptions)`, val: 'TransportRequestPromise<ApiResponse<TResponse, TContext>>' }
]
}
}
if (hasBody) {
let methods = [
{ key: `${api}<TResponse = ${responseType}, TRequestBody extends ${bodyType} = ${defaultBodyType}, TContext = Context>(params?: RequestParams.${Name}<TRequestBody>, options?: TransportRequestOptions)`, val: 'TransportRequestPromise<ApiResponse<TResponse, TContext>>' },
{ key: `${api}<TResponse = ${responseType}, TRequestBody extends ${bodyType} = ${defaultBodyType}, TContext = Context>(callback: callbackFn<TResponse, TContext>)`, val: 'TransportRequestCallback' },
{ key: `${api}<TResponse = ${responseType}, TRequestBody extends ${bodyType} = ${defaultBodyType}, TContext = Context>(params: RequestParams.${Name}<TRequestBody>, callback: callbackFn<TResponse, TContext>)`, val: 'TransportRequestCallback' },
{ key: `${api}<TResponse = ${responseType}, TRequestBody extends ${bodyType} = ${defaultBodyType}, TContext = Context>(params: RequestParams.${Name}<TRequestBody>, options: TransportRequestOptions, callback: callbackFn<TResponse, TContext>)`, val: 'TransportRequestCallback' }
]
if (isSnakeCased(api)) {
methods = methods.concat([
{ key: `${camelify(api)}<TResponse = ${responseType}, TRequestBody extends ${bodyType} = ${defaultBodyType}, TContext = Context>(params?: RequestParams.${Name}<TRequestBody>, options?: TransportRequestOptions)`, val: 'TransportRequestPromise<ApiResponse<TResponse, TContext>>' },
{ key: `${camelify(api)}<TResponse = ${responseType}, TRequestBody extends ${bodyType} = ${defaultBodyType}, TContext = Context>(callback: callbackFn<TResponse, TContext>)`, val: 'TransportRequestCallback' },
{ key: `${camelify(api)}<TResponse = ${responseType}, TRequestBody extends ${bodyType} = ${defaultBodyType}, TContext = Context>(params: RequestParams.${Name}<TRequestBody>, callback: callbackFn<TResponse, TContext>)`, val: 'TransportRequestCallback' },
{ key: `${camelify(api)}<TResponse = ${responseType}, TRequestBody extends ${bodyType} = ${defaultBodyType}, TContext = Context>(params: RequestParams.${Name}<TRequestBody>, options: TransportRequestOptions, callback: callbackFn<TResponse, TContext>)`, val: 'TransportRequestCallback' }
])
}
return methods
} else {
let methods = [
{ key: `${api}<TResponse = ${responseType}, TContext = Context>(params?: RequestParams.${Name}, options?: TransportRequestOptions)`, val: 'TransportRequestPromise<ApiResponse<TResponse, TContext>>' },
{ key: `${api}<TResponse = ${responseType}, TContext = Context>(callback: callbackFn<TResponse, TContext>)`, val: 'TransportRequestCallback' },
{ key: `${api}<TResponse = ${responseType}, TContext = Context>(params: RequestParams.${Name}, callback: callbackFn<TResponse, TContext>)`, val: 'TransportRequestCallback' },
{ key: `${api}<TResponse = ${responseType}, TContext = Context>(params: RequestParams.${Name}, options: TransportRequestOptions, callback: callbackFn<TResponse, TContext>)`, val: 'TransportRequestCallback' }
]
if (isSnakeCased(api)) {
methods = methods.concat([
{ key: `${camelify(api)}<TResponse = ${responseType}, TContext = Context>(params?: RequestParams.${Name}, options?: TransportRequestOptions)`, val: 'TransportRequestPromise<ApiResponse<TResponse, TContext>>' },
{ key: `${camelify(api)}<TResponse = ${responseType}, TContext = Context>(callback: callbackFn<TResponse, TContext>)`, val: 'TransportRequestCallback' },
{ key: `${camelify(api)}<TResponse = ${responseType}, TContext = Context>(params: RequestParams.${Name}, callback: callbackFn<TResponse, TContext>)`, val: 'TransportRequestCallback' },
{ key: `${camelify(api)}<TResponse = ${responseType}, TContext = Context>(params: RequestParams.${Name}, options: TransportRequestOptions, callback: callbackFn<TResponse, TContext>)`, val: 'TransportRequestCallback' }
])
}
return methods
}
}
function hasBody (spec, api) {
return !!spec[api].body
}
function isHeadMethod (spec, api) {
const { paths } = spec[api].url
const methods = []
for (const path of paths) {
for (const method of path.methods) {
if (!methods.includes(method)) {
methods.push(method)
}
}
}
return methods.length === 1 && methods[0] === 'HEAD'
}
function readSpec (specFolder, file) {
try {
return require(join(specFolder, file))
} catch (err) {
throw new Error(`Cannot read spec file ${file}`)
}
}
module.exports = genFactory

View File

@ -0,0 +1,191 @@
/*
* 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.
*/
/* eslint camelcase: 0 */
'use strict'
const deprecatedParameters = require('./patch.json')
function generate (version, api) {
const release = version.charAt(0)
let types = `/*
* 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.
*/
import { RequestBody, RequestNDBody } from '../lib/Transport'
export interface Generic {
method?: string;
filter_path?: string | string[];
pretty?: boolean;
human?: boolean;
error_trace?: boolean;
source?: string;
}
`
api.forEach(generateRequestType)
return types
function generateRequestType (spec) {
const api = Object.keys(spec)[0]
const name = api
.replace(/\.([a-z])/g, k => k[1].toUpperCase())
.replace(/_([a-z])/g, k => k[1].toUpperCase())
const { paths = {} } = spec[api].url
const { body, params = {} } = spec[api]
// get the required parts from the url
// if the url has at least one static path,
// then there are not required parts of the url
let allParts = []
let requiredParts = []
for (const path of paths) {
if (path.parts) {
allParts.push(Object.keys(path.parts))
} else {
allParts = []
break
}
}
if (allParts.length > 0) {
requiredParts = intersect(...allParts)
}
const parts = paths.reduce((acc, path) => {
if (!path.parts) return acc
for (const part in path.parts) {
if (acc[part] != null) continue
acc[part] = { key: part, value: path.parts[part], required: requiredParts.includes(part) }
}
return acc
}, {})
const deprecatedParametersToAdd = []
const paramsArr = Object.keys(params)
.filter(k => !Object.keys(parts).includes(k))
.map(k => {
if (deprecatedParameters[release] && deprecatedParameters[release][k]) {
deprecatedParametersToAdd.push({
key: deprecatedParameters[release][k],
value: params[k],
required: params[k].required
})
}
return { key: k, value: params[k], required: params[k].required }
})
const partsArr = Object.keys(parts).map(k => parts[k])
deprecatedParametersToAdd.forEach(k => partsArr.push(k))
const genLine = e => {
const optional = e.required ? '' : '?'
return `${e.key}${optional}: ${getType(e.value.type, e.value.options)};`
}
const { content_type } = spec[api].headers
const bodyGeneric = content_type && content_type.includes('application/x-ndjson') ? 'RequestNDBody' : 'RequestBody'
const code = `
export interface ${toPascalCase(name)}${body ? `<T = ${bodyGeneric}>` : ''} extends Generic {
${partsArr.map(genLine).join('\n ')}
${paramsArr.map(genLine).join('\n ')}
${body ? `body${body.required ? '' : '?'}: T;` : ''}
}
`
types += '\n'
// remove empty lines
types += code.replace(/^\s*\n/gm, '')
}
function getType (type, options) {
switch (type) {
case 'list':
return 'string | string[]'
case 'date':
case 'time':
case 'timeout':
return 'string'
case 'enum': {
// the following code changes 'true' | 'false' to boolean
let foundTrue = false
let foundFalse = false
options = options
.map(k => {
if (k === 'true') {
foundTrue = true
return true
} else if (k === 'false') {
foundFalse = true
return false
} else {
return `'${k}'`
}
})
.filter(k => {
if (foundTrue && foundFalse && (k === true || k === false)) {
return false
}
return true
})
if (foundTrue && foundFalse) {
options.push('boolean')
}
return options.join(' | ')
}
case 'int':
case 'double':
case 'long':
return 'number'
case 'boolean|long':
return 'boolean | number'
default:
return type
}
}
}
function intersect (first, ...rest) {
return rest.reduce((accum, current) => {
return accum.filter(x => current.indexOf(x) !== -1)
}, first)
}
function toPascalCase (str) {
return str[0].toUpperCase() + str.slice(1)
}
module.exports = generate

View File

@ -17,12 +17,18 @@
* under the License.
*/
import { Client } from '@elastic/elasticsearch'
'use strict'
new Client({
node: 'http://localhost:9200',
auth: {
username: 'elastic',
password: 'changeme',
}
})
const generate = require('./generateApis')
const cloneAndCheckout = require('./clone-es')
const genFactory = require('./generateMain')
const generateDocs = require('./generateDocs')
const generateRequestTypes = require('./generateRequestTypes')
module.exports = {
generate,
cloneAndCheckout,
genFactory,
generateDocs,
generateRequestTypes
}

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

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

Some files were not shown because too many files have changed in this diff Show More