Skip to content

Commit

Permalink
initial commit.
Browse files Browse the repository at this point in the history
  • Loading branch information
tkaratug committed Jun 23, 2020
1 parent eb9f5b1 commit 9391a4c
Show file tree
Hide file tree
Showing 8 changed files with 442 additions and 0 deletions.
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/.idea
/bin
/vendor
/composer.lock
/phpunit.xml
/.phpunit.result.cache
93 changes: 93 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
# Elastic AWS Client

The official PHP Elasticsearch client for AWS Elasticsearch Service integrated with Laravel.

## Contents

* [Compatibility](#compatibility)
* [Installation](#installation)
* [Configuration](#configuration)
* [Usage](#usage)

## Compatibility

The current version of Elastic AWS Client has been tested with the following configuration:

* PHP 7.2-7.4
* Elasticsearch 7.x
* AWS-SDK-PHP ^3.80

## Installation

The library can be installed via Composer:

```bash
composer require tarfin-labs/elastic-aws-client
```

## Configuration

To change the client settings you need to publish the configuration file first:

```bash
php artisan vendor:publish --provider="ElasticAwsClient\ServiceProvider"
```

You can use a bunch of settings supported by [\Elasticsearch\ClientBuilder::fromConfig](https://www.elastic.co/guide/en/elasticsearch/client/php-api/current/configuration.html#_building_the_client_from_a_configuration_hash)
method in the `config/elastic-aws-client.php` file as this factory is used under the hood:

```php
return [
'hosts' => [
[
'host' => env('ELASTICSEARCH_HOST', 'localhost'),
'port' => env('ELASTICSEARCH_PORT', 9200),
'scheme' => env('ELASTICSEARCH_SCHEME', null),
'user' => env('ELASTICSEARCH_USER', null),
'pass' => env('ELASTICSEARCH_PASS', null),

// AWS
'aws' => env('AWS_ELASTICSEARCH_ENABLED', false),
'aws_region' => env('AWS_DEFAULT_REGION', ''),
'aws_key' => env('AWS_ACCESS_KEY_ID', ''),
'aws_secret' => env('AWS_SECRET_ACCESS_KEY', ''),
'aws_credentials' => null
],
],
'sslVerification' => null,
'retries' => null,
'sniffOnStart' => false,
'httpHandler' => null,
'connectionPool' => null,
'connectionSelector' => null,
'serializer' => null,
'connectionFactory' => null,
'endpoint' => null,
'namespaces' => [],
];
```

## Usage

Type hint `\Elasticsearch\Client` or use `resolve` function to retrieve the client instance in your code:

```php
namespace App\Console\Commands;

use Elasticsearch\Client;
use Illuminate\Console\Command;

class CreateIndex extends Command
{
protected $signature = 'create:index {name}';

protected $description = 'Creates an index';

public function handle(Client $client)
{
$client->indices()->create([
'index' => $this->argument('name')
]);
}
}
```
67 changes: 67 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
{
"name": "tarfin-labs/elastic-aws-client",
"description": "The official PHP Elasticsearch AWS client integrated with Laravel",
"keywords": [
"laravel",
"aws",
"elastic",
"elasticsearch",
"client",
"php"
],
"type": "library",
"license": "MIT",
"authors": [
{
"name": "Turan Karatuğ",
"email": "[email protected]",
"role": "Developer"
},
{
"name": "Faruk Can",
"email": "[email protected]",
"role": "Developer"
},
{
"name": "Yunus Emre Deligöz",
"email": "[email protected]",
"role": "Developer"
},
{
"name": "Hakan Özdemir",
"email": "[email protected]",
"role": "Developer"
}
],
"suggest": {
"aws/aws-sdk-php": "Required to connect to an Elasticsearch host on AWS (^3.80)"
},
"autoload": {
"psr-4": {
"ElasticAwsClient\\": "src"
}
},
"autoload-dev": {
"psr-4": {
"ElasticAwsClient\\Tests\\": "tests"
}
},
"require": {
"php": "^7.2",
"elasticsearch/elasticsearch": "^7.3"
},
"require-dev": {
"phpunit/phpunit": "^8.4",
"orchestra/testbench": "^4.3"
},
"config": {
"bin-dir": "bin"
},
"extra": {
"laravel": {
"providers": [
"ElasticAwsClient\\ServiceProvider"
]
}
}
}
31 changes: 31 additions & 0 deletions config/elastic-aws-client.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?php

return [
'hosts' => [
[
'host' => env('ELASTICSEARCH_HOST', 'localhost'),
'port' => env('ELASTICSEARCH_PORT', 9200),
'scheme' => env('ELASTICSEARCH_SCHEME', null),
'user' => env('ELASTICSEARCH_USER', null),
'pass' => env('ELASTICSEARCH_PASS', null),

// AWS
'aws' => env('AWS_ELASTICSEARCH_ENABLED', false),
'aws_region' => env('AWS_DEFAULT_REGION', ''),
'aws_key' => env('AWS_ACCESS_KEY_ID', ''),
'aws_secret' => env('AWS_SECRET_ACCESS_KEY', ''),
'aws_credentials' => null
],
],

'sslVerification' => null,
'retries' => null,
'sniffOnStart' => false,
'httpHandler' => null,
'connectionPool' => null,
'connectionSelector' => null,
'serializer' => null,
'connectionFactory' => null,
'endpoint' => null,
'namespaces' => [],
];
23 changes: 23 additions & 0 deletions phpunit.xml.dist
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/8.4/phpunit.xsd"
bootstrap="vendor/autoload.php"
executionOrder="depends,defects"
forceCoversAnnotation="true"
beStrictAboutCoversAnnotation="true"
beStrictAboutOutputDuringTests="true"
beStrictAboutTodoAnnotatedTests="true"
verbose="true"
colors="true">
<testsuites>
<testsuite name="unit">
<directory suffix="Test.php">tests/Unit</directory>
</testsuite>
</testsuites>

<filter>
<whitelist processUncoveredFilesFromWhitelist="true">
<directory suffix=".php">src</directory>
</whitelist>
</filter>
</phpunit>
134 changes: 134 additions & 0 deletions src/Factory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
<?php

namespace ElasticAwsClient;

use Elasticsearch\Client;
use Elasticsearch\ClientBuilder;
use Illuminate\Support\Arr;
use Psr\Log\LoggerInterface;
use Monolog\Logger;
use Monolog\Handler\StreamHandler;

class Factory
{
/**
* Map configuration array keys with ES ClientBuilder setters
*
* @var array
*/
protected $configMappings = [
'sslVerification' => 'setSSLVerification',
'sniffOnStart' => 'setSniffOnStart',
'retries' => 'setRetries',
'httpHandler' => 'setHandler',
'connectionPool' => 'setConnectionPool',
'connectionSelector' => 'setSelector',
'serializer' => 'setSerializer',
'connectionFactory' => 'setConnectionFactory',
'endpoint' => 'setEndpoint',
'namespaces' => 'registerNamespace',
];

/**
* Make the Elasticsearch client for the given named configuration, or
* the default client.
*
* @param array $config
*
* @return \Elasticsearch\Client|mixed
*/
public function make(array $config)
{
// Build the client
return $this->buildClient($config);
}

/**
* Build and configure an Elasticsearch client.
*
* @param array $config
*
* @return \Elasticsearch\Client
*/
protected function buildClient(array $config): Client
{
$clientBuilder = ClientBuilder::create();

// Configure hosts
$clientBuilder->setHosts($config['hosts']);

// Set additional client configuration
foreach ($this->configMappings as $key => $method) {
$value = Arr::get($config, $key);
if (is_array($value)) {
foreach ($value as $vItem) {
$clientBuilder->$method($vItem);
}
} elseif ($value !== null) {
$clientBuilder->$method($value);
}
}

// Configure handlers for any AWS hosts
foreach ($config['hosts'] as $host) {
if (isset($host['aws']) && $host['aws']) {
$clientBuilder->setHandler(function(array $request) use ($host) {
$psr7Handler = \Aws\default_http_handler();
$signer = new \Aws\Signature\SignatureV4('es', $host['aws_region']);
$request['headers']['Host'][0] = parse_url($request['headers']['Host'][0])['host'];

// Create a PSR-7 request from the array passed to the handler
$psr7Request = new \GuzzleHttp\Psr7\Request(
$request['http_method'],
(new \GuzzleHttp\Psr7\Uri($request['uri']))
->withScheme($request['scheme'])
->withHost($request['headers']['Host'][0]),
$request['headers'],
$request['body']
);

// Create the Credentials instance with the credentials from the environment
$credentials = new \Aws\Credentials\Credentials($host['aws_key'], $host['aws_secret']);
// check if the aws_credentials from config is set and if it contains a Credentials instance
if (!empty($host['aws_credentials']) && $host['aws_credentials'] instanceof \Aws\Credentials\Credentials) {
// Set the credentials as in config
$credentials = $host['aws_credentials'];
}

if (!empty($host['aws_credentials']) && $host['aws_credentials'] instanceof \Closure) {
// If it contains a closure you can obtain the credentials by invoking it
$credentials = $host['aws_credentials']()->wait();
}

// Sign the PSR-7 request
$signedRequest = $signer->signRequest(
$psr7Request,
$credentials
);

// Send the signed request to Amazon ES
/** @var \Psr\Http\Message\ResponseInterface $response */
$response = $psr7Handler($signedRequest)
->then(function(\Psr\Http\Message\ResponseInterface $response) {
return $response;
}, function($error) {
return $error['response'];
})
->wait();

// Convert the PSR-7 response to a RingPHP response
return new \GuzzleHttp\Ring\Future\CompletedFutureArray([
'status' => $response->getStatusCode(),
'headers' => $response->getHeaders(),
'body' => $response->getBody()->detach(),
'transfer_stats' => ['total_time' => 0],
'effective_url' => (string)$psr7Request->getUri(),
]);
});
}
}

// Build and return the client
return $clientBuilder->build();
}
}
Loading

0 comments on commit 9391a4c

Please sign in to comment.