Elasticsearch-php: Get final request url?

Created on 26 Feb 2014  路  8Comments  路  Source: elastic/elasticsearch-php

Hi,
is there a way to retrieve the final url that is generated and used to query ES?
I think this can be very useful for unit testing and debugging.

Thanks

Most helpful comment

Mocking would be possible, with caveats. There is no way to inject a different Guzzle right now, so you would have to mock out the entire Connection class and inject that instead.

Something like:

public class MockConnection implements Elasticsearch\Connections\ConnectionInterface
{
    public function __construct($hostDetails, $connectionParams, LoggerInterface $log, LoggerInterface $trace){}

    public function getTransportSchema(){}

    public function isAlive(){}

    public function markAlive(){}

    public function markDead(){}

    public function getLastRequestInfo(){}

    public function performRequest($method, $uri, $params = null, $body = null){}
}

$params['connectionClass'] = '\MyProject\MockConnection';
$client = new Elasticsearch\Client($params);

The caveat is that each Connection class does a certain amount of processing internal to the class (e.g. pre-pending the connection schema, adding URI parameters, setting curl options, etc). So mocking out a connection class is a good way to unit-test the data that your app is providing to the library...but not a great way to integration test the whole system.

Instead of implementing a complete mock connection, you could extend one of the existing connection classes and just call parent:: on every method, which would be act like a hazy middle-ground between unit and integration testing :)

I went ahead and implemented the "getLastRequest" functionality, since it has several useful side effects (exposing transport, etc).

The data is buried pretty deep inside the library (all the way down at the connection level), so it requires a fairly long/obnoxious API to surface the data.

But for stuff like debugging/unit testing, I think that is ok. This won't be used often in "normal" code.

$client = new Elasticsearch\Client();

$client->index(['index' => 'test', 'type' => 'test', 'id' => 1, 'body' => ['field' => 'value']]);
$last = $client->transport->getLastConnection()->getLastRequestInfo();

print_r($last);
(
    [request] => Array
        (
            [uri] => http://localhost:9200/test/test/1?
            [body] => {"field":"value"}
            [options] => Array
                (
                )
            [method] => PUT
        )
    [response] => Array
        (
            [body] => {"_index":"test","_type":"test","_id":"1","_version":1,"created":true}
            [status] => 201
        )
)

All 8 comments

Sorta. The debug and trace logs will provide that information, but it may not be the easiest to work with in unit testing environments. For example, I'm using it in this test to determine if HTTP Basic Auth headers were being properly sent (since the client doesn't expose the raw headers being sent over the wire):

https://github.com/elasticsearch/elasticsearch-php/blob/master/tests/Elasticsearch/Tests/ClientTest.php#L216

That test is using a virtual filesystem to collect the logs, but now that I'm thinking about it, a Monolog handler that emits to stdout or similar may be a better option. Will have to think about that...

Another possibility is adding a lastRequestInfo() method, which returns headers/URI/etc about the last request.

Thanks for the quick reply.
I think both the monolog solution AND the lastRequestInfo() could both be helpful in certain situations.
However the lastRequestInfo() was specifically what i was thinking about, and would really be great i think.

So I'm on the road at the moment, but I'll investigate the options once I get home, and chat with the other client devs to see if there is a way we can standardize it across the clients. Will keep you posted.

Hi, do you already have some news on this?
Maybe there's a possibility to do some mocking using the fact the client is based on Guzzle?

Mocking would be possible, with caveats. There is no way to inject a different Guzzle right now, so you would have to mock out the entire Connection class and inject that instead.

Something like:

public class MockConnection implements Elasticsearch\Connections\ConnectionInterface
{
    public function __construct($hostDetails, $connectionParams, LoggerInterface $log, LoggerInterface $trace){}

    public function getTransportSchema(){}

    public function isAlive(){}

    public function markAlive(){}

    public function markDead(){}

    public function getLastRequestInfo(){}

    public function performRequest($method, $uri, $params = null, $body = null){}
}

$params['connectionClass'] = '\MyProject\MockConnection';
$client = new Elasticsearch\Client($params);

The caveat is that each Connection class does a certain amount of processing internal to the class (e.g. pre-pending the connection schema, adding URI parameters, setting curl options, etc). So mocking out a connection class is a good way to unit-test the data that your app is providing to the library...but not a great way to integration test the whole system.

Instead of implementing a complete mock connection, you could extend one of the existing connection classes and just call parent:: on every method, which would be act like a hazy middle-ground between unit and integration testing :)

I went ahead and implemented the "getLastRequest" functionality, since it has several useful side effects (exposing transport, etc).

The data is buried pretty deep inside the library (all the way down at the connection level), so it requires a fairly long/obnoxious API to surface the data.

But for stuff like debugging/unit testing, I think that is ok. This won't be used often in "normal" code.

$client = new Elasticsearch\Client();

$client->index(['index' => 'test', 'type' => 'test', 'id' => 1, 'body' => ['field' => 'value']]);
$last = $client->transport->getLastConnection()->getLastRequestInfo();

print_r($last);
(
    [request] => Array
        (
            [uri] => http://localhost:9200/test/test/1?
            [body] => {"field":"value"}
            [options] => Array
                (
                )
            [method] => PUT
        )
    [response] => Array
        (
            [body] => {"_index":"test","_type":"test","_id":"1","_version":1,"created":true}
            [status] => 201
        )
)

Wow, super cool, works like a charm!
And indeed the long/obnoxious API is indeed not a problem.
Thanks a lot!

fuck

We are currently using cviebrock/laravel-elasticsearch. Because it presents a facade to the Elasticsearch client, it wasn't immediately obvious how @polyfractal's example should be translated to work within that environment. This thread shows up high in Google, so I'd like to share our solution:

// Get to `transport` through the facade's `connection` static method
$last = Elasticsearch::connection('default')->transport->lastConnection->getLastRequestInfo();

// Just the body (JSON string)
$body = $last['request']['body'];

// If you are inside a controller, this is prettier than dd()
return response( $body )->header('Content-Type', 'application/json');

Equivalent one-liner for debugging:

return response( Elasticsearch::connection('default')->transport->lastConnection->getLastRequestInfo()['request']['body'] )->header('Content-Type', 'application/json');

Hopefully this'll save some folks the dd() calls to figure it out.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

adoerrES picture adoerrES  路  5Comments

m9rco picture m9rco  路  3Comments

KarboniteKream picture KarboniteKream  路  5Comments

marksparrish picture marksparrish  路  6Comments

angelxmoreno picture angelxmoreno  路  3Comments