Compare commits

...

9 Commits

Author SHA1 Message Date
9924ada7d7 Bumped v5.6.16-rc.2 2019-04-17 14:17:38 +02:00
2827708ca5 Updated typings (#819) 2019-04-17 11:25:18 +02:00
c2118c5168 Drop Node.js v6 support (#818)
Node.js v6 will go EOL at the end of April 2019, and already two of the production dependencies of the client have already dropped support for it, and soon others will do *(as well as development dependencies)*.

Furthermore, since Node.js will go in EOL it will never get security patches, plus, also OpenSSL-1.0.2 will go EOL [this year](https://github.com/nodejs/Release#release-schedule); to avoid risks for the client users it is better to drop support for Node.js v6 right away.
2019-04-17 11:25:18 +02:00
66b6c17ee1 Fix a typo in usage example (#806) 2019-04-11 16:38:30 +02:00
92e786e9c6 API generation 2019-04-10 11:47:21 +02:00
e6f0d272ca Improve typings (#813)
The ApiResponse now accepts a generics that defaults to any, same for every API method that might need a body.
2019-04-10 11:46:29 +02:00
fb365c9adf Custom http agent support (#810) 2019-04-10 11:46:09 +02:00
bbd6b5a546 Support for publish_address as hostname/ip:port (#804) 2019-04-09 12:06:51 +02:00
729444c060 Updated README.md 2019-04-01 09:50:43 +02:00
24 changed files with 555 additions and 229 deletions

View File

@ -6,6 +6,5 @@ NODE_JS_VERSION:
- 11
- 10
- 8
- 6
exclude: ~

View File

@ -67,3 +67,6 @@ scripts
.ci
.travis.yml
certs
.github
CODE_OF_CONDUCT.md
CONTRIBUTING.md

View File

@ -5,9 +5,9 @@ sudo: required
language: node_js
node_js:
- "11"
- "10"
- "8"
- "6"
env:
global:

View File

@ -6,8 +6,7 @@
---
**Note:** In the past months we have worked on the new Elasticsearch Node.js client, we will announce it soon!
If you want you can already try it by following the instructions below, while if you're going to use the legacy one or report an issue, please check out [elastic/elasticsearch-js-legacy](https://github.com/elastic/elasticsearch-js-legacy).
**Note:** In the past months we have worked on the new Elasticsearch Node.js client, and if you want you can already try it by following the instructions below, while if you're going to use the legacy one or report an issue, please check out [elastic/elasticsearch-js-legacy](https://github.com/elastic/elasticsearch-js-legacy).
---
@ -28,11 +27,7 @@ npm install @elastic/elasticsearch
### Compatibility
---
**Warning:** currently on npm you will only find a beta release that works with Elasticsearch 7, we will add the support for Elasticsearch 5, 6, and 7 once we release the final version of this library.
---
The minimum supported version of Node.js is `v8`.
The library is compatible with all Elasticsearch versions since 5.x, but you should use the same major version of the Elasticsearch instance that you are using.
```
@ -47,7 +42,9 @@ The library is compatible with all Elasticsearch versions since 5.x, but you sho
```
## Usage
You can find the full documentation in the [docs](https://github.com/elastic/elasticsearch-js/tree/master/docs) folder.
You can find the full documentation in our [docs](https://www.elastic.co/guide/en/elasticsearch/client/javascript-api/current/index.html) website.
```js
const { Client } = require('@elastic/elasticsearch')
const client = new Client({ node: 'http://localhost:9200' })
@ -77,7 +74,9 @@ The returned value of **every** API call is formed as follows:
}
```
### Client options
The client is designed to be easily configured as you see fit for your needs, following you can see all the possible options that you can use to configure it.
```ts
{
// the Elasticsearch endpoint to use

180
api/requestParams.d.ts vendored
View File

@ -27,7 +27,7 @@ export interface Generic {
source?: string;
}
export interface Bulk extends Generic {
export interface Bulk<T = any> extends Generic {
index?: string;
type?: string;
wait_for_active_shards?: string;
@ -39,7 +39,7 @@ export interface Bulk extends Generic {
_source_exclude?: string | string[];
_source_include?: string | string[];
pipeline?: string;
body: any;
body: T;
}
export interface CatAliases extends Generic {
@ -259,15 +259,15 @@ export interface CatThreadPool extends Generic {
v?: boolean;
}
export interface ClearScroll extends Generic {
export interface ClearScroll<T = any> extends Generic {
scroll_id?: string | string[];
body?: any;
body?: T;
}
export interface ClusterAllocationExplain extends Generic {
export interface ClusterAllocationExplain<T = any> extends Generic {
include_yes_decisions?: boolean;
include_disk_info?: boolean;
body?: any;
body?: T;
}
export interface ClusterGetSettings extends Generic {
@ -295,24 +295,24 @@ export interface ClusterPendingTasks extends Generic {
master_timeout?: string;
}
export interface ClusterPutSettings extends Generic {
export interface ClusterPutSettings<T = any> extends Generic {
flat_settings?: boolean;
master_timeout?: string;
timeout?: string;
body?: any;
body?: T;
}
export interface ClusterRemoteInfo extends Generic {
}
export interface ClusterReroute extends Generic {
export interface ClusterReroute<T = any> extends Generic {
dry_run?: boolean;
explain?: boolean;
retry_failed?: boolean;
metric?: string | string[];
master_timeout?: string;
timeout?: string;
body?: any;
body?: T;
}
export interface ClusterState extends Generic {
@ -332,7 +332,7 @@ export interface ClusterStats extends Generic {
timeout?: string;
}
export interface Count extends Generic {
export interface Count<T = any> extends Generic {
index?: string | string[];
type?: string | string[];
ignore_unavailable?: boolean;
@ -348,10 +348,10 @@ export interface Count extends Generic {
df?: string;
lenient?: boolean;
terminate_after?: number;
body?: any;
body?: T;
}
export interface CountPercolate extends Generic {
export interface CountPercolate<T = any> extends Generic {
index: string;
type: string;
id?: string;
@ -364,10 +364,10 @@ export interface CountPercolate extends Generic {
percolate_type?: string;
version?: number;
version_type?: 'internal' | 'external' | 'external_gte' | 'force';
body?: any;
body?: T;
}
export interface Create extends Generic {
export interface Create<T = any> extends Generic {
id: string;
index: string;
type: string;
@ -381,7 +381,7 @@ export interface Create extends Generic {
version?: number;
version_type?: 'internal' | 'external' | 'external_gte' | 'force';
pipeline?: string;
body: any;
body: T;
}
export interface Delete extends Generic {
@ -397,7 +397,7 @@ export interface Delete extends Generic {
version_type?: 'internal' | 'external' | 'external_gte' | 'force';
}
export interface DeleteByQuery extends Generic {
export interface DeleteByQuery<T = any> extends Generic {
index: string | string[];
type?: string | string[];
analyzer?: string;
@ -432,7 +432,7 @@ export interface DeleteByQuery extends Generic {
wait_for_completion?: boolean;
requests_per_second?: number;
slices?: number;
body: any;
body: T;
}
export interface DeleteScript extends Generic {
@ -479,7 +479,7 @@ export interface ExistsSource extends Generic {
version_type?: 'internal' | 'external' | 'external_gte' | 'force';
}
export interface Explain extends Generic {
export interface Explain<T = any> extends Generic {
id: string;
index: string;
type: string;
@ -496,26 +496,26 @@ export interface Explain extends Generic {
_source?: string | string[];
_source_exclude?: string | string[];
_source_include?: string | string[];
body?: any;
body?: T;
}
export interface FieldCaps extends Generic {
export interface FieldCaps<T = any> extends Generic {
index?: string | string[];
fields?: string | string[];
ignore_unavailable?: boolean;
allow_no_indices?: boolean;
expand_wildcards?: 'open' | 'closed' | 'none' | 'all';
body?: any;
body?: T;
}
export interface FieldStats extends Generic {
export interface FieldStats<T = any> extends Generic {
index?: string | string[];
fields?: string | string[];
level?: 'indices' | 'cluster';
ignore_unavailable?: boolean;
allow_no_indices?: boolean;
expand_wildcards?: 'open' | 'closed' | 'none' | 'all';
body?: any;
body?: T;
}
export interface Get extends Generic {
@ -560,7 +560,7 @@ export interface GetTemplate extends Generic {
id: string;
}
export interface Index extends Generic {
export interface Index<T = any> extends Generic {
id?: string;
index: string;
type: string;
@ -575,10 +575,10 @@ export interface Index extends Generic {
version?: number;
version_type?: 'internal' | 'external' | 'external_gte' | 'force';
pipeline?: string;
body: any;
body: T;
}
export interface IndicesAnalyze extends Generic {
export interface IndicesAnalyze<T = any> extends Generic {
index?: string;
analyzer?: string;
char_filter?: string | string[];
@ -590,7 +590,7 @@ export interface IndicesAnalyze extends Generic {
explain?: boolean;
attributes?: string | string[];
format?: 'detailed' | 'text';
body?: any;
body?: T;
}
export interface IndicesClearCache extends Generic {
@ -616,13 +616,13 @@ export interface IndicesClose extends Generic {
expand_wildcards?: 'open' | 'closed' | 'none' | 'all';
}
export interface IndicesCreate extends Generic {
export interface IndicesCreate<T = any> extends Generic {
index: string;
wait_for_active_shards?: string;
timeout?: string;
master_timeout?: string;
update_all_types?: boolean;
body?: any;
body?: T;
}
export interface IndicesDelete extends Generic {
@ -781,15 +781,15 @@ export interface IndicesOpen extends Generic {
expand_wildcards?: 'open' | 'closed' | 'none' | 'all';
}
export interface IndicesPutAlias extends Generic {
export interface IndicesPutAlias<T = any> extends Generic {
index: string | string[];
name: string;
timeout?: string;
master_timeout?: string;
body?: any;
body?: T;
}
export interface IndicesPutMapping extends Generic {
export interface IndicesPutMapping<T = any> extends Generic {
index?: string | string[];
type: string;
timeout?: string;
@ -798,10 +798,10 @@ export interface IndicesPutMapping extends Generic {
allow_no_indices?: boolean;
expand_wildcards?: 'open' | 'closed' | 'none' | 'all';
update_all_types?: boolean;
body: any;
body: T;
}
export interface IndicesPutSettings extends Generic {
export interface IndicesPutSettings<T = any> extends Generic {
index?: string | string[];
master_timeout?: string;
preserve_existing?: boolean;
@ -809,17 +809,17 @@ export interface IndicesPutSettings extends Generic {
allow_no_indices?: boolean;
expand_wildcards?: 'open' | 'closed' | 'none' | 'all';
flat_settings?: boolean;
body: any;
body: T;
}
export interface IndicesPutTemplate extends Generic {
export interface IndicesPutTemplate<T = any> extends Generic {
name: string;
order?: number;
create?: boolean;
timeout?: string;
master_timeout?: string;
flat_settings?: boolean;
body: any;
body: T;
}
export interface IndicesRecovery extends Generic {
@ -835,14 +835,14 @@ export interface IndicesRefresh extends Generic {
expand_wildcards?: 'open' | 'closed' | 'none' | 'all';
}
export interface IndicesRollover extends Generic {
export interface IndicesRollover<T = any> extends Generic {
alias: string;
new_index?: string;
timeout?: string;
dry_run?: boolean;
master_timeout?: string;
wait_for_active_shards?: string;
body?: any;
body?: T;
}
export interface IndicesSegments extends Generic {
@ -863,13 +863,13 @@ export interface IndicesShardStores extends Generic {
operation_threading?: undefined;
}
export interface IndicesShrink extends Generic {
export interface IndicesShrink<T = any> extends Generic {
index: string;
target: string;
timeout?: string;
master_timeout?: string;
wait_for_active_shards?: string;
body?: any;
body?: T;
}
export interface IndicesStats extends Generic {
@ -884,10 +884,10 @@ export interface IndicesStats extends Generic {
include_segment_file_sizes?: boolean;
}
export interface IndicesUpdateAliases extends Generic {
export interface IndicesUpdateAliases<T = any> extends Generic {
timeout?: string;
master_timeout?: string;
body: any;
body: T;
}
export interface IndicesUpgrade extends Generic {
@ -899,7 +899,7 @@ export interface IndicesUpgrade extends Generic {
only_ancient_segments?: boolean;
}
export interface IndicesValidateQuery extends Generic {
export interface IndicesValidateQuery<T = any> extends Generic {
index?: string | string[];
type?: string | string[];
explain?: boolean;
@ -915,7 +915,7 @@ export interface IndicesValidateQuery extends Generic {
lenient?: boolean;
rewrite?: boolean;
all_shards?: boolean;
body?: any;
body?: T;
}
export interface Info extends Generic {
@ -935,20 +935,20 @@ export interface IngestGetPipeline extends Generic {
export interface IngestProcessorGrok extends Generic {
}
export interface IngestPutPipeline extends Generic {
export interface IngestPutPipeline<T = any> extends Generic {
id: string;
master_timeout?: string;
timeout?: string;
body: any;
body: T;
}
export interface IngestSimulate extends Generic {
export interface IngestSimulate<T = any> extends Generic {
id?: string;
verbose?: boolean;
body: any;
body: T;
}
export interface Mget extends Generic {
export interface Mget<T = any> extends Generic {
index?: string;
type?: string;
stored_fields?: string | string[];
@ -959,38 +959,38 @@ export interface Mget extends Generic {
_source?: string | string[];
_source_exclude?: string | string[];
_source_include?: string | string[];
body: any;
body: T;
}
export interface Mpercolate extends Generic {
export interface Mpercolate<T = any> extends Generic {
index?: string;
type?: string;
ignore_unavailable?: boolean;
allow_no_indices?: boolean;
expand_wildcards?: 'open' | 'closed' | 'none' | 'all';
body: any;
body: T;
}
export interface Msearch extends Generic {
export interface Msearch<T = any> extends Generic {
index?: string | string[];
type?: string | string[];
search_type?: 'query_then_fetch' | 'query_and_fetch' | 'dfs_query_then_fetch' | 'dfs_query_and_fetch';
max_concurrent_searches?: number;
typed_keys?: boolean;
pre_filter_shard_size?: number;
body: any;
body: T;
}
export interface MsearchTemplate extends Generic {
export interface MsearchTemplate<T = any> extends Generic {
index?: string | string[];
type?: string | string[];
search_type?: 'query_then_fetch' | 'query_and_fetch' | 'dfs_query_then_fetch' | 'dfs_query_and_fetch';
typed_keys?: boolean;
max_concurrent_searches?: number;
body: any;
body: T;
}
export interface Mtermvectors extends Generic {
export interface Mtermvectors<T = any> extends Generic {
index?: string;
type?: string;
ids?: string | string[];
@ -1006,7 +1006,7 @@ export interface Mtermvectors extends Generic {
realtime?: boolean;
version?: number;
version_type?: 'internal' | 'external' | 'external_gte' | 'force';
body?: any;
body?: T;
}
export interface NodesHotThreads extends Generic {
@ -1040,7 +1040,7 @@ export interface NodesStats extends Generic {
include_segment_file_sizes?: boolean;
}
export interface Percolate extends Generic {
export interface Percolate<T = any> extends Generic {
index: string;
type: string;
id?: string;
@ -1056,33 +1056,33 @@ export interface Percolate extends Generic {
percolate_format?: 'ids';
version?: number;
version_type?: 'internal' | 'external' | 'external_gte' | 'force';
body?: any;
body?: T;
}
export interface Ping extends Generic {
}
export interface PutScript extends Generic {
export interface PutScript<T = any> extends Generic {
id: string;
lang: string;
timeout?: string;
master_timeout?: string;
body: any;
body: T;
}
export interface PutTemplate extends Generic {
export interface PutTemplate<T = any> extends Generic {
id: string;
body: any;
body: T;
}
export interface Reindex extends Generic {
export interface Reindex<T = any> extends Generic {
refresh?: boolean;
timeout?: string;
wait_for_active_shards?: string;
wait_for_completion?: boolean;
requests_per_second?: number;
slices?: number;
body: any;
body: T;
}
export interface ReindexRethrottle extends Generic {
@ -1090,18 +1090,18 @@ export interface ReindexRethrottle extends Generic {
requests_per_second: number;
}
export interface RenderSearchTemplate extends Generic {
export interface RenderSearchTemplate<T = any> extends Generic {
id?: string;
body?: any;
body?: T;
}
export interface Scroll extends Generic {
export interface Scroll<T = any> extends Generic {
scroll_id?: string;
scroll?: string;
body?: any;
body?: T;
}
export interface Search extends Generic {
export interface Search<T = any> extends Generic {
index?: string | string[];
type?: string | string[];
analyzer?: string;
@ -1141,7 +1141,7 @@ export interface Search extends Generic {
batched_reduce_size?: number;
max_concurrent_shard_requests?: number;
pre_filter_shard_size?: number;
body?: any;
body?: T;
}
export interface SearchShards extends Generic {
@ -1155,7 +1155,7 @@ export interface SearchShards extends Generic {
expand_wildcards?: 'open' | 'closed' | 'none' | 'all';
}
export interface SearchTemplate extends Generic {
export interface SearchTemplate<T = any> extends Generic {
index?: string | string[];
type?: string | string[];
ignore_unavailable?: boolean;
@ -1168,23 +1168,23 @@ export interface SearchTemplate extends Generic {
explain?: boolean;
profile?: boolean;
typed_keys?: boolean;
body?: any;
body?: T;
}
export interface SnapshotCreate extends Generic {
export interface SnapshotCreate<T = any> extends Generic {
repository: string;
snapshot: string;
master_timeout?: string;
wait_for_completion?: boolean;
body?: any;
body?: T;
}
export interface SnapshotCreateRepository extends Generic {
export interface SnapshotCreateRepository<T = any> extends Generic {
repository: string;
master_timeout?: string;
timeout?: string;
verify?: boolean;
body: any;
body: T;
}
export interface SnapshotDelete extends Generic {
@ -1213,12 +1213,12 @@ export interface SnapshotGetRepository extends Generic {
local?: boolean;
}
export interface SnapshotRestore extends Generic {
export interface SnapshotRestore<T = any> extends Generic {
repository: string;
snapshot: string;
master_timeout?: string;
wait_for_completion?: boolean;
body?: any;
body?: T;
}
export interface SnapshotStatus extends Generic {
@ -1234,14 +1234,14 @@ export interface SnapshotVerifyRepository extends Generic {
timeout?: string;
}
export interface Suggest extends Generic {
export interface Suggest<T = any> extends Generic {
index?: string | string[];
ignore_unavailable?: boolean;
allow_no_indices?: boolean;
expand_wildcards?: 'open' | 'closed' | 'none' | 'all';
preference?: string;
routing?: string;
body: any;
body: T;
}
export interface TasksCancel extends Generic {
@ -1267,7 +1267,7 @@ export interface TasksList extends Generic {
group_by?: 'nodes' | 'parents';
}
export interface Termvectors extends Generic {
export interface Termvectors<T = any> extends Generic {
index: string;
type: string;
id?: string;
@ -1283,10 +1283,10 @@ export interface Termvectors extends Generic {
realtime?: boolean;
version?: number;
version_type?: 'internal' | 'external' | 'external_gte' | 'force';
body?: any;
body?: T;
}
export interface Update extends Generic {
export interface Update<T = any> extends Generic {
id: string;
index: string;
type: string;
@ -1305,10 +1305,10 @@ export interface Update extends Generic {
ttl?: string;
version?: number;
version_type?: 'internal' | 'force';
body?: any;
body?: T;
}
export interface UpdateByQuery extends Generic {
export interface UpdateByQuery<T = any> extends Generic {
index: string | string[];
type?: string | string[];
analyzer?: string;
@ -1345,5 +1345,5 @@ export interface UpdateByQuery extends Generic {
wait_for_completion?: boolean;
requests_per_second?: number;
slices?: number;
body?: any;
body?: T;
}

View File

@ -9,7 +9,7 @@ Every breaking change was carefully weighed, and each is justified. Furthermore,
=== Breaking changes
* Minimum supported version of Node.js is `v6`.
* Minimum supported version of Node.js is `v8`.
* Everything has been rewritten using ES6 classes to help users extend the defaults more easily.

View File

@ -90,8 +90,20 @@ _Default:_ `false`
_Default:_ `null`
|`agent`
|`http.AgentOptions` - http agent https://nodejs.org/api/http.html#http_new_agent_options[options]. +
a|`http.AgentOptions, function` - http agent https://nodejs.org/api/http.html#http_new_agent_options[options], or a function that returns an actual http agent instance. +
_Default:_ `null`
[source,js]
----
const client = new Client({
node: 'http://localhost:9200',
agent: { agent: 'options' }
})
const client = new Client({
node: 'http://localhost:9200',
agent: () => new CustomAgent()
})
----
|`nodeFilter`
a|`function` - Filters which node not to use for a request. +

View File

@ -18,6 +18,7 @@ npm install @elastic/elasticsearch
----
=== Compatibility
The minimum supported version of Node.js is `v8`.
The library is compatible with all Elasticsearch versions since 5.x, but you should use the same major version of the Elasticsearch instance that you are using.
----
@ -50,4 +51,4 @@ client.search({
}, (err, result) => {
if (err) console.log(err)
})
----
----

View File

@ -3,58 +3,146 @@
The client offers a first-class support for TypeScript, since it ships the type definitions for every exposed API.
While the client offers type definitions for Request parameters, Request bodies and responses are shipped with `any` because there is not an official spec that defines them, so we cannot make guarantees over them (but since they are shipped with `any`, you can easily override them with your own typing definitions).
NOTE: If you are using TypeScript you will be required to use _snake_case_ style to define the API parameters instead of _camelCase_.
=== How to extend the provided typings?
Extend the provided typings is very straightforward, you should declare a custom `.d.ts` file and then write inside your type extensions, following there is an example of how do it.
Other than the types for the surface API, the client offers the types for every request method, via the `RequestParams`, if you need the types for a search request for instance, you can access them via `RequestParams.Search`.
Every API that supports a body, accepts a https://www.typescriptlang.org/docs/handbook/generics.html[generics] which represents the type of the request body, if you don't configure anything, it will default to `any`.
For example:
[source,ts]
----
declare module '@elastic/elasticsearch' {
export interface ShardsResponse {
total: number;
successful: number;
failed: number;
skipped: number;
}
import { RequestParams } from '@elastic/elasticsearch'
export interface Explanation {
value: number;
description: string;
details: Explanation[];
}
export interface SearchResponse<T> {
took: number;
timed_out: boolean;
_scroll_id?: string;
_shards: ShardsResponse;
hits: {
total: number;
max_score: number;
hits: Array<{
_index: string;
_type: string;
_id: string;
_score: number;
_source: T;
_version?: number;
_explanation?: Explanation;
fields?: any;
highlight?: any;
inner_hits?: any;
matched_queries?: string[];
sort?: string[];
}>;
};
aggregations?: any;
}
export interface MSearchResponse<T> {
responses?: Array<SearchResponse<T>>;
interface SearchBody {
query: {
match: { foo: string }
}
}
export {};
const searchParams: RequestParams.Search<SearchBody> = {
index: 'test',
body: {
query: {
match: { foo: 'bar' }
}
}
}
// This is valid as well
const searchParams: RequestParams.Search = {
index: 'test',
body: {
query: {
match: { foo: 'bar' }
}
}
}
----
You can find the type definiton of a response in `ApiResponse`, which accepts a generics as well if you want to specify the body type, otherwise it defaults to `any`.
[source,ts]
----
interface SearchResponse<T> {
hits: {
hits: Array<{
_source: T;
}>
}
}
// Define the intefrace of the source object
interface Source {
foo: string
}
client.search(searchParams)
.then((response: ApiResponse<SearchResponse<Source>>) => console.log(response))
.catch((err: Error) => {})
----
=== A complete example
[source,ts]
----
import {
Client,
// Object that contains the type definitions of every API method
RequestParams,
// Interface of the generic API response
ApiResponse,
} from '@elastic/elasticsearch'
const client = new Client({ node: 'http://localhost:9200' })
// Define the type of the body for the Search request
interface SearchBody {
query: {
match: { foo: string }
}
}
// Complete definition of the Search response
interface ShardsResponse {
total: number;
successful: number;
failed: number;
skipped: number;
}
interface Explanation {
value: number;
description: string;
details: Explanation[];
}
interface SearchResponse<T> {
took: number;
timed_out: boolean;
_scroll_id?: string;
_shards: ShardsResponse;
hits: {
total: number;
max_score: number;
hits: Array<{
_index: string;
_type: string;
_id: string;
_score: number;
_source: T;
_version?: number;
_explanation?: Explanation;
fields?: any;
highlight?: any;
inner_hits?: any;
matched_queries?: string[];
sort?: string[];
}>;
};
aggregations?: any;
}
// Define the intefrace of the source object
interface Source {
foo: string
}
async function run (): Promise<void> {
// Define the search parameters
const searchParams: RequestParams.Search<SearchBody> = {
index: 'test',
body: {
query: {
match: { foo: 'bar' }
}
}
}
// Craft the final type definition
const response: ApiResponse<SearchResponse<Source>> = await client.search(searchParams)
console.log(response.body)
}
run().catch(console.log)
----

View File

@ -66,7 +66,7 @@ When using the callback style API, the function will also return an object that
[source,js]
----
// calback API
const requesty = client.search({
const request = client.search({
index: 'my-index',
body: { foo: 'bar' }
}, {
@ -302,4 +302,4 @@ export interface ResurrectEvent {
isAlive: boolean;
connection: Connection;
}
----
----

24
index.d.ts vendored
View File

@ -27,9 +27,10 @@ import Transport, {
TransportRequestParams,
TransportRequestOptions,
nodeFilterFn,
nodeSelectorFn
nodeSelectorFn,
TransportRequestCallback
} from './lib/Transport';
import Connection, { AgentOptions } from './lib/Connection';
import Connection, { AgentOptions, agentFn } from './lib/Connection';
import ConnectionPool, { ResurrectEvent } from './lib/ConnectionPool';
import Serializer from './lib/Serializer';
import * as RequestParams from './api/requestParams';
@ -38,12 +39,18 @@ import * as errors from './lib/errors';
declare type anyObject = {
[key: string]: any;
};
declare type callbackFn = (err: Error | null, result: ApiResponse) => void;
interface ApiMethod<T> {
(callback?: callbackFn): any;
(params: T, callback?: callbackFn): any;
(params: T, options: TransportRequestOptions, callback?: callbackFn): any;
declare type callbackFn<T> = (err: Error | null, result: ApiResponse<T>) => void;
interface ApiMethod<TParams, TBody = any> {
// Promise API
(): Promise<ApiResponse<TBody>>;
(params: TParams): Promise<ApiResponse<TBody>>;
(params: TParams, options: TransportRequestOptions): Promise<ApiResponse<TBody>>;
// Callback API
(callback: callbackFn<TBody>): TransportRequestCallback;
(params: TParams, callback: callbackFn<TBody>): TransportRequestCallback;
(params: TParams, options: TransportRequestOptions, callback: callbackFn<TBody>): TransportRequestCallback;
}
// Extend API
@ -82,7 +89,7 @@ interface ClientOptions {
suggestCompression?: boolean;
compression?: 'gzip';
ssl?: SecureContextOptions;
agent?: AgentOptions;
agent?: AgentOptions | agentFn;
nodeFilter?: nodeFilterFn;
nodeSelector?: nodeSelectorFn | string;
headers?: anyObject;
@ -315,5 +322,6 @@ export {
RequestEvent,
ResurrectEvent,
RequestParams,
ClientOptions,
ClientExtendsCallbackOptions
};

4
lib/Connection.d.ts vendored
View File

@ -24,12 +24,14 @@ import { inspect, InspectOptions } from 'util';
import * as http from 'http';
import { SecureContextOptions } from 'tls';
export declare type agentFn = () => any;
interface ConnectionOptions {
url: URL;
ssl?: SecureContextOptions;
id?: string;
headers?: any;
agent?: AgentOptions;
agent?: AgentOptions | agentFn;
status?: string;
roles?: any;
}

View File

@ -46,18 +46,20 @@ class Connection {
throw new ConfigurationError(`Invalid protocol: '${this.url.protocol}'`)
}
// Probably there is a bug in Node Core
// see https://github.com/nodejs/node/issues/26357
const keepAliveFalse = opts.agent && opts.agent.keepAlive === false
const agentOptions = Object.assign({}, {
keepAlive: true,
keepAliveMsecs: 1000,
maxSockets: keepAliveFalse ? Infinity : 256,
maxFreeSockets: 256
}, opts.agent)
this.agent = this.url.protocol === 'http:'
? new http.Agent(agentOptions)
: new https.Agent(Object.assign({}, agentOptions, this.ssl))
if (typeof opts.agent === 'function') {
this.agent = opts.agent()
} else {
const keepAliveFalse = opts.agent && opts.agent.keepAlive === false
const agentOptions = Object.assign({}, {
keepAlive: true,
keepAliveMsecs: 1000,
maxSockets: keepAliveFalse ? Infinity : 256,
maxFreeSockets: 256
}, opts.agent)
this.agent = this.url.protocol === 'http:'
? new http.Agent(agentOptions)
: new https.Agent(Object.assign({}, agentOptions, this.ssl))
}
this.makeRequest = this.url.protocol === 'http:'
? http.request

View File

@ -127,7 +127,7 @@ export default class ConnectionPool {
* @param {object} nodes
* @returns {array} hosts
*/
nodesToHost(nodes: any): any[];
nodesToHost(nodes: any, protocol: string): any[];
/**
* Transforms an url string to a host object
*

View File

@ -332,18 +332,33 @@ class ConnectionPool {
* @param {object} nodes
* @returns {array} hosts
*/
nodesToHost (nodes) {
nodesToHost (nodes, protocol) {
const ids = Object.keys(nodes)
const hosts = []
for (var i = 0, len = ids.length; i < len; i++) {
const node = nodes[ids[i]]
// If there is no protocol in
// the `publish_address` new URL wil throw
// the `publish_address` new URL will throw
// the publish_address can have two forms:
// - ip:port
// - hostname/ip:port
// if we encounter the second case, we should
// use the hostname instead of the ip
var address = node.http.publish_address
const hostAndIpRegex = /^[a-z0-9_.-]*\/\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b/gi
const match = address.match(hostAndIpRegex)
if (match !== null) {
const ipRegex = /\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b/
const ip = address.match(ipRegex)[0]
// extract the hostname, the -1 at the end removes the final /
const hostname = address.slice(0, address.indexOf(ip) - 1)
const port = address.split(':')[1]
address = `${hostname}:${port}`
}
address = address.slice(0, 4) === 'http'
? address
: 'http://' + address
: `${protocol}//${address}`
const roles = node.roles.reduce((acc, role) => {
acc[role] = true
return acc

11
lib/Transport.d.ts vendored
View File

@ -49,7 +49,7 @@ interface TransportOptions {
headers?: anyObject;
}
export interface RequestEvent {
export interface RequestEvent<T = any> {
body: any;
statusCode: number | null;
headers: anyObject | null;
@ -71,7 +71,7 @@ export interface RequestEvent {
// ApiResponse and RequestEvent are the same thing
// we are doing this for have more clear names
export interface ApiResponse extends RequestEvent {}
export interface ApiResponse<T = any> extends RequestEvent<T> {}
declare type anyObject = {
[key: string]: any;
@ -96,6 +96,10 @@ export interface TransportRequestOptions {
warnings?: [string];
}
export interface TransportRequestCallback {
abort: () => void;
}
export default class Transport {
static sniffReasons: {
SNIFF_ON_START: string;
@ -117,7 +121,8 @@ export default class Transport {
_nextSniff: number;
_isSniffing: boolean;
constructor(opts: TransportOptions);
request(params: TransportRequestParams, options: TransportRequestOptions, callback: (err: Error | null, result: ApiResponse) => void): any;
request(params: TransportRequestParams, options?: TransportRequestOptions): Promise<ApiResponse>;
request(params: TransportRequestParams, options?: TransportRequestOptions, callback?: (err: Error | null, result: ApiResponse) => void): TransportRequestCallback;
getConnection(): Connection | null;
sniff(callback?: (...args: any[]) => void): void;
}

View File

@ -353,7 +353,8 @@ class Transport {
}
debug('Sniffing ended successfully', result.body)
const hosts = this.connectionPool.nodesToHost(result.body.nodes)
const protocol = result.meta.connection.url.protocol || 'http:'
const hosts = this.connectionPool.nodesToHost(result.body.nodes, protocol)
this.connectionPool.update(hosts)
result.meta.sniff = { hosts, reason }

View File

@ -4,7 +4,7 @@
"main": "index.js",
"types": "index.d.ts",
"homepage": "http://www.elastic.co/guide/en/elasticsearch/client/javascript-api/current/index.html",
"version": "5.6.16-rc.1",
"version": "5.6.16-rc.2",
"keywords": [
"elasticsearch",
"elastic",
@ -36,33 +36,33 @@
"company": "Elasticsearch BV"
},
"devDependencies": {
"@types/node": "^10.12.24",
"codecov": "^3.2.0",
"convert-hrtime": "^2.0.0",
"@types/node": "^11.13.4",
"codecov": "^3.3.0",
"convert-hrtime": "^3.0.0",
"dedent": "^0.7.0",
"deepmerge": "^3.1.0",
"deepmerge": "^3.2.0",
"dezalgo": "^1.0.3",
"js-yaml": "^3.12.1",
"js-yaml": "^3.13.1",
"license-checker": "^25.0.1",
"lolex": "^3.1.0",
"minimist": "^1.2.0",
"ora": "^3.2.0",
"ora": "^3.4.0",
"pretty-hrtime": "^1.0.3",
"rimraf": "^2.6.3",
"semver": "^5.6.0",
"simple-git": "^1.107.0",
"semver": "^6.0.0",
"simple-git": "^1.110.0",
"simple-statistics": "^7.0.2",
"split2": "^3.1.0",
"split2": "^3.1.1",
"standard": "^12.0.1",
"stoppable": "^1.1.0",
"tap": "^12.6.0",
"typescript": "^3.3.3",
"tap": "^12.6.1",
"typescript": "^3.4.3",
"workq": "^2.1.0"
},
"dependencies": {
"debug": "^4.1.1",
"decompress-response": "^4.0.0",
"into-stream": "^4.0.0",
"decompress-response": "^4.2.0",
"into-stream": "^5.1.0",
"ms": "^2.1.1",
"once": "^1.4.0",
"pump": "^3.0.0"
@ -76,6 +76,6 @@
"url": "https://github.com/elastic/elasticsearch-js/issues"
},
"engines": {
"node": ">=6"
"node": ">=8"
}
}

View File

@ -74,10 +74,10 @@ export interface Generic {
}
const code = `
export interface ${name[0].toUpperCase() + name.slice(1)} extends Generic {
export interface ${name[0].toUpperCase() + name.slice(1)}${body ? '<T = any>' : ''} extends Generic {
${partsArr.map(genLine).join('\n ')}
${paramsArr.map(genLine).join('\n ')}
${body ? `body${body.required ? '' : '?'}: any;` : ''}
${body ? `body${body.required ? '' : '?'}: T;` : ''}
}
`

View File

@ -93,6 +93,39 @@ test('Should update the connection pool', t => {
})
})
test('Should handle hostnames in publish_address', t => {
t.plan(10)
buildCluster({ hostPublishAddress: true }, ({ nodes, shutdown }) => {
const client = new Client({
node: nodes[Object.keys(nodes)[0]].url
})
t.strictEqual(client.connectionPool.connections.size, 1)
client.on(events.SNIFF, (err, request) => {
t.error(err)
t.strictEqual(
request.meta.sniff.reason,
Transport.sniffReasons.DEFAULT
)
})
// run the sniffer
client.transport.sniff((err, hosts) => {
t.error(err)
t.strictEqual(hosts.length, 4)
for (var i = 0; i < hosts.length; i++) {
// the first node will be an update of the existing one
t.strictEqual(hosts[i].url.hostname, 'localhost')
}
t.strictEqual(client.connectionPool.connections.size, 4)
})
t.teardown(shutdown)
})
})
test('Sniff interval', t => {
t.plan(10)

View File

@ -22,6 +22,7 @@
import {
Client,
ApiResponse,
RequestParams,
RequestEvent,
ResurrectEvent,
events,
@ -94,6 +95,40 @@ client.index({
.then((result: ApiResponse) => {})
.catch((err: Error) => {})
// --- Use generics ---
// Define the search parameters
interface SearchBody {
query: {
match: { foo: string }
}
}
const searchParams: RequestParams.Search<SearchBody> = {
index: 'test',
body: {
query: {
match: { foo: 'bar' }
}
}
}
// Dewfine the interface of the search response
interface SearchResponse<T> {
hits: {
hits: Array<{
_source: T;
}>
}
}
// Define the intefrace of the source object
interface Source {
foo: string
}
client.search(searchParams)
.then((response: ApiResponse<SearchResponse<Source>>) => console.log(response))
.catch((err: Error) => {})
// extend client
client.extend('namespace.method', (options: ClientExtendsCallbackOptions) => {
return function (params: any) {

View File

@ -282,41 +282,112 @@ test('API', t => {
})
t.test('nodesToHost', t => {
const pool = new ConnectionPool({ Connection })
const nodes = {
a1: {
http: {
publish_address: '127.0.0.1:9200'
t.test('publish_address as ip address', t => {
const pool = new ConnectionPool({ Connection })
const nodes = {
a1: {
http: {
publish_address: '127.0.0.1:9200'
},
roles: ['master', 'data', 'ingest']
},
roles: ['master', 'data', 'ingest']
},
a2: {
http: {
publish_address: '127.0.0.1:9202'
},
roles: ['master', 'data', 'ingest']
a2: {
http: {
publish_address: '127.0.0.1:9201'
},
roles: ['master', 'data', 'ingest']
}
}
}
t.deepEqual(pool.nodesToHost(nodes), [{
url: new URL('http://127.0.0.1:9200'),
id: 'a1',
roles: {
master: true,
data: true,
ingest: true,
ml: false
t.deepEqual(pool.nodesToHost(nodes, 'http:'), [{
url: new URL('http://127.0.0.1:9200'),
id: 'a1',
roles: {
master: true,
data: true,
ingest: true,
ml: false
}
}, {
url: new URL('http://127.0.0.1:9201'),
id: 'a2',
roles: {
master: true,
data: true,
ingest: true,
ml: false
}
}])
t.strictEqual(pool.nodesToHost(nodes, 'http:')[0].url.host, '127.0.0.1:9200')
t.strictEqual(pool.nodesToHost(nodes, 'http:')[1].url.host, '127.0.0.1:9201')
t.end()
})
t.test('publish_address as host/ip', t => {
const pool = new ConnectionPool({ Connection })
const nodes = {
a1: {
http: {
publish_address: 'example.com/127.0.0.1:9200'
},
roles: ['master', 'data', 'ingest']
},
a2: {
http: {
publish_address: 'example.com/127.0.0.1:9201'
},
roles: ['master', 'data', 'ingest']
}
}
}, {
url: new URL('http://127.0.0.1:9201'),
id: 'a2',
roles: {
master: true,
data: true,
ingest: true,
ml: false
t.deepEqual(pool.nodesToHost(nodes, 'http:'), [{
url: new URL('http://example.com:9200'),
id: 'a1',
roles: {
master: true,
data: true,
ingest: true,
ml: false
}
}, {
url: new URL('http://example.com:9201'),
id: 'a2',
roles: {
master: true,
data: true,
ingest: true,
ml: false
}
}])
t.strictEqual(pool.nodesToHost(nodes, 'http:')[0].url.host, 'example.com:9200')
t.strictEqual(pool.nodesToHost(nodes, 'http:')[1].url.host, 'example.com:9201')
t.end()
})
t.test('Should use the configure protocol', t => {
const pool = new ConnectionPool({ Connection })
const nodes = {
a1: {
http: {
publish_address: 'example.com/127.0.0.1:9200'
},
roles: ['master', 'data', 'ingest']
},
a2: {
http: {
publish_address: 'example.com/127.0.0.1:9201'
},
roles: ['master', 'data', 'ingest']
}
}
}])
t.strictEqual(pool.nodesToHost(nodes, 'https:')[0].url.protocol, 'https:')
t.strictEqual(pool.nodesToHost(nodes, 'http:')[1].url.protocol, 'http:')
t.end()
})
t.end()
})

View File

@ -23,6 +23,7 @@ const { test } = require('tap')
const { inspect } = require('util')
const { createGzip, createDeflate } = require('zlib')
const { URL } = require('url')
const { Agent } = require('http')
const intoStream = require('into-stream')
const { buildServer } = require('../utils')
const Connection = require('../../lib/Connection')
@ -149,6 +150,55 @@ test('Basic (https with ssl agent)', t => {
})
})
test('Custom http agent', t => {
t.plan(5)
function handler (req, res) {
t.match(req.headers, {
'x-custom-test': 'true',
connection: 'keep-alive'
})
res.end('ok')
}
buildServer(handler, ({ port }, server) => {
const agent = new Agent({
keepAlive: true,
keepAliveMsecs: 1000,
maxSockets: 256,
maxFreeSockets: 256
})
agent.custom = true
const connection = new Connection({
url: new URL(`http://localhost:${port}`),
agent: () => agent
})
t.true(connection.agent.custom)
connection.request({
path: '/hello',
method: 'GET',
headers: {
'X-Custom-Test': true
}
}, (err, res) => {
t.error(err)
t.match(res.headers, {
connection: 'keep-alive'
})
var payload = ''
res.setEncoding('utf8')
res.on('data', chunk => { payload += chunk })
res.on('error', err => t.fail(err))
res.on('end', () => {
t.strictEqual(payload, 'ok')
server.stop()
})
})
})
})
test('Disable keep alive', t => {
t.plan(3)

View File

@ -53,12 +53,14 @@ function buildCluster (options, callback) {
buildServer(options.handler || handler, ({ port }, server) => {
nodes[opts.id] = {
url: `http://localhost:${port}`,
url: `http://127.0.0.1:${port}`,
server
}
sniffResult.nodes[opts.id] = {
http: {
publish_address: `http://localhost:${port}`
publish_address: options.hostPublishAddress
? `localhost/127.0.0.1:${port}`
: `127.0.0.1:${port}`
},
roles: ['master', 'data', 'ingest']
}