Skip to content

Switched request and response generics position #1132

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 14 commits into from
Apr 6, 2020
Merged
4,507 changes: 2,253 additions & 2,254 deletions index.d.ts

Large diffs are not rendered by default.

10 changes: 5 additions & 5 deletions lib/Helpers.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,17 @@
// See the LICENSE file in the project root for more information

import { Readable as ReadableStream } from 'stream'
import { TransportRequestOptions, ApiResponse, RequestBody, ResponseBody } from './Transport'
import { TransportRequestOptions, ApiResponse, RequestBody } from './Transport'
import { Search, Bulk } from '../api/requestParams'

export default class Helpers {
search<TRequestBody extends RequestBody, TDocument = unknown>(params: Search<TRequestBody>, options?: TransportRequestOptions): Promise<TDocument[]>
scrollSearch<TRequestBody extends RequestBody, TDocument = unknown, TResponse = ResponseBody, TContext = unknown>(params: Search<TRequestBody>, options?: TransportRequestOptions): AsyncIterable<ScrollSearchResponse<TDocument, TResponse, TContext>>
scrollDocuments<TRequestBody extends RequestBody, TDocument = unknown>(params: Search<TRequestBody>, options?: TransportRequestOptions): AsyncIterable<TDocument>
search<TDocument = unknown, TRequestBody extends RequestBody = Record<string, any>>(params: Search<TRequestBody>, options?: TransportRequestOptions): Promise<TDocument[]>
scrollSearch<TDocument = unknown, TResponse = Record<string, any>, TRequestBody extends RequestBody = Record<string, any>, TContext = unknown>(params: Search<TRequestBody>, options?: TransportRequestOptions): AsyncIterable<ScrollSearchResponse<TDocument, TResponse, TContext>>
scrollDocuments<TDocument = unknown, TRequestBody extends RequestBody = Record<string, any>>(params: Search<TRequestBody>, options?: TransportRequestOptions): AsyncIterable<TDocument>
bulk<TDocument = unknown>(options: BulkHelperOptions<TDocument>): BulkHelper<BulkStats>
}

