Updated abort behavior (#1141)
* Updated abort behavior - Support for aborting a request with the promise api - Aborting a request will cause a RequestAbortedError - Normalized Connection class errors, now every error returned is wrapped by the client errors constructors * Updated test * Updated docs * Updated code generation script * Renamed test * Code coverage * Avoid calling twice transport.request
This commit is contained in:
committed by
GitHub
parent
953a8033ab
commit
27a8e2a9bf
@ -210,7 +210,7 @@ test('Sniff on connection fault', t => {
|
||||
class MyConnection extends Connection {
|
||||
request (params, callback) {
|
||||
if (this.id === 'http://localhost:9200/') {
|
||||
callback(new Error('kaboom'), null)
|
||||
callback(new errors.ConnectionError('kaboom'), null)
|
||||
return {}
|
||||
} else {
|
||||
return super.request(params, callback)
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
|
||||
import { expectType } from 'tsd'
|
||||
import { Client, ApiError, ApiResponse, RequestEvent, ResurrectEvent } from '../../'
|
||||
import { TransportRequestCallback } from '../..//lib/Transport';
|
||||
import { TransportRequestCallback, TransportRequestPromise } from '../..//lib/Transport';
|
||||
|
||||
const client = new Client({
|
||||
node: 'http://localhost:9200'
|
||||
@ -39,6 +39,7 @@ client.on('resurrect', (err, meta) => {
|
||||
expectType<ApiResponse>(result)
|
||||
})
|
||||
expectType<TransportRequestCallback>(result)
|
||||
expectType<void>(result.abort())
|
||||
}
|
||||
|
||||
{
|
||||
@ -47,6 +48,7 @@ client.on('resurrect', (err, meta) => {
|
||||
expectType<ApiResponse>(result)
|
||||
})
|
||||
expectType<TransportRequestCallback>(result)
|
||||
expectType<void>(result.abort())
|
||||
}
|
||||
|
||||
{
|
||||
@ -55,37 +57,42 @@ client.on('resurrect', (err, meta) => {
|
||||
expectType<ApiResponse>(result)
|
||||
})
|
||||
expectType<TransportRequestCallback>(result)
|
||||
expectType<void>(result.abort())
|
||||
}
|
||||
|
||||
// Promise style
|
||||
{
|
||||
const promise = client.info()
|
||||
expectType<Promise<ApiResponse>>(promise)
|
||||
expectType<TransportRequestPromise<ApiResponse>>(promise)
|
||||
promise
|
||||
.then(result => expectType<ApiResponse>(result))
|
||||
.catch((err: ApiError) => expectType<ApiError>(err))
|
||||
expectType<void>(promise.abort())
|
||||
}
|
||||
|
||||
{
|
||||
const promise = client.info({ pretty: true })
|
||||
expectType<Promise<ApiResponse>>(promise)
|
||||
expectType<TransportRequestPromise<ApiResponse>>(promise)
|
||||
promise
|
||||
.then(result => expectType<ApiResponse>(result))
|
||||
.catch((err: ApiError) => expectType<ApiError>(err))
|
||||
expectType<void>(promise.abort())
|
||||
}
|
||||
|
||||
{
|
||||
const promise = client.info({ pretty: true }, { ignore: [404] })
|
||||
expectType<Promise<ApiResponse>>(promise)
|
||||
expectType<TransportRequestPromise<ApiResponse>>(promise)
|
||||
promise
|
||||
.then(result => expectType<ApiResponse>(result))
|
||||
.catch((err: ApiError) => expectType<ApiError>(err))
|
||||
expectType<void>(promise.abort())
|
||||
}
|
||||
|
||||
// Promise style with async await
|
||||
{
|
||||
const promise = client.info()
|
||||
expectType<Promise<ApiResponse>>(promise)
|
||||
expectType<TransportRequestPromise<ApiResponse>>(promise)
|
||||
expectType<void>(promise.abort())
|
||||
try {
|
||||
expectType<ApiResponse>(await promise)
|
||||
} catch (err) {
|
||||
@ -95,7 +102,8 @@ client.on('resurrect', (err, meta) => {
|
||||
|
||||
{
|
||||
const promise = client.info({ pretty: true })
|
||||
expectType<Promise<ApiResponse>>(promise)
|
||||
expectType<TransportRequestPromise<ApiResponse>>(promise)
|
||||
expectType<void>(promise.abort())
|
||||
try {
|
||||
expectType<ApiResponse>(await promise)
|
||||
} catch (err) {
|
||||
@ -105,7 +113,8 @@ client.on('resurrect', (err, meta) => {
|
||||
|
||||
{
|
||||
const promise = client.info({ pretty: true }, { ignore: [404] })
|
||||
expectType<Promise<ApiResponse>>(promise)
|
||||
expectType<TransportRequestPromise<ApiResponse>>(promise)
|
||||
expectType<void>(promise.abort())
|
||||
try {
|
||||
expectType<ApiResponse>(await promise)
|
||||
} catch (err) {
|
||||
|
||||
@ -81,3 +81,10 @@ const response = {
|
||||
expectType<number>(err.statusCode)
|
||||
expectType<Record<string, any>>(err.headers)
|
||||
}
|
||||
|
||||
{
|
||||
const err = new errors.RequestAbortedError('message', response)
|
||||
expectType<string>(err.name)
|
||||
expectType<string>(err.message)
|
||||
expectType<ApiResponse>(err.meta)
|
||||
}
|
||||
@ -135,7 +135,7 @@ test('Abort method (callback)', t => {
|
||||
})
|
||||
})
|
||||
|
||||
test('Abort is not supported in promises', t => {
|
||||
test('Abort method (promises)', t => {
|
||||
t.plan(2)
|
||||
|
||||
function handler (req, res) {
|
||||
@ -160,7 +160,7 @@ test('Abort is not supported in promises', t => {
|
||||
})
|
||||
.catch(t.fail)
|
||||
|
||||
t.type(request.abort, 'undefined')
|
||||
t.type(request.abort, 'function')
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@ -12,7 +12,7 @@ const { Agent } = require('http')
|
||||
const intoStream = require('into-stream')
|
||||
const { buildServer } = require('../utils')
|
||||
const Connection = require('../../lib/Connection')
|
||||
const { TimeoutError, ConfigurationError } = require('../../lib/errors')
|
||||
const { TimeoutError, ConfigurationError, RequestAbortedError } = require('../../lib/errors')
|
||||
|
||||
test('Basic (http)', t => {
|
||||
t.plan(4)
|
||||
@ -855,3 +855,48 @@ test('Should not add agent and ssl to the serialized connection', t => {
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
test('Abort a request syncronously', t => {
|
||||
t.plan(1)
|
||||
|
||||
function handler (req, res) {
|
||||
t.fail('The server should not be contacted')
|
||||
}
|
||||
|
||||
buildServer(handler, ({ port }, server) => {
|
||||
const connection = new Connection({
|
||||
url: new URL(`http://localhost:${port}`)
|
||||
})
|
||||
const request = connection.request({
|
||||
path: '/hello',
|
||||
method: 'GET'
|
||||
}, (err, res) => {
|
||||
t.ok(err instanceof RequestAbortedError)
|
||||
server.stop()
|
||||
})
|
||||
request.abort()
|
||||
})
|
||||
})
|
||||
|
||||
test('Abort a request asyncronously', t => {
|
||||
t.plan(1)
|
||||
|
||||
function handler (req, res) {
|
||||
// might be called or not
|
||||
res.end('ok')
|
||||
}
|
||||
|
||||
buildServer(handler, ({ port }, server) => {
|
||||
const connection = new Connection({
|
||||
url: new URL(`http://localhost:${port}`)
|
||||
})
|
||||
const request = connection.request({
|
||||
path: '/hello',
|
||||
method: 'GET'
|
||||
}, (err, res) => {
|
||||
t.ok(err instanceof RequestAbortedError)
|
||||
server.stop()
|
||||
})
|
||||
setImmediate(() => request.abort())
|
||||
})
|
||||
})
|
||||
|
||||
@ -80,3 +80,11 @@ test('ResponseError', t => {
|
||||
t.ok(err.headers)
|
||||
t.end()
|
||||
})
|
||||
|
||||
test('RequestAbortedError', t => {
|
||||
const err = new errors.RequestAbortedError()
|
||||
t.true(err instanceof Error)
|
||||
t.true(err instanceof errors.ElasticsearchClientError)
|
||||
t.true(err.hasOwnProperty('meta'))
|
||||
t.end()
|
||||
})
|
||||
|
||||
@ -21,7 +21,8 @@ const {
|
||||
TimeoutError,
|
||||
ResponseError,
|
||||
ConnectionError,
|
||||
ConfigurationError
|
||||
ConfigurationError,
|
||||
RequestAbortedError
|
||||
} = require('../../lib/errors')
|
||||
|
||||
const ConnectionPool = require('../../lib/pool/ConnectionPool')
|
||||
@ -88,6 +89,32 @@ test('Basic (promises support)', t => {
|
||||
.catch(t.fail)
|
||||
})
|
||||
|
||||
test('Basic - failing (promises support)', t => {
|
||||
t.plan(1)
|
||||
|
||||
const pool = new ConnectionPool({ Connection: MockConnectionTimeout })
|
||||
pool.addConnection('http://localhost:9200')
|
||||
|
||||
const transport = new Transport({
|
||||
emit: () => {},
|
||||
connectionPool: pool,
|
||||
serializer: new Serializer(),
|
||||
maxRetries: 3,
|
||||
requestTimeout: 30000,
|
||||
sniffInterval: false,
|
||||
sniffOnStart: false
|
||||
})
|
||||
|
||||
transport
|
||||
.request({
|
||||
method: 'GET',
|
||||
path: '/hello'
|
||||
})
|
||||
.catch(err => {
|
||||
t.ok(err instanceof TimeoutError)
|
||||
})
|
||||
})
|
||||
|
||||
test('Basic (options + promises support)', t => {
|
||||
t.plan(1)
|
||||
|
||||
@ -764,7 +791,7 @@ test('Should call resurrect on every request', t => {
|
||||
test('Should return a request aborter utility', t => {
|
||||
t.plan(1)
|
||||
|
||||
const pool = new ConnectionPool({ Connection, MockConnection })
|
||||
const pool = new ConnectionPool({ Connection: MockConnection })
|
||||
pool.addConnection({
|
||||
url: new URL('http://localhost:9200'),
|
||||
id: 'node1'
|
||||
@ -783,12 +810,11 @@ test('Should return a request aborter utility', t => {
|
||||
const request = transport.request({
|
||||
method: 'GET',
|
||||
path: '/hello'
|
||||
}, (_err, body) => {
|
||||
t.fail('Should not be called')
|
||||
}, (err, result) => {
|
||||
t.ok(err instanceof RequestAbortedError)
|
||||
})
|
||||
|
||||
request.abort()
|
||||
t.pass('ok')
|
||||
})
|
||||
|
||||
test('Retry mechanism and abort', t => {
|
||||
@ -819,8 +845,6 @@ test('Retry mechanism and abort', t => {
|
||||
emit: event => {
|
||||
if (event === 'request' && count++ > 0) {
|
||||
request.abort()
|
||||
server.stop()
|
||||
t.pass('ok')
|
||||
}
|
||||
},
|
||||
connectionPool: pool,
|
||||
@ -834,12 +858,48 @@ test('Retry mechanism and abort', t => {
|
||||
const request = transport.request({
|
||||
method: 'GET',
|
||||
path: '/hello'
|
||||
}, (e, { body }) => {
|
||||
t.fail('Should not be called')
|
||||
}, (err, result) => {
|
||||
t.ok(err instanceof RequestAbortedError)
|
||||
server.stop()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
test('Abort a request with the promise API', t => {
|
||||
t.plan(1)
|
||||
|
||||
const pool = new ConnectionPool({ Connection: MockConnection })
|
||||
pool.addConnection({
|
||||
url: new URL('http://localhost:9200'),
|
||||
id: 'node1'
|
||||
})
|
||||
|
||||
const transport = new Transport({
|
||||
emit: () => {},
|
||||
connectionPool: pool,
|
||||
serializer: new Serializer(),
|
||||
maxRetries: 3,
|
||||
requestTimeout: 30000,
|
||||
sniffInterval: false,
|
||||
sniffOnStart: false
|
||||
})
|
||||
|
||||
const request = transport.request({
|
||||
method: 'GET',
|
||||
path: '/hello'
|
||||
})
|
||||
|
||||
request
|
||||
.then(() => {
|
||||
t.fail('Should not be called')
|
||||
})
|
||||
.catch(err => {
|
||||
t.ok(err instanceof RequestAbortedError)
|
||||
})
|
||||
|
||||
request.abort()
|
||||
})
|
||||
|
||||
test('ResponseError', t => {
|
||||
t.plan(3)
|
||||
|
||||
|
||||
@ -6,7 +6,11 @@
|
||||
|
||||
const assert = require('assert')
|
||||
const { Connection } = require('../../index')
|
||||
const { TimeoutError } = require('../../lib/errors')
|
||||
const {
|
||||
ConnectionError,
|
||||
RequestAbortedError,
|
||||
TimeoutError
|
||||
} = require('../../lib/errors')
|
||||
const intoStream = require('into-stream')
|
||||
|
||||
class MockConnection extends Connection {
|
||||
@ -23,6 +27,8 @@ class MockConnection extends Connection {
|
||||
process.nextTick(() => {
|
||||
if (!aborted) {
|
||||
callback(null, stream)
|
||||
} else {
|
||||
callback(new RequestAbortedError(), null)
|
||||
}
|
||||
})
|
||||
return {
|
||||
@ -37,6 +43,8 @@ class MockConnectionTimeout extends Connection {
|
||||
process.nextTick(() => {
|
||||
if (!aborted) {
|
||||
callback(new TimeoutError('Request timed out', params), null)
|
||||
} else {
|
||||
callback(new RequestAbortedError(), null)
|
||||
}
|
||||
})
|
||||
return {
|
||||
@ -50,7 +58,9 @@ class MockConnectionError extends Connection {
|
||||
var aborted = false
|
||||
process.nextTick(() => {
|
||||
if (!aborted) {
|
||||
callback(new Error('Kaboom'), null)
|
||||
callback(new ConnectionError('Kaboom'), null)
|
||||
} else {
|
||||
callback(new RequestAbortedError(), null)
|
||||
}
|
||||
})
|
||||
return {
|
||||
@ -93,6 +103,8 @@ class MockConnectionSniff extends Connection {
|
||||
} else {
|
||||
callback(null, stream)
|
||||
}
|
||||
} else {
|
||||
callback(new RequestAbortedError(), null)
|
||||
}
|
||||
})
|
||||
return {
|
||||
@ -122,6 +134,8 @@ function buildMockConnection (opts) {
|
||||
process.nextTick(() => {
|
||||
if (!aborted) {
|
||||
callback(null, stream)
|
||||
} else {
|
||||
callback(new RequestAbortedError(), null)
|
||||
}
|
||||
})
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user