diff --git a/.env.dist b/.env.dist index b764662..525d7ba 100644 --- a/.env.dist +++ b/.env.dist @@ -16,6 +16,9 @@ MYSQL_USER=chatterer MYSQL_PASSWORD=chatterer MYSQL_VERSION=8.0 +SEARCH_HOSTS=search:9200 +SEARCH_CREDENTIALS=elastic:P@ssw0rd + MERCURE_URL=http://hub/.well-known/mercure MERCURE_PUBLIC_URL=http://127.0.0.1:8080/.well-known/mercure MERCURE_PUBLISHER_JWT_KEY=!ChangeMeWithSomethingWithAtLeast32Chars! diff --git a/apps/api/composer.json b/apps/api/composer.json index 563732b..41d16f2 100644 --- a/apps/api/composer.json +++ b/apps/api/composer.json @@ -11,6 +11,7 @@ "doctrine/doctrine-bundle": "^2.7", "doctrine/doctrine-migrations-bundle": "^3.2", "doctrine/orm": "^2.12", + "elasticsearch/elasticsearch": "^8.12", "firebase/php-jwt": "^6.3", "ramsey/uuid": "^4.3", "symfony/console": "6.1.*", @@ -28,7 +29,8 @@ "allow-plugins": { "composer/package-versions-deprecated": true, "symfony/flex": true, - "symfony/runtime": true + "symfony/runtime": true, + "php-http/discovery": false }, "optimize-autoloader": true, "preferred-install": { diff --git a/apps/api/composer.lock b/apps/api/composer.lock index a9f6af3..bc71293 100644 --- a/apps/api/composer.lock +++ b/apps/api/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "5fb03fa7ff41450e04f133fa56618c61", + "content-hash": "e5cc852be34c9da3604e31c01bcf8c87", "packages": [ { "name": "beberlei/assert", @@ -1437,6 +1437,110 @@ }, "time": "2022-05-23T21:33:49+00:00" }, + { + "name": "elastic/transport", + "version": "v8.8.0", + "source": { + "type": "git", + "url": "git@github.com:elastic/elastic-transport-php.git", + "reference": "cdf9f63a16ec6bfb4c881ab89aa0e2a61fb7c20b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/elastic/elastic-transport-php/zipball/cdf9f63a16ec6bfb4c881ab89aa0e2a61fb7c20b", + "reference": "cdf9f63a16ec6bfb4c881ab89aa0e2a61fb7c20b", + "shasum": "" + }, + "require": { + "composer-runtime-api": "^2.0", + "php": "^7.4 || ^8.0", + "php-http/discovery": "^1.14", + "php-http/httplug": "^2.3", + "psr/http-client": "^1.0", + "psr/http-factory": "^1.0", + "psr/http-message": "^1.0 || ^2.0", + "psr/log": "^1 || ^2 || ^3" + }, + "require-dev": { + "nyholm/psr7": "^1.5", + "php-http/mock-client": "^1.5", + "phpstan/phpstan": "^1.4", + "phpunit/phpunit": "^9.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Elastic\\Transport\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "HTTP transport PHP library for Elastic products", + "keywords": [ + "PSR_17", + "elastic", + "http", + "psr-18", + "psr-7", + "transport" + ], + "time": "2023-11-08T10:51:51+00:00" + }, + { + "name": "elasticsearch/elasticsearch", + "version": "v8.12.0", + "source": { + "type": "git", + "url": "git@github.com:elastic/elasticsearch-php.git", + "reference": "6f4a2ee4ff2cb9f75a6ee00f574aa2d9c1bb14fd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/elastic/elasticsearch-php/zipball/6f4a2ee4ff2cb9f75a6ee00f574aa2d9c1bb14fd", + "reference": "6f4a2ee4ff2cb9f75a6ee00f574aa2d9c1bb14fd", + "shasum": "" + }, + "require": { + "elastic/transport": "^8.8", + "guzzlehttp/guzzle": "^7.0", + "php": "^7.4 || ^8.0", + "psr/http-client": "^1.0", + "psr/http-message": "^1.1 || ^2.0", + "psr/log": "^1|^2|^3" + }, + "require-dev": { + "ext-yaml": "*", + "ext-zip": "*", + "mockery/mockery": "^1.5", + "nyholm/psr7": "^1.5", + "php-http/mock-client": "^1.5", + "phpstan/phpstan": "^1.4", + "phpunit/phpunit": "^9.5", + "psr/http-factory": "^1.0", + "symfony/finder": "~4.0", + "symfony/http-client": "^5.0|^6.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Elastic\\Elasticsearch\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHP Client for Elasticsearch", + "keywords": [ + "client", + "elastic", + "elasticsearch", + "search" + ], + "time": "2024-01-26T14:58:39+00:00" + }, { "name": "firebase/php-jwt", "version": "v6.5.0", @@ -1582,6 +1686,331 @@ ], "time": "2023-01-30T10:40:19+00:00" }, + { + "name": "guzzlehttp/guzzle", + "version": "7.8.1", + "source": { + "type": "git", + "url": "https://github.com/guzzle/guzzle.git", + "reference": "41042bc7ab002487b876a0683fc8dce04ddce104" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/41042bc7ab002487b876a0683fc8dce04ddce104", + "reference": "41042bc7ab002487b876a0683fc8dce04ddce104", + "shasum": "" + }, + "require": { + "ext-json": "*", + "guzzlehttp/promises": "^1.5.3 || ^2.0.1", + "guzzlehttp/psr7": "^1.9.1 || ^2.5.1", + "php": "^7.2.5 || ^8.0", + "psr/http-client": "^1.0", + "symfony/deprecation-contracts": "^2.2 || ^3.0" + }, + "provide": { + "psr/http-client-implementation": "1.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "ext-curl": "*", + "php-http/client-integration-tests": "dev-master#2c025848417c1135031fdf9c728ee53d0a7ceaee as 3.0.999", + "php-http/message-factory": "^1.1", + "phpunit/phpunit": "^8.5.36 || ^9.6.15", + "psr/log": "^1.1 || ^2.0 || ^3.0" + }, + "suggest": { + "ext-curl": "Required for CURL handler support", + "ext-intl": "Required for Internationalized Domain Name (IDN) support", + "psr/log": "Required for using the Log middleware" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "autoload": { + "files": [ + "src/functions_include.php" + ], + "psr-4": { + "GuzzleHttp\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Jeremy Lindblom", + "email": "jeremeamia@gmail.com", + "homepage": "https://github.com/jeremeamia" + }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://github.com/sagikazarmark" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + } + ], + "description": "Guzzle is a PHP HTTP client library", + "keywords": [ + "client", + "curl", + "framework", + "http", + "http client", + "psr-18", + "psr-7", + "rest", + "web service" + ], + "support": { + "issues": "https://github.com/guzzle/guzzle/issues", + "source": "https://github.com/guzzle/guzzle/tree/7.8.1" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/guzzle", + "type": "tidelift" + } + ], + "time": "2023-12-03T20:35:24+00:00" + }, + { + "name": "guzzlehttp/promises", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/guzzle/promises.git", + "reference": "bbff78d96034045e58e13dedd6ad91b5d1253223" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/promises/zipball/bbff78d96034045e58e13dedd6ad91b5d1253223", + "reference": "bbff78d96034045e58e13dedd6ad91b5d1253223", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "phpunit/phpunit": "^8.5.36 || ^9.6.15" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Promise\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + } + ], + "description": "Guzzle promises library", + "keywords": [ + "promise" + ], + "support": { + "issues": "https://github.com/guzzle/promises/issues", + "source": "https://github.com/guzzle/promises/tree/2.0.2" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/promises", + "type": "tidelift" + } + ], + "time": "2023-12-03T20:19:20+00:00" + }, + { + "name": "guzzlehttp/psr7", + "version": "2.6.2", + "source": { + "type": "git", + "url": "https://github.com/guzzle/psr7.git", + "reference": "45b30f99ac27b5ca93cb4831afe16285f57b8221" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/45b30f99ac27b5ca93cb4831afe16285f57b8221", + "reference": "45b30f99ac27b5ca93cb4831afe16285f57b8221", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0", + "psr/http-factory": "^1.0", + "psr/http-message": "^1.1 || ^2.0", + "ralouphie/getallheaders": "^3.0" + }, + "provide": { + "psr/http-factory-implementation": "1.0", + "psr/http-message-implementation": "1.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "http-interop/http-factory-tests": "^0.9", + "phpunit/phpunit": "^8.5.36 || ^9.6.15" + }, + "suggest": { + "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Psr7\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://github.com/sagikazarmark" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://sagikazarmark.hu" + } + ], + "description": "PSR-7 message implementation that also provides common utility methods", + "keywords": [ + "http", + "message", + "psr-7", + "request", + "response", + "stream", + "uri", + "url" + ], + "support": { + "issues": "https://github.com/guzzle/psr7/issues", + "source": "https://github.com/guzzle/psr7/tree/2.6.2" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/psr7", + "type": "tidelift" + } + ], + "time": "2023-12-03T20:05:35+00:00" + }, { "name": "laminas/laminas-code", "version": "4.11.0", @@ -1720,6 +2149,193 @@ ], "time": "2023-02-25T21:35:16+00:00" }, + { + "name": "php-http/discovery", + "version": "1.19.2", + "source": { + "type": "git", + "url": "https://github.com/php-http/discovery.git", + "reference": "61e1a1eb69c92741f5896d9e05fb8e9d7e8bb0cb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-http/discovery/zipball/61e1a1eb69c92741f5896d9e05fb8e9d7e8bb0cb", + "reference": "61e1a1eb69c92741f5896d9e05fb8e9d7e8bb0cb", + "shasum": "" + }, + "require": { + "composer-plugin-api": "^1.0|^2.0", + "php": "^7.1 || ^8.0" + }, + "conflict": { + "nyholm/psr7": "<1.0", + "zendframework/zend-diactoros": "*" + }, + "provide": { + "php-http/async-client-implementation": "*", + "php-http/client-implementation": "*", + "psr/http-client-implementation": "*", + "psr/http-factory-implementation": "*", + "psr/http-message-implementation": "*" + }, + "require-dev": { + "composer/composer": "^1.0.2|^2.0", + "graham-campbell/phpspec-skip-example-extension": "^5.0", + "php-http/httplug": "^1.0 || ^2.0", + "php-http/message-factory": "^1.0", + "phpspec/phpspec": "^5.1 || ^6.1 || ^7.3", + "symfony/phpunit-bridge": "^6.2" + }, + "type": "composer-plugin", + "extra": { + "class": "Http\\Discovery\\Composer\\Plugin", + "plugin-optional": true + }, + "autoload": { + "psr-4": { + "Http\\Discovery\\": "src/" + }, + "exclude-from-classmap": [ + "src/Composer/Plugin.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com" + } + ], + "description": "Finds and installs PSR-7, PSR-17, PSR-18 and HTTPlug implementations", + "homepage": "http://php-http.org", + "keywords": [ + "adapter", + "client", + "discovery", + "factory", + "http", + "message", + "psr17", + "psr7" + ], + "support": { + "issues": "https://github.com/php-http/discovery/issues", + "source": "https://github.com/php-http/discovery/tree/1.19.2" + }, + "time": "2023-11-30T16:49:05+00:00" + }, + { + "name": "php-http/httplug", + "version": "2.4.0", + "source": { + "type": "git", + "url": "https://github.com/php-http/httplug.git", + "reference": "625ad742c360c8ac580fcc647a1541d29e257f67" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-http/httplug/zipball/625ad742c360c8ac580fcc647a1541d29e257f67", + "reference": "625ad742c360c8ac580fcc647a1541d29e257f67", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0", + "php-http/promise": "^1.1", + "psr/http-client": "^1.0", + "psr/http-message": "^1.0 || ^2.0" + }, + "require-dev": { + "friends-of-phpspec/phpspec-code-coverage": "^4.1 || ^5.0 || ^6.0", + "phpspec/phpspec": "^5.1 || ^6.0 || ^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Http\\Client\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Eric GELOEN", + "email": "geloen.eric@gmail.com" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://sagikazarmark.hu" + } + ], + "description": "HTTPlug, the HTTP client abstraction for PHP", + "homepage": "http://httplug.io", + "keywords": [ + "client", + "http" + ], + "support": { + "issues": "https://github.com/php-http/httplug/issues", + "source": "https://github.com/php-http/httplug/tree/2.4.0" + }, + "time": "2023-04-14T15:10:03+00:00" + }, + { + "name": "php-http/promise", + "version": "1.3.0", + "source": { + "type": "git", + "url": "https://github.com/php-http/promise.git", + "reference": "2916a606d3b390f4e9e8e2b8dd68581508be0f07" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-http/promise/zipball/2916a606d3b390f4e9e8e2b8dd68581508be0f07", + "reference": "2916a606d3b390f4e9e8e2b8dd68581508be0f07", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "friends-of-phpspec/phpspec-code-coverage": "^4.3.2 || ^6.3", + "phpspec/phpspec": "^5.1.2 || ^6.2 || ^7.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "Http\\Promise\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Joel Wurtz", + "email": "joel.wurtz@gmail.com" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com" + } + ], + "description": "Promise used for asynchronous HTTP requests", + "homepage": "http://httplug.io", + "keywords": [ + "promise" + ], + "support": { + "issues": "https://github.com/php-http/promise/issues", + "source": "https://github.com/php-http/promise/tree/1.3.0" + }, + "time": "2024-01-04T18:49:48+00:00" + }, { "name": "psr/cache", "version": "3.0.0", @@ -1920,6 +2536,166 @@ }, "time": "2019-01-08T18:20:26+00:00" }, + { + "name": "psr/http-client", + "version": "1.0.3", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-client.git", + "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-client/zipball/bb5906edc1c324c9a05aa0873d40117941e5fa90", + "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^8.0", + "psr/http-message": "^1.0 || ^2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Client\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP clients", + "homepage": "https://github.com/php-fig/http-client", + "keywords": [ + "http", + "http-client", + "psr", + "psr-18" + ], + "support": { + "source": "https://github.com/php-fig/http-client" + }, + "time": "2023-09-23T14:17:50+00:00" + }, + { + "name": "psr/http-factory", + "version": "1.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-factory.git", + "reference": "e616d01114759c4c489f93b099585439f795fe35" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-factory/zipball/e616d01114759c4c489f93b099585439f795fe35", + "reference": "e616d01114759c4c489f93b099585439f795fe35", + "shasum": "" + }, + "require": { + "php": ">=7.0.0", + "psr/http-message": "^1.0 || ^2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interfaces for PSR-7 HTTP message factories", + "keywords": [ + "factory", + "http", + "message", + "psr", + "psr-17", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-factory/tree/1.0.2" + }, + "time": "2023-04-10T20:10:41+00:00" + }, + { + "name": "psr/http-message", + "version": "2.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-message.git", + "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-message/zipball/402d35bcb92c70c026d1a6a9883f06b2ead23d71", + "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP messages", + "homepage": "https://github.com/php-fig/http-message", + "keywords": [ + "http", + "http-message", + "psr", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-message/tree/2.0" + }, + "time": "2023-04-04T09:54:51+00:00" + }, { "name": "psr/link", "version": "2.0.1", @@ -2026,6 +2802,50 @@ }, "time": "2021-07-14T16:46:02+00:00" }, + { + "name": "ralouphie/getallheaders", + "version": "3.0.3", + "source": { + "type": "git", + "url": "https://github.com/ralouphie/getallheaders.git", + "reference": "120b605dfeb996808c31b6477290a714d356e822" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822", + "reference": "120b605dfeb996808c31b6477290a714d356e822", + "shasum": "" + }, + "require": { + "php": ">=5.6" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.1", + "phpunit/phpunit": "^5 || ^6.5" + }, + "type": "library", + "autoload": { + "files": [ + "src/getallheaders.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ralph Khattar", + "email": "ralph.khattar@gmail.com" + } + ], + "description": "A polyfill for getallheaders.", + "support": { + "issues": "https://github.com/ralouphie/getallheaders/issues", + "source": "https://github.com/ralouphie/getallheaders/tree/develop" + }, + "time": "2019-03-08T08:55:37+00:00" + }, { "name": "ramsey/collection", "version": "2.0.0", diff --git a/apps/api/config/packages/http_discovery.yaml b/apps/api/config/packages/http_discovery.yaml new file mode 100644 index 0000000..b4d7622 --- /dev/null +++ b/apps/api/config/packages/http_discovery.yaml @@ -0,0 +1,11 @@ +--- +services: + Psr\Http\Message\RequestFactoryInterface: '@http_discovery.psr17_factory' + Psr\Http\Message\ResponseFactoryInterface: '@http_discovery.psr17_factory' + Psr\Http\Message\ServerRequestFactoryInterface: '@http_discovery.psr17_factory' + Psr\Http\Message\StreamFactoryInterface: '@http_discovery.psr17_factory' + Psr\Http\Message\UploadedFileFactoryInterface: '@http_discovery.psr17_factory' + Psr\Http\Message\UriFactoryInterface: '@http_discovery.psr17_factory' + + http_discovery.psr17_factory: + class: Http\Discovery\Psr17Factory diff --git a/apps/api/config/parameters.yaml b/apps/api/config/parameters.yaml index 4aede60..a40abf4 100644 --- a/apps/api/config/parameters.yaml +++ b/apps/api/config/parameters.yaml @@ -21,4 +21,7 @@ parameters: mercure_public_url: '%env(MERCURE_PUBLIC_URL)%' mercure_publisher_jwt_key: '%env(MERCURE_PUBLISHER_JWT_KEY)%' + search_hosts: '%env(csv:SEARCH_HOSTS)%' + search_credentials: '%env(SEARCH_CREDENTIALS)%' + default_channel_name: 'General' diff --git a/apps/api/config/search/mapping/message.yml b/apps/api/config/search/mapping/message.yml new file mode 100644 index 0000000..024ddc5 --- /dev/null +++ b/apps/api/config/search/mapping/message.yml @@ -0,0 +1,17 @@ +--- +message: + mappings: + properties: + content: + type: text + fields: + raw: + type: keyword + author_id: + type: keyword + channel_id: + type: keyword + created_at: + type: date + updated_at: + type: date diff --git a/apps/api/config/services/aliases.yaml b/apps/api/config/services/aliases.yaml index dc14c3b..41fb1ee 100644 --- a/apps/api/config/services/aliases.yaml +++ b/apps/api/config/services/aliases.yaml @@ -36,3 +36,8 @@ services: Domain\Security\AccessControl: '@Application\Security\AccessControl' Domain\Security\PasswordHasher: '@Application\Security\PasswordHasher' Domain\Security\UserProvider: '@Application\Security\UserProvider' + + ## Search + + Domain\Search\Indexer: '@Application\Search\Indexer\ElasticsearchIndexer' + Application\Search\Configuration\Provider: '@Application\Search\Configuration\YAMLProvider' diff --git a/apps/api/config/services/application.yaml b/apps/api/config/services/application.yaml index 5179a51..dbb2a45 100644 --- a/apps/api/config/services/application.yaml +++ b/apps/api/config/services/application.yaml @@ -65,3 +65,10 @@ services: Application\Event\Listener\: resource: '%kernel.project_dir%/src/Application/Event/Listener' tags: [ kernel.event_listener ] + + ## Search + + Application\Search\Indexer\ElasticsearchIndexer: ~ + Application\Search\Configuration\YAMLProvider: + arguments: + $filePath: '%kernel.project_dir%/config/search/mapping/message.yml' diff --git a/apps/api/config/services/infra.yaml b/apps/api/config/services/infra.yaml index beda510..1b0ce93 100644 --- a/apps/api/config/services/infra.yaml +++ b/apps/api/config/services/infra.yaml @@ -61,3 +61,10 @@ services: ## Faker Infra\Faker\Factory: ~ + + ## Elasticsearch + + Infra\Elasticsearch\ClientFactory: + arguments: + $hosts: '%search_hosts%' + $credentials: '%search_credentials%' diff --git a/apps/api/config/services/vendor.yaml b/apps/api/config/services/vendor.yaml index dc07a77..7e5250a 100644 --- a/apps/api/config/services/vendor.yaml +++ b/apps/api/config/services/vendor.yaml @@ -11,3 +11,8 @@ services: public: true calls: - seed: [ 123456 ] # DO NOT CHANGE IT (make sure that faker data be always the same) + + ## Elasticsearch + + Elastic\Elasticsearch\Client: + factory: ['@Infra\Elasticsearch\ClientFactory', 'create'] diff --git a/apps/api/config/services_test.yaml b/apps/api/config/services_test.yaml index ff879c1..c98d3ac 100644 --- a/apps/api/config/services_test.yaml +++ b/apps/api/config/services_test.yaml @@ -8,11 +8,13 @@ services: ## Test Services Infra\Doctrine\Truncater: ~ + Application\Search\Indexer\MockedIndexer: ~ ## Test overrides Application\Synchronization\Hub: '@Application\Synchronization\Hub\MockedHub' Domain\EventLog: '@Application\TraceableEventLog' + Domain\Search\Indexer: '@Application\Search\Indexer\MockedIndexer' ## Acceptance Test diff --git a/apps/api/phpstan.neon b/apps/api/phpstan.neon index 7901ead..14b1de9 100644 --- a/apps/api/phpstan.neon +++ b/apps/api/phpstan.neon @@ -1,5 +1,5 @@ parameters: - level: 8 + level: max paths: - src/ diff --git a/apps/api/src/Application/CLI/InsertFixturesCommand.php b/apps/api/src/Application/CLI/InsertFixturesCommand.php index 6795c8e..8a06bde 100644 --- a/apps/api/src/Application/CLI/InsertFixturesCommand.php +++ b/apps/api/src/Application/CLI/InsertFixturesCommand.php @@ -146,6 +146,7 @@ private function insertAdmins(): void /** @var User */ $admin = $this->domainGenerator->admin($this->domainGenerator->email()); + /** @var User */ $newAdmin = $this->bus->execute(new UserCommand\Create( firstname: $admin->getFirstname(), lastname: $admin->getLastname(), @@ -165,6 +166,7 @@ private function insertUsers(): void /** @var User */ $user = $this->domainGenerator->user($this->domainGenerator->email()); + /** @var User */ $newUser = $this->bus->execute(new UserCommand\Create( firstname: $user->getFirstname(), lastname: $user->getLastname(), @@ -184,6 +186,7 @@ private function insertMainData(): void /** @var Station */ $station = $this->domainGenerator->station(); + /** @var Station */ $newStation = $this->bus->execute(new StationCommand\Create( name: $station->getName(), description: $station->getDescription() @@ -198,6 +201,7 @@ private function insertMainData(): void /** @var Channel */ $channel = $this->domainGenerator->channel([ $station ]); + /** @var Channel */ $newChannel = $this->bus->execute(new ChannelCommand\Create( stationId: (string) $station->getIdentifier(), name: $channel->getName(), diff --git a/apps/api/src/Application/Event/Listener/ChannelEventListener.php b/apps/api/src/Application/Event/Listener/ChannelEventListener.php index ecb3f56..806ea36 100644 --- a/apps/api/src/Application/Event/Listener/ChannelEventListener.php +++ b/apps/api/src/Application/Event/Listener/ChannelEventListener.php @@ -23,7 +23,7 @@ public static function getSubscribedEvents(): array ]; } - public function __construct(private Hub $hub) + public function __construct(private readonly Hub $hub) { } diff --git a/apps/api/src/Application/Event/Listener/MessageEventListener.php b/apps/api/src/Application/Event/Listener/MessageEventListener.php index 9be156e..cf78b0c 100644 --- a/apps/api/src/Application/Event/Listener/MessageEventListener.php +++ b/apps/api/src/Application/Event/Listener/MessageEventListener.php @@ -7,6 +7,7 @@ use Application\Synchronization\Hub; use Application\Synchronization\Push; use Domain\Event\Message as Event; +use Domain\Search\Indexer; use Symfony\Component\EventDispatcher\EventSubscriberInterface; final class MessageEventListener implements EventSubscriberInterface @@ -22,17 +23,19 @@ public static function getSubscribedEvents(): array ]; } - public function __construct(private Hub $hub) + public function __construct(private readonly Hub $hub, private readonly Indexer $indexer) { } public function onMessageCreated(Event\Created $event): void { $this->hub->push(Push\Message::insert($event->message)); + $this->indexer->upsert($event->message); } public function onMessageDeleted(Event\Deleted $event): void { $this->hub->push(Push\Message::delete($event->message)); + $this->indexer->remove($event->message); } } diff --git a/apps/api/src/Application/Event/Listener/StationEventListener.php b/apps/api/src/Application/Event/Listener/StationEventListener.php index eb19a50..c91da22 100644 --- a/apps/api/src/Application/Event/Listener/StationEventListener.php +++ b/apps/api/src/Application/Event/Listener/StationEventListener.php @@ -13,7 +13,7 @@ final class StationEventListener implements EventSubscriberInterface { - public function __construct(private Hub $hub, private Bus $bus) + public function __construct(private readonly Hub $hub, private readonly Bus $bus) { } diff --git a/apps/api/src/Application/Event/Listener/SynchronizationListener.php b/apps/api/src/Application/Event/Listener/SynchronizationListener.php index 1b8e81c..e898592 100644 --- a/apps/api/src/Application/Event/Listener/SynchronizationListener.php +++ b/apps/api/src/Application/Event/Listener/SynchronizationListener.php @@ -10,7 +10,7 @@ final class SynchronizationListener implements EventSubscriberInterface { - public function __construct(private Hub $hub) + public function __construct(private readonly Hub $hub) { } diff --git a/apps/api/src/Application/HTTP/Payload.php b/apps/api/src/Application/HTTP/Payload.php index 26b9edb..f3bdef8 100644 --- a/apps/api/src/Application/HTTP/Payload.php +++ b/apps/api/src/Application/HTTP/Payload.php @@ -10,7 +10,7 @@ final class Payload { - public function __construct(private Request $request) + public function __construct(private readonly Request $request) { } @@ -27,6 +27,8 @@ public function mandatory(string $name): mixed } /** + * @param string|int|float|bool|null $defaultValue + * * @return string|int|float|bool|null */ public function optional(string $name, mixed $defaultValue = null): mixed @@ -54,10 +56,13 @@ public function mandatories(string $name): array */ public function optionals(string $name): array { - return array_merge( + /** @var array */ + $results = array_merge( $this->request->request->all($name), $this->request->query->all($name), ); + + return $results; } public function file(string $name): UploadedFile diff --git a/apps/api/src/Application/Search/Configuration/Provider.php b/apps/api/src/Application/Search/Configuration/Provider.php new file mode 100644 index 0000000..0000d0a --- /dev/null +++ b/apps/api/src/Application/Search/Configuration/Provider.php @@ -0,0 +1,13 @@ + + */ + public function getConfig(): array; +} diff --git a/apps/api/src/Application/Search/Configuration/YAMLProvider.php b/apps/api/src/Application/Search/Configuration/YAMLProvider.php new file mode 100644 index 0000000..4df505e --- /dev/null +++ b/apps/api/src/Application/Search/Configuration/YAMLProvider.php @@ -0,0 +1,25 @@ + */ + $config = Yaml::parseFile($this->filePath); + + return $config; + } +} diff --git a/apps/api/src/Application/Search/Indexer/ElasticsearchIndexer.php b/apps/api/src/Application/Search/Indexer/ElasticsearchIndexer.php new file mode 100644 index 0000000..ddb5190 --- /dev/null +++ b/apps/api/src/Application/Search/Indexer/ElasticsearchIndexer.php @@ -0,0 +1,37 @@ +index; + } + + /** + * {@inheritDoc} + */ + public function upsert(Message $message): void + { + $this->index[] = (string) $message->getIdentifier(); + } + + /** + * {@inheritDoc} + */ + public function remove(Message $message): void + { + $messageId = (string) $message->getIdentifier(); + + $this->index = array_filter( + $this->index, + static fn (string $id): bool => $id !== $messageId + ); + } + + /** + * {@inheritDoc} + */ + public function prune(): void + { + $this->index = []; + } +} diff --git a/apps/api/src/Application/Synchronization/Hub/MercureHub.php b/apps/api/src/Application/Synchronization/Hub/MercureHub.php index 1bb9225..5f6204f 100644 --- a/apps/api/src/Application/Synchronization/Hub/MercureHub.php +++ b/apps/api/src/Application/Synchronization/Hub/MercureHub.php @@ -27,9 +27,9 @@ final class MercureHub implements Hub * @param Normalizer> $normalizer */ public function __construct( - private HubInterface $hub, - private Normalizer $normalizer, - private Serializer $serializer + private readonly HubInterface $hub, + private readonly Normalizer $normalizer, + private readonly Serializer $serializer ) { $this->pushs = []; } diff --git a/apps/api/src/Domain/Search/Indexer.php b/apps/api/src/Domain/Search/Indexer.php new file mode 100644 index 0000000..b2bd97a --- /dev/null +++ b/apps/api/src/Domain/Search/Indexer.php @@ -0,0 +1,16 @@ + $hosts + */ + public function __construct( + private readonly array $hosts, + private readonly string $credentials + ) { + if (strlen($credentials) > 0 && -1 !== strpos($credentials, ':')) { + [$this->username, $this->password] = explode(':', $credentials); + } + } + + public function create(): Client + { + if (null === $this->username || null === $this->password) { + throw new Exception('Invalid credentials for elasticsearch client'); + } + + $clientBuilder = ClientBuilder::create(); + $clientBuilder + ->setHosts($this->hosts) + ->setBasicAuthentication($this->username, $this->password) + ; + + return $clientBuilder->build(); + } +} diff --git a/apps/api/symfony.lock b/apps/api/symfony.lock index 82850a4..673453e 100644 --- a/apps/api/symfony.lock +++ b/apps/api/symfony.lock @@ -50,6 +50,18 @@ "tests/Behat/DemoContext.php" ] }, + "php-http/discovery": { + "version": "1.19", + "recipe": { + "repo": "github.com/symfony/recipes", + "branch": "main", + "version": "1.18", + "ref": "f45b5dd173a27873ab19f5e3180b2f661c21de02" + }, + "files": [ + "config/packages/http_discovery.yaml" + ] + }, "symfony/console": { "version": "6.1", "recipe": { diff --git a/apps/api/tests/integrations/Domain/CommandHandler/Message/CreateHandler.spec.php b/apps/api/tests/integrations/Domain/CommandHandler/Message/CreateHandler.spec.php index e021549..822639d 100644 --- a/apps/api/tests/integrations/Domain/CommandHandler/Message/CreateHandler.spec.php +++ b/apps/api/tests/integrations/Domain/CommandHandler/Message/CreateHandler.spec.php @@ -14,6 +14,7 @@ use Domain\Model\Station; use Domain\Model\User; use Domain\Repository\Messages; +use Domain\Search\Indexer; describe(CreateHandler::class, function () { beforeEach(function () { @@ -26,6 +27,7 @@ given('messages', fn () => $this->container->get(Messages::class)); given('hub', fn () => $this->container->get(Hub::class)); given('events', fn () => $this->container->get(EventLog::class)); + given('indexer', fn () => $this->container->get(Indexer::class)); it ('create new message in database', function () { $station = new Station('Station', 'desc'); @@ -74,6 +76,9 @@ $actual->message->getIdentifier() === $newMessage->getIdentifier() ; }); + + expect(count($this->indexer->getCurrentIndex()))->toBe(1); + expect($this->indexer->getCurrentIndex()[0])->toEqual((string) $newMessage->getIdentifier()); }); it ('throws if author not found from database', function () { diff --git a/apps/search/Dockerfile b/apps/search/Dockerfile new file mode 100644 index 0000000..e36a2b6 --- /dev/null +++ b/apps/search/Dockerfile @@ -0,0 +1,3 @@ +FROM elasticsearch:8.12.1 + +EXPOSE 9200 diff --git a/docker-compose.dev.yaml b/docker-compose.dev.yaml index 123e3aa..78e2bb4 100644 --- a/docker-compose.dev.yaml +++ b/docker-compose.dev.yaml @@ -70,6 +70,22 @@ services: ports: - '8080:80' + search: + build: + context: apps/search + env_file: + - .env + ports: + - 9200:9200 + environment: + - xpack.security.enabled=true + - discovery.type=single-node + - ELASTIC_USERNAME=elastic + - ELASTIC_PASSWORD=P@ssw0rd + volumes: + - search:/usr/share/elasticsearch/data + volumes: database: ~ + search: ~