export interface ScrollSearchResponse<TDocument = unknown, TResponse = ResponseBody, TContext = unknown> extends ApiResponse<TResponse, TContext> {
export interface ScrollSearchResponse<TDocument = unknown, TResponse = Record<string, any>, TContext = unknown> extends ApiResponse<TResponse, TContext> {
clear: () => Promise<void>
documents: TDocument[]
}
Expand Down
9 changes: 4 additions & 5 deletions lib/Transport.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ interface TransportOptions {
opaqueIdPrefix?: string;
}

export interface RequestEvent<TResponse = ResponseBody, TContext = unknown> {
export interface RequestEvent<TResponse = Record<string, any>, TContext = unknown> {
body: TResponse;
statusCode: number | null;
headers: Record<string, any> | null;
Expand All @@ -70,11 +70,10 @@ export interface RequestEvent<TResponse = ResponseBody, TContext = unknown> {

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

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 type RequestBody<T = Record<string, any>> = T | string | Buffer | ReadableStream
export type RequestNDBody<T = Record<string, any>[]> = T | string | string[] | Buffer | ReadableStream

export interface TransportRequestParams {
method: string;
Expand Down
24 changes: 12 additions & 12 deletions lib/errors.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,31 +2,31 @@
// 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, ResponseBody } from './Transport'
import { ApiResponse } from './Transport'

export declare class ElasticsearchClientError extends Error {
name: string;
message: string;
}

export declare class TimeoutError extends ElasticsearchClientError {
export declare class TimeoutError<TResponse = Record<string, any>, TContext = unknown> extends ElasticsearchClientError {
name: string;
message: string;
meta: ApiResponse;
meta: ApiResponse<TResponse, TContext>;
constructor(message: string, meta: ApiResponse);
}

export declare class ConnectionError extends ElasticsearchClientError {
export declare class ConnectionError<TResponse = Record<string, any>, TContext = unknown> extends ElasticsearchClientError {
name: string;
message: string;
meta: ApiResponse;
meta: ApiResponse<TResponse, TContext>;
constructor(message: string, meta: ApiResponse);
}

export declare class NoLivingConnectionsError extends ElasticsearchClientError {
export declare class NoLivingConnectionsError<TResponse = Record<string, any>, TContext = unknown> extends ElasticsearchClientError {
name: string;
message: string;
meta: ApiResponse;
meta: ApiResponse<TResponse, TContext>;
constructor(message: string, meta: ApiResponse);
}

Expand All @@ -50,19 +50,19 @@ export declare class ConfigurationError extends ElasticsearchClientError {
constructor(message: string);
}

export declare class ResponseError extends ElasticsearchClientError {
export declare class ResponseError<TResponse = Record<string, any>, TContext = unknown> extends ElasticsearchClientError {
name: string;
message: string;
meta: ApiResponse;
body: ResponseBody;
meta: ApiResponse<TResponse, TContext>;
body: TResponse;
statusCode: number;
headers: Record<string, any>;
constructor(meta: ApiResponse);
}

export declare class RequestAbortedError extends ElasticsearchClientError {
export declare class RequestAbortedError<TResponse = Record<string, any>, TContext = unknown> extends ElasticsearchClientError {
name: string;
message: string;
meta: ApiResponse;
meta: ApiResponse<TResponse, TContext>;
constructor(message: string, meta: ApiResponse);
}
33 changes: 17 additions & 16 deletions scripts/utils/generateMain.js
Original file line number Diff line number Diff line change
Expand Up @@ -180,36 +180,37 @@ function toPascalCase (str) {
function buildMethodDefinition (api, name, hasBody) {
const Name = toPascalCase(name)
const bodyType = ndjsonApiKey.includes(Name) ? 'RequestNDBody' : 'RequestBody'
const defaultBodyType = ndjsonApiKey.includes(Name) ? 'Record<string, any>[]' : 'Record<string, any>'

if (hasBody) {
let methods = [
{ key: `${api}<TRequestBody extends ${bodyType}, TResponse = ResponseBody, TContext = unknown>(params?: RequestParams.${Name}<TRequestBody>, options?: TransportRequestOptions)`, val: `TransportRequestPromise<ApiResponse<TResponse, TContext>>` },
{ key: `${api}<TRequestBody extends ${bodyType}, TResponse = ResponseBody, TContext = unknown>(callback: callbackFn<TResponse, TContext>)`, val: `TransportRequestCallback` },
{ key: `${api}<TRequestBody extends ${bodyType}, TResponse = ResponseBody, TContext = unknown>(params: RequestParams.${Name}<TRequestBody>, callback: callbackFn<TResponse, TContext>)`, val: `TransportRequestCallback` },
{ key: `${api}<TRequestBody extends ${bodyType}, TResponse = ResponseBody, TContext = unknown>(params: RequestParams.${Name}<TRequestBody>, options: TransportRequestOptions, callback: callbackFn<TResponse, TContext>)`, val: `TransportRequestCallback` }
{ key: `${api}<TResponse = Record<string, any>, TRequestBody extends ${bodyType} = ${defaultBodyType}, TContext = unknown>(params?: RequestParams.${Name}<TRequestBody>, options?: TransportRequestOptions)`, val: `TransportRequestPromise<ApiResponse<TResponse, TContext>>` },
{ key: `${api}<TResponse = Record<string, any>, TRequestBody extends ${bodyType} = ${defaultBodyType}, TContext = unknown>(callback: callbackFn<TResponse, TContext>)`, val: `TransportRequestCallback` },
{ key: `${api}<TResponse = Record<string, any>, TRequestBody extends ${bodyType} = ${defaultBodyType}, TContext = unknown>(params: RequestParams.${Name}<TRequestBody>, callback: callbackFn<TResponse, TContext>)`, val: `TransportRequestCallback` },
{ key: `${api}<TResponse = Record<string, any>, TRequestBody extends ${bodyType} = ${defaultBodyType}, TContext = unknown>(params: RequestParams.${Name}<TRequestBody>, options: TransportRequestOptions, callback: callbackFn<TResponse, TContext>)`, val: `TransportRequestCallback` }
]
if (isSnakeCased(api)) {
methods = methods.concat([
{ key: `${camelify(api)}<TRequestBody extends ${bodyType}, TResponse = ResponseBody, TContext = unknown>(params?: RequestParams.${Name}<TRequestBody>, options?: TransportRequestOptions)`, val: `TransportRequestPromise<ApiResponse<TResponse, TContext>>` },
{ key: `${camelify(api)}<TRequestBody extends ${bodyType}, TResponse = ResponseBody, TContext = unknown>(callback: callbackFn<TResponse, TContext>)`, val: `TransportRequestCallback` },
{ key: `${camelify(api)}<TRequestBody extends ${bodyType}, TResponse = ResponseBody, TContext = unknown>(params: RequestParams.${Name}<TRequestBody>, callback: callbackFn<TResponse, TContext>)`, val: `TransportRequestCallback` },
{ key: `${camelify(api)}<TRequestBody extends ${bodyType}, TResponse = ResponseBody, TContext = unknown>(params: RequestParams.${Name}<TRequestBody>, options: TransportRequestOptions, callback: callbackFn<TResponse, TContext>)`, val: `TransportRequestCallback` }
{ key: `${camelify(api)}<TResponse = Record<string, any>, TRequestBody extends ${bodyType} = ${defaultBodyType}, TContext = unknown>(params?: RequestParams.${Name}<TRequestBody>, options?: TransportRequestOptions)`, val: `TransportRequestPromise<ApiResponse<TResponse, TContext>>` },
{ key: `${camelify(api)}<TResponse = Record<string, any>, TRequestBody extends ${bodyType} = ${defaultBodyType}, TContext = unknown>(callback: callbackFn<TResponse, TContext>)`, val: `TransportRequestCallback` },
{ key: `${camelify(api)}<TResponse = Record<string, any>, TRequestBody extends ${bodyType} = ${defaultBodyType}, TContext = unknown>(params: RequestParams.${Name}<TRequestBody>, callback: callbackFn<TResponse, TContext>)`, val: `TransportRequestCallback` },
{ key: `${camelify(api)}<TResponse = Record<string, any>, TRequestBody extends ${bodyType} = ${defaultBodyType}, TContext = unknown>(params: RequestParams.${Name}<TRequestBody>, options: TransportRequestOptions, callback: callbackFn<TResponse, TContext>)`, val: `TransportRequestCallback` }
])
}
return methods
} else {
let methods = [
{ key: `${api}<TResponse = ResponseBody, TContext = unknown>(params?: RequestParams.${Name}, options?: TransportRequestOptions)`, val: `TransportRequestPromise<ApiResponse<TResponse, TContext>>` },
{ key: `${api}<TResponse = ResponseBody, TContext = unknown>(callback: callbackFn<TResponse, TContext>)`, val: `TransportRequestCallback` },
{ key: `${api}<TResponse = ResponseBody, TContext = unknown>(params: RequestParams.${Name}, callback: callbackFn<TResponse, TContext>)`, val: `TransportRequestCallback` },
{ key: `${api}<TResponse = ResponseBody, TContext = unknown>(params: RequestParams.${Name}, options: TransportRequestOptions, callback: callbackFn<TResponse, TContext>)`, val: `TransportRequestCallback` }
{ key: `${api}<TResponse = Record<string, any>, TContext = unknown>(params?: RequestParams.${Name}, options?: TransportRequestOptions)`, val: `TransportRequestPromise<ApiResponse<TResponse, TContext>>` },
{ key: `${api}<TResponse = Record<string, any>, TContext = unknown>(callback: callbackFn<TResponse, TContext>)`, val: `TransportRequestCallback` },
{ key: `${api}<TResponse = Record<string, any>, TContext = unknown>(params: RequestParams.${Name}, callback: callbackFn<TResponse, TContext>)`, val: `TransportRequestCallback` },
{ key: `${api}<TResponse = Record<string, any>, TContext = unknown>(params: RequestParams.${Name}, options: TransportRequestOptions, callback: callbackFn<TResponse, TContext>)`, val: `TransportRequestCallback` }
]
if (isSnakeCased(api)) {
methods = methods.concat([
{ key: `${camelify(api)}<TResponse = ResponseBody, TContext = unknown>(params?: RequestParams.${Name}, options?: TransportRequestOptions)`, val: `TransportRequestPromise<ApiResponse<TResponse, TContext>>` },
{ key: `${camelify(api)}<TResponse = ResponseBody, TContext = unknown>(callback: callbackFn<TResponse, TContext>)`, val: `TransportRequestCallback` },
{ key: `${camelify(api)}<TResponse = ResponseBody, TContext = unknown>(params: RequestParams.${Name}, callback: callbackFn<TResponse, TContext>)`, val: `TransportRequestCallback` },
{ key: `${camelify(api)}<TResponse = ResponseBody, TContext = unknown>(params: RequestParams.${Name}, options: TransportRequestOptions, callback: callbackFn<TResponse, TContext>)`, val: `TransportRequestCallback` }
{ key: `${camelify(api)}<TResponse = Record<string, any>, TContext = unknown>(params?: RequestParams.${Name}, options?: TransportRequestOptions)`, val: `TransportRequestPromise<ApiResponse<TResponse, TContext>>` },
{ key: `${camelify(api)}<TResponse = Record<string, any>, TContext = unknown>(callback: callbackFn<TResponse, TContext>)`, val: `TransportRequestCallback` },
{ key: `${camelify(api)}<TResponse = Record<string, any>, TContext = unknown>(params: RequestParams.${Name}, callback: callbackFn<TResponse, TContext>)`, val: `TransportRequestCallback` },
{ key: `${camelify(api)}<TResponse = Record<string, any>, TContext = unknown>(params: RequestParams.${Name}, options: TransportRequestOptions, callback: callbackFn<TResponse, TContext>)`, val: `TransportRequestCallback` }
])
}
return methods
Expand Down
165 changes: 153 additions & 12 deletions test/types/api-response-body.test-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@
// See the LICENSE file in the project root for more information

import { expectType, expectError } from 'tsd'
import { ResponseBody } from '../../lib/Transport'
import { Client } from '../../'
import { Readable as ReadableStream } from 'stream';
import { TransportRequestCallback } from '../../lib/Transport'
import { Client, ApiError } from '../../'

const client = new Client({
node: 'http://localhost:9200'
Expand Down Expand Up @@ -59,15 +60,15 @@ interface Source {
foo: string
}

// Use a bad body
// body that does not respect the RequestBody constraint
expectError(
client.search({
index: 'hello',
body: 42
}).then(console.log)
)

// No generics
// No generics (promise style)
{
const response = await client.search({
index: 'test',
Expand All @@ -78,13 +79,13 @@ expectError(
}
})

expectType<ResponseBody>(response.body)
expectType<Record<string, any>>(response.body)
expectType<unknown>(response.meta.context)
}

// Define only the request body
// Define only the response body (promise style)
{
const response = await client.search<SearchBody>({
const response = await client.search<SearchResponse<Source>>({
index: 'test',
body: {
query: {
Expand All @@ -93,13 +94,13 @@ expectError(
}
})

expectType<ResponseBody>(response.body)
expectType<SearchResponse<Source>>(response.body)
expectType<unknown>(response.meta.context)
}

// Define request body and response body
// Define response body and request body (promise style)
{
const response = await client.search<SearchBody, SearchResponse<Source>>({
const response = await client.search<SearchResponse<Source>, SearchBody>({
index: 'test',
body: {
query: {
Expand All @@ -112,9 +113,9 @@ expectError(
expectType<unknown>(response.meta.context)
}

// Define request body, response body and the context
// Define response body, request body and the context (promise style)
{
const response = await client.search<SearchBody, SearchResponse<Source>, string>({
const response = await client.search<SearchResponse<Source>, SearchBody, string>({
index: 'test',
body: {
query: {
Expand All @@ -126,3 +127,143 @@ expectError(
expectType<SearchResponse<Source>>(response.body)
expectType<string>(response.meta.context)
}

// Send request body as string (promise style)
{
const response = await client.search({
index: 'test',
body: 'hello world'
})

expectType<Record<string, any>>(response.body)
expectType<unknown>(response.meta.context)
}

// Send request body as buffer (promise style)
{
const response = await client.search({
index: 'test',
body: Buffer.from('hello world')
})

expectType<Record<string, any>>(response.body)
expectType<unknown>(response.meta.context)
}

// Send request body as readable stream (promise style)
{
const response = await client.search({
index: 'test',
body: new ReadableStream()
})

expectType<Record<string, any>>(response.body)
expectType<unknown>(response.meta.context)
}

// No generics (callback style)
{
const result = client.search({
index: 'test',
body: {
query: {
match: { foo: 'bar' }
}
}
}, (err, response) => {
expectType<ApiError>(err)
expectType<Record<string, any>>(response.body)
expectType<unknown>(response.meta.context)
})
expectType<TransportRequestCallback>(result)
}

// Define only the response body (callback style)
{
const result = client.search<SearchResponse<Source>>({
index: 'test',
body: {
query: {
match: { foo: 'bar' }
}
}
}, (err, response) => {
expectType<ApiError>(err)
expectType<SearchResponse<Source>>(response.body)
expectType<unknown>(response.meta.context)
})
expectType<TransportRequestCallback>(result)
}

// Define response body and request body (callback style)
{
const result = client.search<SearchResponse<Source>, SearchBody>({
index: 'test',
body: {
query: {
match: { foo: 'bar' }
}
}
}, (err, response) => {
expectType<ApiError>(err)
expectType<SearchResponse<Source>>(response.body)
expectType<unknown>(response.meta.context)
})
expectType<TransportRequestCallback>(result)
}

// Define response body, request body and the context (callback style)
{
const result = client.search<SearchResponse<Source>, SearchBody, string>({
index: 'test',
body: {
query: {
match: { foo: 'bar' }
}
}
}, (err, response) => {
expectType<ApiError>(err)
expectType<SearchResponse<Source>>(response.body)
expectType<string>(response.meta.context)
})
expectType<TransportRequestCallback>(result)
}

// Send request body as string (callback style)
{
const result = client.search({
index: 'test',
body: 'hello world'
}, (err, response) => {
expectType<ApiError>(err)
expectType<Record<string, any>>(response.body)
expectType<unknown>(response.meta.context)
})
expectType<TransportRequestCallback>(result)
}

// Send request body as buffer (callback style)
{
const result = client.search({
index: 'test',
body: Buffer.from('hello world')
}, (err, response) => {
expectType<ApiError>(err)
expectType<Record<string, any>>(response.body)
expectType<unknown>(response.meta.context)
})
expectType<TransportRequestCallback>(result)
}

// Send request body as readable stream (callback style)
{
const result = client.search({
index: 'test',
body: new ReadableStream()
}, (err, response) => {
expectType<ApiError>(err)
expectType<Record<string, any>>(response.body)
expectType<unknown>(response.meta.context)
})
expectType<TransportRequestCallback>(result)
}
Loading