Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 61 additions & 22 deletions .docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@

## Content

- [Setup](#usage)
- [Setup](#setup)
- [Configuration](#configuration)
- [Usage](#usage)
- [Events](#events)
- [Monolog](#monolog)

This extension integrates the [ruflin/elastica](https://github.com/ruflin/Elastica) into Nette Framework.
Expand All @@ -15,53 +16,79 @@ For more information on how to use Elastica [read the official documentation](ht
```bash
composer require contributte/elastica
```
register extension

Elastica 8+ is built on top of [elastic/transport](https://github.com/elastic/elastic-transport-php), which needs a [PSR-18](https://www.php-fig.org/psr/psr-18/) HTTP client.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rewrite text

If you don't have one yet, install e.g. Guzzle (or `symfony/http-client`), it is discovered automatically.

```bash
composer require guzzlehttp/guzzle
```

Register extension

```neon
extensions:
elastica: Contributte\Elastica\DI\ElasticaExtension
```


## Configuration

Define at least one host, this would be minimal possible config.

```neon
elastica:

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's support multiple connections based on github.com/contributte/doctrine-dbal and github.com/contributte/doctrine-orm

config:
host: localhost
hosts:
- http://localhost:9200
```

Full config with all possible options.

```neon
elastica:
debug: %debugMode%

# PSR-3 logger passed to Elastica client (and elastic/transport), e.g. @monolog.logger
logger: null

config:
host: null
port: null
path: null
url: null
proxy: null
transport: null
compression: false
persistent: true
timeout: null
connections: []
roundRobin: null
retryOnConflict: 0
bigintConversion: null
hosts: [] # list of hosts, e.g. [http://es1:9200, https://user:pass@es2:9200]
cloud_id: null
api_key: null
username: null
password: null
auth_type: null
curl: []
headers: []
retries: null # defaults to number of hosts
retryOnConflict: 0
transport_config:

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

snake_case -> camelCase

http_client: null # PSR-18 client service, e.g. @httpClient
http_client_config: [] # adapter config, e.g. Elastic\Elasticsearch\Transport\Adapter\AdapterOptions
http_client_options: [] # options passed to the HTTP client adapter
node_pool: null # Elastic\Transport\NodePool\NodePoolInterface service
document:
autoPopulate: false
```

Extension does not pass any unset values to elastica so elastica defaults just work.
Take a look to [Elastica docs](https://elastica-docs.readthedocs.io/en/latest/client.html#client-configurations).
Take a look to [Elastica client configuration](https://github.com/ruflin/Elastica/blob/8.x/src/ClientConfiguration.php)
and [elastic/transport](https://github.com/elastic/elastic-transport-php) docs.

In docker environment you should use `host: elasticsearch` and `port: 9200` for example.
In docker environment you should use `hosts: [http://elasticsearch:9200]` for example.

### Upgrade from Elastica 7

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not needed


Elastica 8 replaced its own connection layer with the official Elasticsearch client, so the client configuration changed
(see [UPGRADE-8.0](https://github.com/ruflin/Elastica/blob/8.x/UPGRADE-8.0.md)).

| Before (Elastica 7) | After (Elastica 8+) |
|---------------------------------------------|-------------------------------------------------------------------|
| `host: es.local`, `port: 9200` | `hosts: [http://es.local:9200]` |
| `url: http://es.local:9200/` | `hosts: [http://es.local:9200]` |
| `connections: [{host: a}, {host: b}]` | `hosts: [http://a:9200, http://b:9200]` |
| `roundRobin: true` | `transport_config: node_pool: @nodePool` (e.g. `SimpleNodePool` with `RoundRobin` selector) |
| `username`, `password` | unchanged (or credentials in host URL, or `api_key`) |
| `auth_type`, `transport`, `proxy`, `curl`, `compression`, `persistent`, `timeout`, `headers`, `path`, `bigintConversion` | removed, configure the PSR-18 HTTP client instead (`transport_config: http_client` / `http_client_options`) |

Removed options are rejected by the configuration schema, so you get a clear error after upgrade.

## Usage

Expand All @@ -80,6 +107,18 @@ class YourService
}
```

## Events

`Contributte\Elastica\Client` triggers `onSuccess` and `onFailure` events for every request, which are used by the Tracy panel.

```php
use Elastic\Elasticsearch\Response\Elasticsearch;
use Psr\Http\Message\RequestInterface;

$client->onSuccess[] = function (Client $client, RequestInterface $request, Elasticsearch $response, float $time): void {};
$client->onFailure[] = function (Client $client, RequestInterface $request, Throwable $e, float $time): void {};
```

## Monolog

You can use monolog to log errors to kibana.
Expand Down
8 changes: 1 addition & 7 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,9 @@ jobs:
with:
php: "8.2"

test81:
name: "Nette Tester"
uses: contributte/.github/.github/workflows/nette-tester.yml@master
with:
php: "8.1"

testlower:
name: "Nette Tester"
uses: contributte/.github/.github/workflows/nette-tester.yml@master
with:
php: "8.1"
php: "8.2"
composer: "composer update --no-interaction --no-progress --prefer-dist --prefer-stable --prefer-lowest"
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,10 @@ For details on how to use this package, check out our [documentation](.docs).

## Versions

| State | Version | Branch | Nette | PHP |
|-------------|---------|----------|-------|---------|
| dev | `^2.1` | `master` | 3.1+ | `>=8.1` |
| stable | `^2.0` | `master` | 3.1+ | `>=8.1` |
| State | Version | Branch | Nette | Elastica | PHP |
|-------------|---------|----------|-------|----------------|---------|
| dev | `^3.0` | `master` | 3.1+ | `^8.0 \|\| ^9.0` | `>=8.2` |
| stable | `^2.0` | `master` | 3.1+ | `^7.3` | `>=8.1` |

## Development

Expand Down
11 changes: 8 additions & 3 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@
}
],
"require": {
"php": ">=8.1",
"ruflin/elastica": "^7.3.1",

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Support only 9.0

"php": ">=8.2",
"ruflin/elastica": "^8.0 || ^9.0",
"nette/di": "^3.1.8",
"nette/utils": "^4.0.3"
},
Expand All @@ -29,10 +29,15 @@
"nette/http": "^3.2.3",
"nette/bootstrap": "^3.2.1",
"mockery/mockery": "^1.6.6",
"guzzlehttp/guzzle": "^7.8",
"contributte/qa": "^0.4",
"contributte/tester": "^0.4",
"contributte/phpstan": "^0.1"
},
"suggest": {
"guzzlehttp/guzzle": "PSR-18 HTTP client used by elastic/transport (or any other PSR-18 client, e.g. symfony/http-client)",
"tracy/tracy": "Tracy bar panel and BlueScreen panel"
},
"conflict": {
"nette/schema": "<1.2.0"
},
Expand All @@ -56,7 +61,7 @@
},
"extra": {
"branch-alias": {
"dev-master": "2.1.0-dev"
"dev-master": "3.0.0-dev"
}
}
}
58 changes: 1 addition & 57 deletions phpstan.neon
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ includes:

parameters:
level: 9
phpVersion: 80100
phpVersion: 80200

scanDirectories:
- src
Expand All @@ -14,59 +14,3 @@ parameters:
paths:
- src
- .docs

ignoreErrors:
-
message: "#^Method Contributte\\\\Elastica\\\\Client\\:\\:request\\(\\) has parameter \\$data with no value type specified in iterable type array\\.$#"
count: 1
path: src/Client.php

-
message: "#^Method Contributte\\\\Elastica\\\\Client\\:\\:request\\(\\) has parameter \\$query with no value type specified in iterable type array\\.$#"
count: 1
path: src/Client.php

-
message: "#^Argument of an invalid type mixed supplied for foreach, only iterables are supported\\.$#"
count: 1
path: src/Diagnostics/Panel.php

-
message: "#^Cannot access an offset on mixed\\.$#"
count: 2
path: src/Diagnostics/Panel.php

-
message: "#^Casting to string something that's already string\\.$#"
count: 1
path: src/Diagnostics/Panel.php

-
message: "#^Only booleans are allowed in a negated boolean, array given\\.$#"
count: 1
path: src/Diagnostics/Panel.php

-
message: "#^Only booleans are allowed in a ternary operator condition, string\\|null given\\.$#"
count: 1
path: src/Diagnostics/Panel.php

-
message: "#^Only booleans are allowed in an if condition, Elastica\\\\Response\\|null given\\.$#"
count: 1
path: src/Diagnostics/Panel.php

-
message: "#^Only booleans are allowed in an if condition, int given\\.$#"
count: 1
path: src/Diagnostics/Panel.php

-
message: "#^Return type \\(string\\|null\\) of method Contributte\\\\Elastica\\\\Diagnostics\\\\Panel\\:\\:getPanel\\(\\) should be covariant with return type \\(string\\) of method Tracy\\\\IBarPanel\\:\\:getPanel\\(\\)$#"
count: 1
path: src/Diagnostics/Panel.php

-
message: "#^Short ternary operator is not allowed\\. Use null coalesce operator if applicable or consider using long ternary\\.$#"
count: 1
path: src/Diagnostics/Panel.php
3 changes: 2 additions & 1 deletion ruleset.xml
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<ruleset name="Contributte" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="vendor/squizlabs/php_codesniffer/phpcs.xsd">
<!-- Rulesets -->
<rule ref="./vendor/contributte/qa/ruleset-8.0.xml"/>
<config name="php_version" value="80200"/>
<rule ref="./vendor/contributte/qa/ruleset.xml"/>

<!-- Rules -->
<rule ref="SlevomatCodingStandard.Files.TypeNameMatchesFileName">
Expand Down
46 changes: 35 additions & 11 deletions src/Client.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,40 +2,64 @@

namespace Contributte\Elastica;

use Elastic\Elasticsearch\Response\Elasticsearch;
use Elastica\Client as ElasticaClient;
use Elastica\Request;
use Elastica\Response;
use Error;
use Nette\SmartObject;
use Psr\Http\Message\RequestInterface;
use Throwable;

/**
* @method void onSuccess(Client $client, RequestInterface $request, Elasticsearch $response, float $time)
* @method void onFailure(Client $client, RequestInterface $request, Throwable $e, float $time)
*/
class Client extends ElasticaClient
{

use SmartObject;

/** @var callable[] */
/** @var array<callable(Client, RequestInterface, Elasticsearch, float): void> */
public array $onSuccess = [];

/** @var callable[] */
/** @var array<callable(Client, RequestInterface, Throwable, float): void> */
public array $onFailure = [];

/**
* {@inheritdoc}
*/
public function request(string $path, string $method = Request::GET, $data = [], array $query = [], string $contentType = Request::DEFAULT_CONTENT_TYPE): Response
public function sendRequest(RequestInterface $request): Elasticsearch
{
$start = microtime(true);
$previous = $this->getTransportLastRequest();

try {
$response = parent::request($path, $method, $data, $query, $contentType);
$this->onSuccess($this, $this->_lastRequest, $response, microtime(true) - $start);
$response = parent::sendRequest($request);
$this->onSuccess($this, $this->resolveSentRequest($request, $previous), $response, microtime(true) - $start);

return $response;
} catch (Throwable $e) {
$this->onFailure($this, $this->_lastRequest, $e, microtime(true) - $start);
$this->onFailure($this, $this->resolveSentRequest($request, $previous), $e, microtime(true) - $start);

throw $e;
}
}

/**
* Request built by elastica does not contain the node (host), it is resolved by transport,
* so prefer the request really sent by the transport.
*/
private function resolveSentRequest(RequestInterface $request, ?RequestInterface $previous): RequestInterface
{
$last = $this->getTransportLastRequest();

return $last !== null && $last !== $previous ? $last : $request;
}

private function getTransportLastRequest(): ?RequestInterface
{
try {
return $this->getTransport()->getLastRequest();
} catch (Error) {
// older elastic/transport 8.x throws on uninitialized typed property before the first request
return null;
}
}

}
Loading
Loading