Elasticsearch-js: Connection.request params expected RequestOptions without querystring

Created on 21 Aug 2019  路  7Comments  路  Source: elastic/elasticsearch-js

馃悰 Bug Report

The typing for Connection.request (defined here) states that params is of type RequestOptions.

At runtime, I observe the following object for an indices.exists call:

{ method: 'HEAD',
  path: '/_alias/idxname',
  body: null,
  querystring: '',
  headers:
   { 'User-Agent':
      'elasticsearch-js/7.3.0 (linux 4.19.57-microsoft-standard-x64; Node.js v10.14.1)' },

But RequestOptions has no attributes body and querystring. I expected to find those in TransportRequestParams instead. But TransportRequestParams also has headers missing, so it can't be it either (defined here).

To Reproduce

I ran into this when trying to sign requests with AWS credentials.

import { Connection as UnsignedConnection } from '@elastic/elasticsearch';
import * as AWS from 'aws-sdk';
import RequestSigner from 'aws-sdk/lib/signers/v4';
import { ClientRequest, RequestOptions, IncomingMessage } from 'http';

class AwsElasticsearchError extends Error {}


class AwsSignedConnection extends UnsignedConnection {
  public request(
    params: RequestOptions,
    callback: (err: Error | null, response: IncomingMessage | null) => void,
  ): ClientRequest {
    const signedParams = this.signParams(params);
    return super.request(signedParams, callback);
  }

  private signParams(params: any): RequestOptions {
    const region = AWS.config.region || process.env.AWS_DEFAULT_REGION;
    if (!region) {
      throw new AwsElasticsearchError('missing region configuration');
    }

    const endpoint = new AWS.Endpoint(this.url.href);
    const request = new AWS.HttpRequest(endpoint, region);

    request.method = params.method;
    request.path = params.querystring
      ? `${params.path}/?${params.querystring}`
      : params.path;
    request.body = params.body;

    request.headers = params.headers;
    request.headers.Host = endpoint.host;

    const signer = new RequestSigner(request, 'es');
    signer.addAuthorization(AWS.config.credentials, new Date());
    return request;
  }
}

export { AwsSignedConnection, UnsignedConnection, AwsElasticsearchError };

...

Expected behavior

No type errors.

Your Environment

  • node version: 10.14.1
  • @elastic/elasticsearch version: =7.3.0
  • os: Windows
bug typescript

All 7 comments

Hello, sorry for the delay! You are right, we should add body and querystring here: https://github.com/elastic/elasticsearch-js/blob/f7be49f2bafa3b9ddc90b0caac5b70932e6db35b/lib/Connection.d.ts#L26-L28

This is happening because the client is using the Node.js http.request API, which uses .end to send the body, while the querystring lives in search.

Would you mind open a pull request to fix this?

Hi @villasv I stuck on the same issue, I have to send a Signed Request to Aws elasticSearch.
For example in a simple request:

var endpoint = new AWS.Endpoint(domain);
var request = new AWS.HttpRequest(endpoint, region);

request.method = 'PUT';
request.path += index + '/' + type + '/' + id;
request.body = JSON.stringify(document);
request.headers['host'] = domain;
request.headers['Content-Type'] = 'application/json';
request.headers['Content-Length'] = Buffer.byteLength(request.body);

var credentials = new AWS.EnvironmentCredentials('AWS');
var signer = new AWS.Signers.V4(request, 'es');
signer.addAuthorization(credentials, new Date());


I don't think that Documentation are explaining it well.

Can you please help me in this thing ?

Hi @jawadmjn, here's the code I've been using:

import { Connection as UnsignedConnection } from '@elastic/elasticsearch';
import * as AWS from 'aws-sdk';
import RequestSigner from 'aws-sdk/lib/signers/v4';
import { ClientRequest, RequestOptions, IncomingMessage } from 'http';

class AwsElasticsearchError extends Error {}


class AwsSignedConnection extends UnsignedConnection {
  public request(
    params: RequestOptions,
    callback: (err: Error | null, response: IncomingMessage | null) => void,
  ): ClientRequest {
    const signedParams = this.signParams(params);
    return super.request(signedParams, callback);
  }

  private signParams(params: any): RequestOptions {
    const region = AWS.config.region || process.env.AWS_DEFAULT_REGION;
    if (!region) {
      throw new AwsElasticsearchError('missing region configuration');
    }

    const endpoint = new AWS.Endpoint(this.url.href);
    const request = new AWS.HttpRequest(endpoint, region);

    request.method = params.method;
    request.path = params.querystring
      ? `${params.path}/?${params.querystring}`
      : params.path;
    request.body = params.body;

    request.headers = params.headers;
    request.headers.Host = endpoint.host;

    const signer = new RequestSigner(request, 'es');
    signer.addAuthorization(AWS.config.credentials, new Date());
    return request;
  }
}

export { AwsSignedConnection, UnsignedConnection, AwsElasticsearchError };

@villasv thanks man I have this, and how are you doing this part, part of injecting this connection in the client,
what I have is

`

    let awsSignedConnection = new AwsSignedConnection(this.aws, this.esHost);

    let requestParams = {
        path: `/${this.index}/_search/`,
        method: "POST",
    };

    const client = new Client({
        node: this.esHost,
        Connection: awsSignedConnection.request(requestParams)
    });

    return client.search(params);

`

TS2345: Argument of type '{ node: any; Connection: ClientRequest; }' is not assignable to parameter of type 'ClientOptions'. 聽聽Types of property 'Connection' are incompatible. 聽聽聽聽Type 'ClientRequest' is missing the following properties from type 'typeof Connection': prototype, statuses, roles

That's not you're supposed to use connection classes. Here's an example:

  const esClient = new Client({
    Connection: AWS.config.credentials ? AwsSignedConnection : UnsignedConnection,
    node: 'http://localhost:9200', // esHost
  });

Then you can just use client.search(...) without worrying about signatures.

@villasv You are a rock star, It worked man. Thanks a lot 馃憤 :)

Was this page helpful?
0 / 5 - 0 ratings

Related issues

himanshuteotia picture himanshuteotia  路  4Comments

sjparkinson picture sjparkinson  路  5Comments

ablakey picture ablakey  路  4Comments

selvakn picture selvakn  路  4Comments

mfaizanse picture mfaizanse  路  3Comments