Skip to content

Commit

Permalink
Refactored type definitions (#1119)
Browse files Browse the repository at this point in the history
* Updated types generation script

* Refactored api method definitions

* Updated test
- Removed old test code
- Added tsd dev dependency
- Rewritten test with tsd

* Removed unused dependencies

* Fixed definition

* Updated test

* Updated docs

* Improved events type definitions

* Updated test

* Minor fixes in the type definitons

* More type test

* Improved Transport type definitions

* Updated test

* Addressed comments

* Code generation

* Use RequestBody, Response and Context everywhere, also default Context to unknown

* Updated test

* body -> hasBody

* Fixed conflicts

* Updated code generation

* Improved request body type definition

* Updated code generation

* Use BodyType for both request and reponses generics
- Use extends for defining the RequestBody generic to force the user
  following the same shape.
- BodyType and NDBodyType now accepts a generics to allow injecting
  more specific types in the future

* API generation

* Updated test

* Updated docs

* Use BodyType also in ReponseError

* Removed useless client generics

* Renamed generics and types
- prefixed all generics with a T
- BodyType => RequestBody
- NDBodyType => RequestNDBody
- Added ResponseBody

* Updated test

* Updated docs

* Test ResponseBody as well

* Simplify overloads

* API generation

* Updated test

* Updated error types
  • Loading branch information
delvedor committed Mar 23, 2020
1 parent 9892d63 commit 85674fe
Show file tree
Hide file tree
Showing 23 changed files with 4,071 additions and 631 deletions.
408 changes: 308 additions & 100 deletions api/requestParams.d.ts

Large diffs are not rendered by default.

47 changes: 39 additions & 8 deletions docs/typescript.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,14 @@ 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`.
will default to `RequestBody`. +
`RequestBody`, along with `RequestNDBody` and `ResponseBody` are defined inside the client, and it looks like this:
[source,ts]
----
type RequestBody<T = Record<string, any>> = T | string | Buffer | ReadableStream
type RequestNDBody<T = Record<string, any>[]> = T | string[] | Buffer | ReadableStream
type ResponseBody<T = Record<string, any>> = T | string | boolean | ReadableStream
----

For example:

Expand Down Expand Up @@ -49,7 +56,7 @@ const searchParams: RequestParams.Search = {

You can find the type definiton of a response in `ApiResponse`, which accepts a
generics as well if you want to specify the body type, otherwise it defaults to
`any`.
`BodyType`.

[source,ts]
----
Expand Down Expand Up @@ -137,19 +144,43 @@ interface Source {
foo: string
}
async function run (): Promise<void> {
// Define the search parameters
const searchParams: RequestParams.Search<SearchBody> = {
async function run () {
// All of the examples below are valid code, by default,
// the request body will be `RequestBody` and response will be `ResponseBody`.
const response = await client.search({
index: 'test',
body: {
query: {
match: { foo: 'bar' }
}
}
}
})
// body here is `ResponseBody`
console.log(response.body)
// Craft the final type definition
const response: ApiResponse<SearchResponse<Source>> = await client.search(searchParams)
// The first generic is the request body
const response = await client.search<SearchBody>({
index: 'test',
// Here the body must follow the `SearchBody` interface
body: {
query: {
match: { foo: 'bar' }
}
}
})
// body here is `ResponseBody`
console.log(response.body)
const response = await client.search<SearchBody, SearchResponse<Source>>({
index: 'test',
// Here the body must follow the `SearchBody` interface
body: {
query: {
match: { foo: 'bar' }
}
}
})
// Now you can have full type definition of the response
console.log(response.body)
}
Expand Down
2,553 changes: 2,295 additions & 258 deletions index.d.ts

Large diffs are not rendered by default.

67 changes: 37 additions & 30 deletions lib/Connection.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,35 +5,43 @@
/// <reference types="node" />

import { URL } from 'url';
import { inspect, InspectOptions } from 'util';
import { inspect, InspectOptions } from 'util'
import { Readable as ReadableStream } from 'stream';
import { ApiKeyAuth, BasicAuth } from './pool'
import * as http from 'http';
import { ConnectionOptions as TlsConnectionOptions } from 'tls';
import * as http from 'http'
import { ConnectionOptions as TlsConnectionOptions } from 'tls'

export declare type agentFn = () => any;

interface ConnectionOptions {
url: URL;
ssl?: TlsConnectionOptions;
id?: string;
headers?: any;
headers?: Record<string, any>;
agent?: AgentOptions | agentFn;
status?: string;
roles?: any;
roles?: ConnectionRoles;
auth?: BasicAuth | ApiKeyAuth;
}

interface ConnectionRoles {
master?: boolean
data?: boolean
ingest?: boolean
ml?: boolean
}

interface RequestOptions extends http.ClientRequestArgs {
asStream?: boolean;
body?: any;
body?: string | Buffer | ReadableStream;
querystring?: string;
}

export interface AgentOptions {
keepAlive: boolean;
keepAliveMsecs: number;
maxSockets: number;
maxFreeSockets: number;
keepAlive?: boolean;
keepAliveMsecs?: number;
maxSockets?: number;
maxFreeSockets?: number;
}

export default class Connection {
Expand All @@ -47,27 +55,26 @@ export default class Connection {
INGEST: string;
ML: string;
};
url: URL;
ssl: TlsConnectionOptions | null;
id: string;
headers: any;
deadCount: number;
resurrectTimeout: number;
statuses: any;
roles: any;
makeRequest: any;
_openRequests: number;
_status: string;
_agent: http.Agent;
constructor(opts?: ConnectionOptions);
request(params: RequestOptions, callback: (err: Error | null, response: http.IncomingMessage | null) => void): http.ClientRequest;
close(): Connection;
setRole(role: string, enabled: boolean): Connection;
status: string;
buildRequestObject(params: any): http.ClientRequestArgs;
url: URL
ssl: TlsConnectionOptions | null
id: string
headers: Record<string, any>
status: string
roles: ConnectionRoles
deadCount: number
resurrectTimeout: number
makeRequest: any
_openRequests: number
_status: string
_agent: http.Agent
constructor(opts?: ConnectionOptions)
request(params: RequestOptions, callback: (err: Error | null, response: http.IncomingMessage | null) => void): http.ClientRequest
close(): Connection
setRole(role: string, enabled: boolean): Connection
buildRequestObject(params: any): http.ClientRequestArgs
// @ts-ignore
[inspect.custom](object: any, options: InspectOptions): string;
toJSON(): any;
[inspect.custom](object: any, options: InspectOptions): string
toJSON(): any
}

export {};
60 changes: 31 additions & 29 deletions lib/Transport.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,16 @@
// Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
// See the LICENSE file in the project root for more information

import { Readable as ReadableStream } from 'stream';
import { ConnectionPool, CloudConnectionPool } from './pool';
import Connection from './Connection';
import Serializer from './Serializer';
import * as errors from './errors';

export type ApiError = errors.ConfigurationError | errors.ConnectionError |
errors.DeserializationError | errors.SerializationError |
errors.NoLivingConnectionsError | errors.ResponseError |
errors.TimeoutError

export interface nodeSelectorFn {
(connections: Connection[]): Connection;
Expand All @@ -18,36 +25,33 @@ export interface generateRequestIdFn {
(params: TransportRequestParams, options: TransportRequestOptions): any;
}

declare type noopFn = (...args: any[]) => void;
declare type emitFn = (event: string | symbol, ...args: any[]) => boolean;

interface TransportOptions {
emit: emitFn & noopFn;
emit: (event: string | symbol, ...args: any[]) => boolean;
connectionPool: ConnectionPool | CloudConnectionPool;
serializer: Serializer;
maxRetries: number;
requestTimeout: number | string;
suggestCompression: boolean;
suggestCompression?: boolean;
compression?: 'gzip';
sniffInterval: number;
sniffOnConnectionFault: boolean;
sniffInterval?: number;
sniffOnConnectionFault?: boolean;
sniffEndpoint: string;
sniffOnStart: boolean;
sniffOnStart?: boolean;
nodeFilter?: nodeFilterFn;
nodeSelector?: string | nodeSelectorFn;
headers?: anyObject;
headers?: Record<string, any>;
generateRequestId?: generateRequestIdFn;
name: string;
name?: string;
opaqueIdPrefix?: string;
}

export interface RequestEvent<T = any, C = any> {
body: T;
export interface RequestEvent<TResponse = ResponseBody, TContext = unknown> {
body: TResponse;
statusCode: number | null;
headers: anyObject | null;
headers: Record<string, any> | null;
warnings: string[] | null;
meta: {
context: C;
context: TContext;
name: string;
request: {
params: TransportRequestParams;
Expand All @@ -66,31 +70,31 @@ export interface RequestEvent<T = any, C = any> {

// ApiResponse and RequestEvent are the same thing
// we are doing this for have more clear names
export interface ApiResponse<T = any, C = any> extends RequestEvent<T, C> {}
export interface ApiResponse<TResponse = ResponseBody, TContext = unknown> extends RequestEvent<TResponse, TContext> {}

declare type anyObject = {
[key: string]: any;
};
export type RequestBody<T = Record<string, any>> = T | string | Buffer | ReadableStream
export type RequestNDBody<T = Record<string, any>[]> = T | string[] | Buffer | ReadableStream
export type ResponseBody<T = Record<string, any>> = T | string | boolean | ReadableStream

export interface TransportRequestParams {
method: string;
path: string;
body?: anyObject;
bulkBody?: anyObject;
querystring?: anyObject;
body?: RequestBody;
bulkBody?: RequestNDBody;
querystring?: Record<string, any>;
}

export interface TransportRequestOptions {
ignore?: number[];
requestTimeout?: number | string;
maxRetries?: number;
asStream?: boolean;
headers?: anyObject;
querystring?: anyObject;
compression?: string;
headers?: Record<string, any>;
querystring?: Record<string, any>;
compression?: 'gzip';
id?: any;
context?: any;
warnings?: [string];
warnings?: string[];
opaqueId?: string;
}

Expand All @@ -114,7 +118,7 @@ export default class Transport {
SNIFF_ON_CONNECTION_FAULT: string;
DEFAULT: string;
};
emit: emitFn & noopFn;
emit: (event: string | symbol, ...args: any[]) => boolean;
connectionPool: ConnectionPool | CloudConnectionPool;
serializer: Serializer;
maxRetries: number;
Expand All @@ -130,9 +134,7 @@ export default class Transport {
_isSniffing: boolean;
constructor(opts: TransportOptions);
request(params: TransportRequestParams, options?: TransportRequestOptions): Promise<ApiResponse>;
request(params: TransportRequestParams, options?: TransportRequestOptions, callback?: (err: Error | null, result: ApiResponse) => void): TransportRequestCallback;
request(params: TransportRequestParams, options?: TransportRequestOptions, callback?: (err: ApiError, result: ApiResponse) => void): TransportRequestCallback;
getConnection(opts: TransportGetConnectionOptions): Connection | null;
sniff(opts?: TransportSniffOptions, callback?: (...args: any[]) => void): void;
}

export {};
10 changes: 5 additions & 5 deletions lib/errors.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
// See the LICENSE file in the project root for more information

import { ApiResponse } from './Transport'
import { ApiResponse, ResponseBody } from './Transport'

export declare class ElasticsearchClientError extends Error {
name: string;
Expand Down Expand Up @@ -33,8 +33,8 @@ export declare class NoLivingConnectionsError extends ElasticsearchClientError {
export declare class SerializationError extends ElasticsearchClientError {
name: string;
message: string;
data: object;
constructor(message: string, data: object);
data: any;
constructor(message: string, data: any);
}

export declare class DeserializationError extends ElasticsearchClientError {
Expand All @@ -54,8 +54,8 @@ export declare class ResponseError extends ElasticsearchClientError {
name: string;
message: string;
meta: ApiResponse;
body: any;
body: ResponseBody;
statusCode: number;
headers: any;
headers: Record<string, any>;
constructor(meta: ApiResponse);
}
11 changes: 6 additions & 5 deletions lib/pool/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

/// <reference types="node" />

import { URL } from 'url'
import { SecureContextOptions } from 'tls';
import Connection, { AgentOptions } from '../Connection';
import { nodeFilterFn, nodeSelectorFn } from '../Transport';
Expand All @@ -13,14 +14,12 @@ interface BaseConnectionPoolOptions {
agent?: AgentOptions;
auth?: BasicAuth | ApiKeyAuth;
emit: (event: string | symbol, ...args: any[]) => boolean;
pingTimeout?: number;
Connection: typeof Connection;
resurrectStrategy?: string;
}

interface ConnectionPoolOptions extends BaseConnectionPoolOptions {
pingTimeout?: number;
resurrectStrategy?: string;
resurrectStrategy?: 'ping' | 'optimistic' | 'none';
sniffEnabled?: boolean;
}

Expand Down Expand Up @@ -65,6 +64,8 @@ interface ResurrectEvent {

declare class BaseConnectionPool {
connections: Connection[];
size: number;
emit: (event: string | symbol, ...args: any[]) => boolean;
_ssl: SecureContextOptions | null;
_agent: AgentOptions | null;
auth: BasicAuth | ApiKeyAuth;
Expand Down Expand Up @@ -137,7 +138,7 @@ declare class BaseConnectionPool {
* @param {string} url
* @returns {object} host
*/
urlToHost(url: string): any;
urlToHost(url: string): { url: URL };
}

declare class ConnectionPool extends BaseConnectionPool {
Expand Down Expand Up @@ -167,7 +168,7 @@ declare class ConnectionPool extends BaseConnectionPool {
declare class CloudConnectionPool extends BaseConnectionPool {
cloudConnection: Connection | null
constructor(opts?: BaseConnectionPoolOptions);
getConnection(): Connection;
getConnection(): Connection | null;
}

declare function defaultNodeFilter(node: Connection): boolean;
Expand Down
Loading

0 comments on commit 85674fe

Please sign in to comment.