├── .env.example ├── Dockerfile ├── LICENSE ├── README.md ├── composer.json ├── docker-compose-neo4j-4.yml ├── docker-compose.yml ├── out └── .gitignore ├── phpunit.coverage.xml.dist ├── psalm.xml ├── rector.php ├── src ├── Authentication │ ├── Authenticate.php │ ├── BasicAuth.php │ ├── KerberosAuth.php │ ├── NoAuth.php │ └── OpenIDConnectAuth.php ├── Basic │ ├── Client.php │ ├── Driver.php │ ├── Session.php │ └── UnmanagedTransaction.php ├── Bolt │ ├── BoltConnection.php │ ├── BoltDriver.php │ ├── BoltMessageFactory.php │ ├── BoltResult.php │ ├── BoltUnmanagedTransaction.php │ ├── Connection.php │ ├── ConnectionPool.php │ ├── Messages │ │ ├── BoltBeginMessage.php │ │ ├── BoltCommitMessage.php │ │ ├── BoltDiscardMessage.php │ │ ├── BoltGoodbyeMessage.php │ │ ├── BoltHelloMessage.php │ │ ├── BoltLogoffMessage.php │ │ ├── BoltLogonMessage.php │ │ ├── BoltPullMessage.php │ │ ├── BoltResetMessage.php │ │ ├── BoltRollbackMessage.php │ │ └── BoltRunMessage.php │ ├── ProtocolFactory.php │ ├── Session.php │ ├── SocketConnectionFactory.php │ ├── SslConfigurationFactory.php │ ├── StreamConnectionFactory.php │ ├── SystemWideConnectionFactory.php │ └── UriConfiguration.php ├── BoltFactory.php ├── Client.php ├── ClientBuilder.php ├── Common │ ├── Cache.php │ ├── ConnectionConfiguration.php │ ├── DNSAddressResolver.php │ ├── DriverSetupManager.php │ ├── GeneratorHelper.php │ ├── Neo4jLogger.php │ ├── Resolvable.php │ ├── ResponseHelper.php │ ├── SemaphoreFactory.php │ ├── SingleThreadedSemaphore.php │ ├── SysVSemaphore.php │ ├── TransactionHelper.php │ └── Uri.php ├── Contracts │ ├── AddressResolverInterface.php │ ├── AuthenticateInterface.php │ ├── BasicConnectionFactoryInterface.php │ ├── BoltConvertibleInterface.php │ ├── BoltMessage.php │ ├── ClientInterface.php │ ├── ConnectionInterface.php │ ├── ConnectionPoolInterface.php │ ├── CypherSequence.php │ ├── DriverInterface.php │ ├── HasPropertiesInterface.php │ ├── PointInterface.php │ ├── SemaphoreFactoryInterface.php │ ├── SemaphoreInterface.php │ ├── SessionInterface.php │ ├── TransactionInterface.php │ └── UnmanagedTransactionInterface.php ├── Databags │ ├── Bookmark.php │ ├── BookmarkHolder.php │ ├── ConnectionRequestData.php │ ├── DatabaseInfo.php │ ├── DriverConfiguration.php │ ├── DriverSetup.php │ ├── Neo4jError.php │ ├── Notification.php │ ├── Pair.php │ ├── Plan.php │ ├── PlanArguments.php │ ├── Position.php │ ├── ProfiledQueryPlan.php │ ├── ResultSummary.php │ ├── ServerInfo.php │ ├── SessionConfiguration.php │ ├── SslConfiguration.php │ ├── Statement.php │ ├── SummarizedResult.php │ ├── SummaryCounters.php │ └── TransactionConfiguration.php ├── DriverFactory.php ├── Enum │ ├── AccessMode.php │ ├── ConnectionProtocol.php │ ├── QueryTypeEnum.php │ ├── RoutingRoles.php │ ├── SslMode.php │ └── TransactionState.php ├── Exception │ ├── ClientException.php │ ├── ConnectionPoolException.php │ ├── InvalidCacheArgumentException.php │ ├── Neo4jException.php │ ├── PropertyDoesNotExistException.php │ ├── RuntimeTypeException.php │ └── UnsupportedScheme.php ├── Formatter │ ├── Specialised │ │ └── BoltOGMTranslator.php │ └── SummarizedResultFormatter.php ├── Neo4j │ ├── Neo4jConnectionPool.php │ ├── Neo4jDriver.php │ └── RoutingTable.php ├── ParameterHelper.php ├── TypeCaster.php └── Types │ ├── Abstract3DPoint.php │ ├── AbstractCypherObject.php │ ├── AbstractPoint.php │ ├── AbstractPropertyObject.php │ ├── Cartesian3DPoint.php │ ├── CartesianPoint.php │ ├── CypherList.php │ ├── CypherMap.php │ ├── CypherSequenceTrait.php │ ├── Date.php │ ├── DateTime.php │ ├── DateTimeZoneId.php │ ├── Duration.php │ ├── LocalDateTime.php │ ├── LocalTime.php │ ├── Node.php │ ├── Path.php │ ├── Relationship.php │ ├── Time.php │ ├── UnboundRelationship.php │ ├── WGS843DPoint.php │ └── WGS84Point.php └── testkit-backend ├── blacklist.php ├── features.php ├── index.php ├── register.php ├── src ├── Backend.php ├── Contracts │ ├── RequestHandlerInterface.php │ └── TestkitResponseInterface.php ├── Handlers │ ├── AbstractRunner.php │ ├── CheckMultiDBSupport.php │ ├── DomainNameResolutionCompleted.php │ ├── DriverClose.php │ ├── ForcedRoutingTableUpdate.php │ ├── GetFeatures.php │ ├── GetRoutingTable.php │ ├── NewDriver.php │ ├── NewSession.php │ ├── ResolverResolutionCompleted.php │ ├── ResultConsume.php │ ├── ResultNext.php │ ├── ResultSingle.php │ ├── RetryableNegative.php │ ├── RetryablePositive.php │ ├── SessionBeginTransaction.php │ ├── SessionClose.php │ ├── SessionLastBookmarks.php │ ├── SessionReadTransaction.php │ ├── SessionRun.php │ ├── SessionWriteTransaction.php │ ├── StartTest.php │ ├── TransactionCommit.php │ ├── TransactionRollback.php │ ├── TransactionRun.php │ └── VerifyConnectivity.php ├── MainRepository.php ├── Request.php ├── RequestFactory.php ├── Requests │ ├── AuthorizationTokenRequest.php │ ├── CheckMultiDBSupportRequest.php │ ├── DomainNameResolutionCompletedRequest.php │ ├── DriverCloseRequest.php │ ├── ForcedRoutingTableUpdateRequest.php │ ├── GetFeaturesRequest.php │ ├── GetRoutingTableRequest.php │ ├── NewDriverRequest.php │ ├── NewSessionRequest.php │ ├── ResolverResolutionCompletedRequest.php │ ├── ResultConsumeRequest.php │ ├── ResultNextRequest.php │ ├── ResultSingleRequest.php │ ├── RetryableNegativeRequest.php │ ├── RetryablePositiveRequest.php │ ├── SessionBeginTransactionRequest.php │ ├── SessionCloseRequest.php │ ├── SessionLastBookmarksRequest.php │ ├── SessionReadTransactionRequest.php │ ├── SessionRunRequest.php │ ├── SessionWriteTransactionRequest.php │ ├── StartTestRequest.php │ ├── TransactionCommitRequest.php │ ├── TransactionRollbackRequest.php │ ├── TransactionRunRequest.php │ └── VerifyConnectivityRequest.php ├── Responses │ ├── BackendErrorResponse.php │ ├── BookmarksResponse.php │ ├── DomainNameResolutionRequiredResponse.php │ ├── DriverErrorResponse.php │ ├── DriverResponse.php │ ├── FeatureListResponse.php │ ├── FrontendErrorResponse.php │ ├── MultiDBSupportResponse.php │ ├── NullRecordResponse.php │ ├── RecordResponse.php │ ├── ResolverResolutionRequiredResponse.php │ ├── ResultResponse.php │ ├── RetryableDoneResponse.php │ ├── RetryableTryResponse.php │ ├── RoutingTableResponse.php │ ├── RunTestResponse.php │ ├── SessionResponse.php │ ├── SkipTestResponse.php │ ├── SummaryResponse.php │ ├── TransactionResponse.php │ └── Types │ │ ├── CypherNode.php │ │ ├── CypherObject.php │ │ ├── CypherPath.php │ │ └── CypherRelationship.php └── Socket.php └── testkit.sh /.env.example: -------------------------------------------------------------------------------- 1 | PHP_VERSION=8.1 2 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | ARG PHP_VERSION 2 | 3 | FROM php:${PHP_VERSION}-cli 4 | RUN apt-get update \ 5 | && apt-get install -y \ 6 | libzip-dev \ 7 | unzip \ 8 | git \ 9 | wget \ 10 | && docker-php-ext-install -j$(nproc) bcmath sockets \ 11 | && wget https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 \ 12 | && mv test-reporter-latest-linux-amd64 /usr/bin/cc-test-reporter \ 13 | && chmod +x /usr/bin/cc-test-reporter \ 14 | && pecl install xdebug \ 15 | && docker-php-ext-enable xdebug && \ 16 | curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer 17 | 18 | WORKDIR /opt/project 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2020 laudis 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /out/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /phpunit.coverage.xml.dist: -------------------------------------------------------------------------------- 1 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | ./tests 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | src 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /psalm.xml: -------------------------------------------------------------------------------- 1 | 2 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /rector.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | use Rector\Config\RectorConfig; 15 | use Rector\TypeDeclaration\Rector\ClassMethod\AddVoidReturnTypeWhereNoReturnRector; 16 | 17 | return RectorConfig::configure() 18 | ->withPaths([ 19 | __DIR__.'/src', 20 | __DIR__.'/tests', 21 | ]) 22 | ->withPhpSets(php81: true) 23 | ->withRules([ 24 | AddVoidReturnTypeWhereNoReturnRector::class, 25 | ]); 26 | -------------------------------------------------------------------------------- /src/Basic/Driver.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\Basic; 15 | 16 | use Laudis\Neo4j\Contracts\AuthenticateInterface; 17 | use Laudis\Neo4j\Contracts\DriverInterface; 18 | use Laudis\Neo4j\Databags\DriverConfiguration; 19 | use Laudis\Neo4j\Databags\SessionConfiguration; 20 | use Laudis\Neo4j\DriverFactory; 21 | use Laudis\Neo4j\Formatter\SummarizedResultFormatter; 22 | use Psr\Http\Message\UriInterface; 23 | 24 | final class Driver implements DriverInterface 25 | { 26 | /** 27 | * @psalm-external-mutation-free 28 | */ 29 | public function __construct( 30 | private readonly DriverInterface $driver, 31 | ) { 32 | } 33 | 34 | /** 35 | * @psalm-mutation-free 36 | */ 37 | public function createSession(?SessionConfiguration $config = null): Session 38 | { 39 | return new Session($this->driver->createSession($config)); 40 | } 41 | 42 | public function verifyConnectivity(?SessionConfiguration $config = null): bool 43 | { 44 | return $this->driver->verifyConnectivity($config); 45 | } 46 | 47 | public static function create(string|UriInterface $uri, ?DriverConfiguration $configuration = null, ?AuthenticateInterface $authenticate = null): self 48 | { 49 | $driver = DriverFactory::create($uri, $configuration, $authenticate, SummarizedResultFormatter::create()); 50 | 51 | return new self($driver); 52 | } 53 | 54 | public function closeConnections(): void 55 | { 56 | $this->driver->closeConnections(); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/Basic/UnmanagedTransaction.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\Basic; 15 | 16 | use Laudis\Neo4j\Contracts\UnmanagedTransactionInterface; 17 | use Laudis\Neo4j\Databags\Statement; 18 | use Laudis\Neo4j\Databags\SummarizedResult; 19 | use Laudis\Neo4j\Types\CypherList; 20 | 21 | final class UnmanagedTransaction implements UnmanagedTransactionInterface 22 | { 23 | public function __construct( 24 | private readonly UnmanagedTransactionInterface $tsx, 25 | ) { 26 | } 27 | 28 | /** 29 | * @param iterable $parameters 30 | */ 31 | public function run(string $statement, iterable $parameters = []): SummarizedResult 32 | { 33 | return $this->tsx->run($statement, $parameters); 34 | } 35 | 36 | public function runStatement(Statement $statement): SummarizedResult 37 | { 38 | return $this->tsx->runStatement($statement); 39 | } 40 | 41 | /** 42 | * @param iterable $statements 43 | * 44 | * @return CypherList 45 | */ 46 | public function runStatements(iterable $statements): CypherList 47 | { 48 | return $this->tsx->runStatements($statements); 49 | } 50 | 51 | /** 52 | * @param iterable $statements 53 | * 54 | * @return CypherList 55 | */ 56 | public function commit(iterable $statements = []): CypherList 57 | { 58 | return $this->tsx->commit($statements); 59 | } 60 | 61 | public function rollback(): void 62 | { 63 | $this->tsx->rollback(); 64 | } 65 | 66 | public function isCommitted(): bool 67 | { 68 | return $this->tsx->isCommitted(); 69 | } 70 | 71 | public function isRolledBack(): bool 72 | { 73 | return $this->tsx->isRolledBack(); 74 | } 75 | 76 | public function isFinished(): bool 77 | { 78 | return $this->tsx->isFinished(); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/Bolt/Connection.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\Bolt; 15 | 16 | use Bolt\connection\IConnection; 17 | 18 | class Connection 19 | { 20 | /** 21 | * @param ''|'s'|'ssc' $ssl 22 | */ 23 | public function __construct( 24 | private readonly IConnection $connection, 25 | private readonly string $ssl, 26 | ) { 27 | } 28 | 29 | public function getIConnection(): IConnection 30 | { 31 | return $this->connection; 32 | } 33 | 34 | public function write(string $buffer): void 35 | { 36 | $this->connection->write($buffer); 37 | } 38 | 39 | public function read(int $length = 2048): string 40 | { 41 | return $this->connection->read($length); 42 | } 43 | 44 | public function disconnect(): void 45 | { 46 | $this->connection->disconnect(); 47 | } 48 | 49 | public function getIp(): string 50 | { 51 | return $this->connection->getIp(); 52 | } 53 | 54 | public function getPort(): int 55 | { 56 | return $this->connection->getPort(); 57 | } 58 | 59 | public function getTimeout(): float 60 | { 61 | return $this->connection->getTimeout(); 62 | } 63 | 64 | public function setTimeout(float $timeout): void 65 | { 66 | $this->connection->setTimeout($timeout); 67 | } 68 | 69 | /** 70 | * @return ''|'s'|'ssc' 71 | */ 72 | public function getEncryptionLevel(): string 73 | { 74 | return $this->ssl; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/Bolt/Messages/BoltBeginMessage.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\Bolt\Messages; 15 | 16 | use Bolt\protocol\V4_4; 17 | use Bolt\protocol\V5; 18 | use Bolt\protocol\V5_1; 19 | use Bolt\protocol\V5_2; 20 | use Bolt\protocol\V5_3; 21 | use Bolt\protocol\V5_4; 22 | use Laudis\Neo4j\Common\Neo4jLogger; 23 | use Laudis\Neo4j\Contracts\BoltMessage; 24 | use Psr\Log\LogLevel; 25 | 26 | final class BoltBeginMessage extends BoltMessage 27 | { 28 | public function __construct( 29 | private readonly V4_4|V5|V5_1|V5_2|V5_3|V5_4 $protocol, 30 | private readonly array $extra, 31 | private readonly ?Neo4jLogger $logger, 32 | ) { 33 | parent::__construct($protocol); 34 | } 35 | 36 | public function send(): BoltBeginMessage 37 | { 38 | $this->logger?->log(LogLevel::DEBUG, 'BEGIN', $this->extra); 39 | $this->protocol->begin($this->extra); 40 | 41 | return $this; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/Bolt/Messages/BoltCommitMessage.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\Bolt\Messages; 15 | 16 | use Bolt\enum\ServerState; 17 | use Bolt\protocol\V4_4; 18 | use Bolt\protocol\V5; 19 | use Bolt\protocol\V5_1; 20 | use Bolt\protocol\V5_2; 21 | use Bolt\protocol\V5_3; 22 | use Bolt\protocol\V5_4; 23 | use Laudis\Neo4j\Common\Neo4jLogger; 24 | use Laudis\Neo4j\Contracts\BoltMessage; 25 | use Laudis\Neo4j\Databags\Bookmark; 26 | use Laudis\Neo4j\Databags\BookmarkHolder; 27 | use Psr\Log\LogLevel; 28 | 29 | final class BoltCommitMessage extends BoltMessage 30 | { 31 | public function __construct( 32 | private readonly V4_4|V5|V5_1|V5_2|V5_3|V5_4 $protocol, 33 | private readonly ?Neo4jLogger $logger, 34 | private readonly BookmarkHolder $bookmarks, 35 | ) { 36 | parent::__construct($protocol); 37 | } 38 | 39 | public function send(): BoltCommitMessage 40 | { 41 | $this->logger?->log(LogLevel::DEBUG, 'COMMIT'); 42 | $response = $this->protocol->commit()->getResponse(); 43 | // TODO: This is an issue with the underlying bolt library. 44 | // The serverState should be READY after a successful commit but 45 | // it's still in TX_STREAMING if the results were not consumed 46 | // 47 | // This should be removed once it's fixed 48 | $this->protocol->serverState = ServerState::READY; 49 | 50 | /** @var array{bookmark?: string} $content */ 51 | $content = $response->content; 52 | $bookmark = $content['bookmark'] ?? ''; 53 | 54 | if (trim($bookmark) !== '') { 55 | $this->bookmarks->setBookmark(new Bookmark([$bookmark])); 56 | } 57 | 58 | $this->protocol->serverState = ServerState::READY; 59 | 60 | return $this; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/Bolt/Messages/BoltDiscardMessage.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\Bolt\Messages; 15 | 16 | use Bolt\protocol\V4_4; 17 | use Bolt\protocol\V5; 18 | use Bolt\protocol\V5_1; 19 | use Bolt\protocol\V5_2; 20 | use Bolt\protocol\V5_3; 21 | use Bolt\protocol\V5_4; 22 | use Laudis\Neo4j\Common\Neo4jLogger; 23 | use Laudis\Neo4j\Contracts\BoltMessage; 24 | use Psr\Log\LogLevel; 25 | 26 | final class BoltDiscardMessage extends BoltMessage 27 | { 28 | public function __construct( 29 | private readonly V4_4|V5|V5_1|V5_2|V5_3|V5_4 $protocol, 30 | private readonly array $extra, 31 | private readonly ?Neo4jLogger $logger, 32 | ) { 33 | parent::__construct($protocol); 34 | } 35 | 36 | public function send(): BoltDiscardMessage 37 | { 38 | $this->logger?->log(LogLevel::DEBUG, 'DISCARD', $this->extra); 39 | $this->protocol->discard($this->extra); 40 | 41 | return $this; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/Bolt/Messages/BoltGoodbyeMessage.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\Bolt\Messages; 15 | 16 | use Bolt\protocol\V4_4; 17 | use Bolt\protocol\V5; 18 | use Bolt\protocol\V5_1; 19 | use Bolt\protocol\V5_2; 20 | use Bolt\protocol\V5_3; 21 | use Bolt\protocol\V5_4; 22 | use Laudis\Neo4j\Common\Neo4jLogger; 23 | use Laudis\Neo4j\Contracts\BoltMessage; 24 | use Psr\Log\LogLevel; 25 | 26 | final class BoltGoodbyeMessage extends BoltMessage 27 | { 28 | public function __construct( 29 | private readonly V4_4|V5|V5_1|V5_2|V5_3|V5_4 $protocol, 30 | private readonly ?Neo4jLogger $logger, 31 | ) { 32 | parent::__construct($protocol); 33 | } 34 | 35 | public function send(): BoltGoodbyeMessage 36 | { 37 | $this->logger?->log(LogLevel::DEBUG, 'GOODBYE'); 38 | $this->protocol->goodbye(); 39 | 40 | return $this; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/Bolt/Messages/BoltHelloMessage.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\Bolt\Messages; 15 | 16 | use Bolt\error\BoltException; 17 | use Bolt\protocol\V4_4; 18 | use Bolt\protocol\V5; 19 | use Bolt\protocol\V5_1; 20 | use Bolt\protocol\V5_2; 21 | use Bolt\protocol\V5_3; 22 | use Bolt\protocol\V5_4; 23 | use Laudis\Neo4j\Common\Neo4jLogger; 24 | use Laudis\Neo4j\Contracts\BoltMessage; 25 | use Psr\Log\LogLevel; 26 | 27 | final class BoltHelloMessage extends BoltMessage 28 | { 29 | /** 30 | * Constructor for the BoltHelloMessage. 31 | * 32 | * @param V4_4|V5|V5_1|V5_2|V5_3|V5_4 $protocol The protocol connection 33 | * @param array $metadata The metadata for the HELLO message (like user agent, supported versions) 34 | * @param Neo4jLogger|null $logger Optional logger for debugging purposes 35 | */ 36 | public function __construct( 37 | private readonly V4_4|V5|V5_1|V5_2|V5_3|V5_4 $protocol, 38 | private readonly array $metadata, 39 | private readonly ?Neo4jLogger $logger = null, 40 | ) { 41 | parent::__construct($protocol); 42 | } 43 | 44 | /** 45 | * Sends the HELLO message to the server. 46 | * 47 | * @throws BoltException 48 | */ 49 | public function send(): BoltHelloMessage 50 | { 51 | $this->logger?->log(LogLevel::DEBUG, 'HELLO', $this->metadata); 52 | 53 | $this->protocol->hello($this->metadata); 54 | 55 | return $this; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/Bolt/Messages/BoltLogoffMessage.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\Bolt\Messages; 15 | 16 | use Bolt\protocol\V4_4; 17 | use Bolt\protocol\V5; 18 | use Bolt\protocol\V5_1; 19 | use Bolt\protocol\V5_2; 20 | use Bolt\protocol\V5_3; 21 | use Bolt\protocol\V5_4; 22 | use Laudis\Neo4j\Common\Neo4jLogger; 23 | use Laudis\Neo4j\Contracts\BoltMessage; 24 | use Psr\Log\LogLevel; 25 | 26 | /** 27 | * A message that issues a LOGOFF request to the server to terminate the connection. 28 | */ 29 | class BoltLogoffMessage extends BoltMessage 30 | { 31 | /** 32 | * @param V4_4|V5|V5_1|V5_2|V5_3|V5_4 $protocol The Bolt protocol version 33 | * @param Neo4jLogger|null $logger Optional logger for logging purposes 34 | */ 35 | public function __construct( 36 | private readonly V4_4|V5|V5_1|V5_2|V5_3|V5_4 $protocol, 37 | private readonly ?Neo4jLogger $logger = null, 38 | ) { 39 | parent::__construct($protocol); 40 | } 41 | 42 | /** 43 | * Sends the LOGOFF request to the server to disconnect. 44 | * 45 | * @return BoltLogoffMessage The current instance of the message 46 | */ 47 | public function send(): BoltLogoffMessage 48 | { 49 | $this->logger?->log(LogLevel::DEBUG, 'LOGOFF', []); 50 | /** @psalm-suppress PossiblyUndefinedMethod */ 51 | $this->protocol->logoff(); 52 | 53 | return $this; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/Bolt/Messages/BoltLogonMessage.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\Bolt\Messages; 15 | 16 | use Bolt\protocol\V4_4; 17 | use Bolt\protocol\V5; 18 | use Bolt\protocol\V5_1; 19 | use Bolt\protocol\V5_2; 20 | use Bolt\protocol\V5_3; 21 | use Bolt\protocol\V5_4; 22 | use Laudis\Neo4j\Common\Neo4jLogger; 23 | use Laudis\Neo4j\Contracts\BoltMessage; 24 | use Psr\Log\LogLevel; 25 | 26 | /** 27 | * A message that issues a LOGON request to the server for authentication. 28 | */ 29 | final class BoltLogonMessage extends BoltMessage 30 | { 31 | /** 32 | * @param V4_4|V5|V5_1|V5_2|V5_3|V5_4 $protocol The Bolt protocol version 33 | * @param array $credentials The credentials for the LOGON request (e.g., username and password) 34 | * @param Neo4jLogger|null $logger Optional logger for logging purposes 35 | */ 36 | public function __construct( 37 | private readonly V4_4|V5|V5_1|V5_2|V5_3|V5_4 $protocol, 38 | private readonly array $credentials, 39 | private readonly ?Neo4jLogger $logger, 40 | ) { 41 | parent::__construct($protocol); 42 | } 43 | 44 | /** 45 | * Sends the LOGON request to the server with the provided credentials. 46 | * 47 | * @return BoltLogonMessage The current instance of the message 48 | */ 49 | public function send(): BoltLogonMessage 50 | { 51 | $toLog = $this->credentials; 52 | unset($toLog['credentials']); 53 | 54 | $this->logger?->log(LogLevel::DEBUG, 'LOGON', $toLog); 55 | /** @psalm-suppress PossiblyUndefinedMethod */ 56 | $this->protocol->logon($this->credentials); 57 | 58 | return $this; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/Bolt/Messages/BoltPullMessage.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\Bolt\Messages; 15 | 16 | use Bolt\protocol\V4_4; 17 | use Bolt\protocol\V5; 18 | use Bolt\protocol\V5_1; 19 | use Bolt\protocol\V5_2; 20 | use Bolt\protocol\V5_3; 21 | use Bolt\protocol\V5_4; 22 | use Laudis\Neo4j\Common\Neo4jLogger; 23 | use Laudis\Neo4j\Contracts\BoltMessage; 24 | use Psr\Log\LogLevel; 25 | 26 | final class BoltPullMessage extends BoltMessage 27 | { 28 | public function __construct( 29 | private readonly V4_4|V5|V5_1|V5_2|V5_3|V5_4 $protocol, 30 | private readonly array $extra, 31 | private readonly ?Neo4jLogger $logger, 32 | ) { 33 | parent::__construct($protocol); 34 | } 35 | 36 | public function send(): BoltPullMessage 37 | { 38 | $this->logger?->log(LogLevel::DEBUG, 'PULL', $this->extra); 39 | $this->protocol->pull($this->extra); 40 | 41 | return $this; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/Bolt/Messages/BoltResetMessage.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\Bolt\Messages; 15 | 16 | use Bolt\protocol\V4_4; 17 | use Bolt\protocol\V5; 18 | use Bolt\protocol\V5_1; 19 | use Bolt\protocol\V5_2; 20 | use Bolt\protocol\V5_3; 21 | use Bolt\protocol\V5_4; 22 | use Laudis\Neo4j\Common\Neo4jLogger; 23 | use Laudis\Neo4j\Contracts\BoltMessage; 24 | use Psr\Log\LogLevel; 25 | 26 | final class BoltResetMessage extends BoltMessage 27 | { 28 | public function __construct( 29 | private readonly V4_4|V5|V5_1|V5_2|V5_3|V5_4 $protocol, 30 | private readonly ?Neo4jLogger $logger, 31 | ) { 32 | parent::__construct($protocol); 33 | } 34 | 35 | public function send(): BoltResetMessage 36 | { 37 | $this->logger?->log(LogLevel::DEBUG, 'RESET'); 38 | $this->protocol->reset(); 39 | 40 | return $this; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/Bolt/Messages/BoltRollbackMessage.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\Bolt\Messages; 15 | 16 | use Bolt\protocol\V4_4; 17 | use Bolt\protocol\V5; 18 | use Bolt\protocol\V5_1; 19 | use Bolt\protocol\V5_2; 20 | use Bolt\protocol\V5_3; 21 | use Bolt\protocol\V5_4; 22 | use Laudis\Neo4j\Common\Neo4jLogger; 23 | use Laudis\Neo4j\Contracts\BoltMessage; 24 | use Psr\Log\LogLevel; 25 | 26 | final class BoltRollbackMessage extends BoltMessage 27 | { 28 | public function __construct( 29 | private readonly V4_4|V5|V5_1|V5_2|V5_3|V5_4 $protocol, 30 | private readonly ?Neo4jLogger $logger, 31 | ) { 32 | parent::__construct($protocol); 33 | } 34 | 35 | public function send(): BoltRollbackMessage 36 | { 37 | $this->logger?->log(LogLevel::DEBUG, 'ROLLBACK'); 38 | $this->protocol->rollback(); 39 | 40 | return $this; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/Bolt/Messages/BoltRunMessage.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\Bolt\Messages; 15 | 16 | use Bolt\protocol\V4_4; 17 | use Bolt\protocol\V5; 18 | use Bolt\protocol\V5_1; 19 | use Bolt\protocol\V5_2; 20 | use Bolt\protocol\V5_3; 21 | use Bolt\protocol\V5_4; 22 | use Laudis\Neo4j\Common\Neo4jLogger; 23 | use Laudis\Neo4j\Contracts\BoltMessage; 24 | use Psr\Log\LogLevel; 25 | 26 | final class BoltRunMessage extends BoltMessage 27 | { 28 | public function __construct( 29 | private readonly V4_4|V5|V5_1|V5_2|V5_3|V5_4 $protocol, 30 | private readonly string $text, 31 | private readonly array $parameters, 32 | private readonly array $extra, 33 | private readonly ?Neo4jLogger $logger, 34 | ) { 35 | parent::__construct($protocol); 36 | } 37 | 38 | public function send(): BoltRunMessage 39 | { 40 | $this->logger?->log(LogLevel::DEBUG, 'RUN', [ 41 | 'text' => $this->text, 42 | 'parameters' => $this->parameters, 43 | 'extra' => $this->extra, 44 | ]); 45 | $this->protocol->run($this->text, $this->parameters, $this->extra); 46 | 47 | return $this; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/Bolt/ProtocolFactory.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\Bolt; 15 | 16 | use Bolt\Bolt; 17 | use Bolt\connection\IConnection; 18 | use Bolt\protocol\V4_4; 19 | use Bolt\protocol\V5; 20 | use Bolt\protocol\V5_1; 21 | use Bolt\protocol\V5_2; 22 | use Bolt\protocol\V5_3; 23 | use Bolt\protocol\V5_4; 24 | use Laudis\Neo4j\Contracts\AuthenticateInterface; 25 | use RuntimeException; 26 | 27 | class ProtocolFactory 28 | { 29 | /** 30 | * @return array{0: V4_4|V5|V5_1|V5_2|V5_3|V5_4, 1: array{server: string, connection_id: string, hints: list}} 31 | */ 32 | public function createProtocol(IConnection $connection, AuthenticateInterface $auth, string $userAgent): array 33 | { 34 | $boltOptoutEnv = getenv('BOLT_ANALYTICS_OPTOUT'); 35 | if ($boltOptoutEnv === false) { 36 | putenv('BOLT_ANALYTICS_OPTOUT=1'); 37 | } 38 | 39 | $bolt = new Bolt($connection); 40 | $bolt->setProtocolVersions('5.4.4', 4.4); 41 | $protocol = $bolt->build(); 42 | 43 | if (!($protocol instanceof V4_4 || $protocol instanceof V5 || $protocol instanceof V5_1 || $protocol instanceof V5_2 || $protocol instanceof V5_3 || $protocol instanceof V5_4)) { 44 | throw new RuntimeException('Client only supports bolt version 4.4 to 5.4'); 45 | } 46 | 47 | $response = $auth->authenticateBolt($protocol, $userAgent); 48 | 49 | return [$protocol, $response]; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/Bolt/SocketConnectionFactory.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\Bolt; 15 | 16 | use Bolt\connection\Socket; 17 | use Laudis\Neo4j\Contracts\BasicConnectionFactoryInterface; 18 | use Laudis\Neo4j\Databags\TransactionConfiguration; 19 | 20 | final class SocketConnectionFactory implements BasicConnectionFactoryInterface 21 | { 22 | public function __construct( 23 | private readonly StreamConnectionFactory $factory, 24 | ) { 25 | } 26 | 27 | public function create(UriConfiguration $config): Connection 28 | { 29 | if ($config->getSslLevel() !== '') { 30 | return $this->factory->create($config); 31 | } 32 | 33 | $connection = new Socket($config->getHost(), $config->getPort() ?? 7687, $config->getTimeout() ?? TransactionConfiguration::DEFAULT_TIMEOUT); 34 | 35 | return new Connection($connection, ''); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/Bolt/StreamConnectionFactory.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\Bolt; 15 | 16 | use Bolt\connection\StreamSocket; 17 | use Laudis\Neo4j\Contracts\BasicConnectionFactoryInterface; 18 | use Laudis\Neo4j\Databags\TransactionConfiguration; 19 | 20 | final class StreamConnectionFactory implements BasicConnectionFactoryInterface 21 | { 22 | public function create(UriConfiguration $config): Connection 23 | { 24 | $connection = new StreamSocket($config->getHost(), $config->getPort() ?? 7687, $config->getTimeout() ?? TransactionConfiguration::DEFAULT_TIMEOUT); 25 | if ($config->getSslLevel() !== '') { 26 | $connection->setSslContextOptions($config->getSslConfiguration()); 27 | } 28 | 29 | return new Connection($connection, $config->getSslLevel()); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Bolt/SystemWideConnectionFactory.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\Bolt; 15 | 16 | use function extension_loaded; 17 | 18 | use Laudis\Neo4j\Contracts\BasicConnectionFactoryInterface; 19 | 20 | /** 21 | * Singleton connection factory based on the installed extensions. 22 | */ 23 | class SystemWideConnectionFactory implements BasicConnectionFactoryInterface 24 | { 25 | private static ?SystemWideConnectionFactory $instance = null; 26 | 27 | /** 28 | * @param SocketConnectionFactory|StreamConnectionFactory $factory 29 | */ 30 | private function __construct( 31 | private $factory, 32 | ) { 33 | } 34 | 35 | /** 36 | * @psalm-suppress InvalidNullableReturnType 37 | */ 38 | public static function getInstance(): SystemWideConnectionFactory 39 | { 40 | if (self::$instance === null) { 41 | $factory = new StreamConnectionFactory(); 42 | if (extension_loaded('sockets')) { 43 | self::$instance = new self(new SocketConnectionFactory($factory)); 44 | } else { 45 | self::$instance = new self($factory); 46 | } 47 | } 48 | 49 | /** @psalm-suppress NullableReturnStatement */ 50 | return self::$instance; 51 | } 52 | 53 | public function create(UriConfiguration $config): Connection 54 | { 55 | return $this->factory->create($config); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/Bolt/UriConfiguration.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\Bolt; 15 | 16 | final class UriConfiguration 17 | { 18 | /** 19 | * @param ''|'s'|'ssc' $sslLevel 20 | */ 21 | public function __construct( 22 | private readonly string $host, 23 | private readonly ?int $port, 24 | private readonly string $sslLevel, 25 | private readonly array $sslConfiguration, 26 | private readonly ?float $timeout, 27 | ) { 28 | } 29 | 30 | public function getHost(): string 31 | { 32 | return $this->host; 33 | } 34 | 35 | public function getPort(): ?int 36 | { 37 | return $this->port; 38 | } 39 | 40 | /** 41 | * @return 's'|'ssc'|'' 42 | */ 43 | public function getSslLevel(): string 44 | { 45 | return $this->sslLevel; 46 | } 47 | 48 | public function getSslConfiguration(): array 49 | { 50 | return $this->sslConfiguration; 51 | } 52 | 53 | public function getTimeout(): ?float 54 | { 55 | return $this->timeout; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/Common/ConnectionConfiguration.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\Common; 15 | 16 | use Laudis\Neo4j\Databags\DatabaseInfo; 17 | use Laudis\Neo4j\Enum\AccessMode; 18 | use Laudis\Neo4j\Enum\ConnectionProtocol; 19 | use Psr\Http\Message\UriInterface; 20 | 21 | /** 22 | * @psalm-immutable 23 | */ 24 | final class ConnectionConfiguration 25 | { 26 | /** 27 | * @param ''|'s'|'ssc' $encryptionLevel 28 | */ 29 | public function __construct( 30 | private readonly string $serverAgent, 31 | private readonly UriInterface $serverAddress, 32 | private readonly string $serverVersion, 33 | private readonly ConnectionProtocol $protocol, 34 | private readonly AccessMode $accessMode, 35 | private readonly ?DatabaseInfo $databaseInfo, 36 | private readonly string $encryptionLevel, 37 | ) { 38 | } 39 | 40 | public function getServerAgent(): string 41 | { 42 | return $this->serverAgent; 43 | } 44 | 45 | public function getServerAddress(): UriInterface 46 | { 47 | return $this->serverAddress; 48 | } 49 | 50 | public function getServerVersion(): string 51 | { 52 | return $this->serverVersion; 53 | } 54 | 55 | public function getProtocol(): ConnectionProtocol 56 | { 57 | return $this->protocol; 58 | } 59 | 60 | public function getAccessMode(): AccessMode 61 | { 62 | return $this->accessMode; 63 | } 64 | 65 | public function getDatabaseInfo(): ?DatabaseInfo 66 | { 67 | return $this->databaseInfo; 68 | } 69 | 70 | /** 71 | * @return ''|'s'|'ssc' 72 | */ 73 | public function getEncryptionLevel(): string 74 | { 75 | return $this->encryptionLevel; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/Common/GeneratorHelper.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\Common; 15 | 16 | use Generator; 17 | 18 | use function microtime; 19 | 20 | use RuntimeException; 21 | 22 | use function sprintf; 23 | 24 | final class GeneratorHelper 25 | { 26 | /** 27 | * @template T 28 | * 29 | * @param Generator $generator 30 | * 31 | * @return T 32 | */ 33 | public static function getReturnFromGenerator(Generator $generator, ?float $timeout = null) 34 | { 35 | $start = microtime(true); 36 | while ($generator->valid()) { 37 | if ($timeout !== null) { 38 | self::guardTiming($start, $timeout); 39 | } 40 | $generator->next(); 41 | } 42 | 43 | return $generator->getReturn(); 44 | } 45 | 46 | public static function guardTiming(float $start, float $timeout): void 47 | { 48 | $elapsed = microtime(true) - $start; 49 | if ($elapsed > $timeout) { 50 | throw new RuntimeException(sprintf('Cannot generator timed out out after %s seconds', $elapsed)); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/Common/Resolvable.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\Common; 15 | 16 | use function call_user_func; 17 | 18 | /** 19 | * @template Resolved 20 | */ 21 | final class Resolvable 22 | { 23 | /** @var Resolved|null */ 24 | private $resolved; 25 | private bool $isResolved = false; 26 | /** @var callable():Resolved */ 27 | private $toResolve; 28 | 29 | /** @var array */ 30 | private static array $cache = []; 31 | 32 | /** 33 | * @psalm-mutation-free 34 | * 35 | * @param callable():Resolved $toResolve 36 | */ 37 | public function __construct($toResolve) 38 | { 39 | $this->toResolve = $toResolve; 40 | } 41 | 42 | /** 43 | * @pure 44 | * 45 | * @template U 46 | * 47 | * @param callable():U $toResolve 48 | * 49 | * @return Resolvable 50 | */ 51 | public static function once(string $key, $toResolve): Resolvable 52 | { 53 | /** @psalm-suppress MissingClosureReturnType */ 54 | $tbr = static function () use ($key, $toResolve) { 55 | if (!array_key_exists($key, self::$cache)) { 56 | self::$cache[$key] = call_user_func($toResolve); 57 | } 58 | 59 | /** @var U */ 60 | return self::$cache[$key]; 61 | }; 62 | 63 | /** @var self */ 64 | return new Resolvable($tbr); 65 | } 66 | 67 | /** 68 | * @return Resolved 69 | */ 70 | public function resolve() 71 | { 72 | if (!$this->isResolved) { 73 | $this->resolved = call_user_func($this->toResolve); 74 | $this->isResolved = true; 75 | } 76 | 77 | return $this->resolved; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/Common/ResponseHelper.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\Common; 15 | 16 | use Bolt\enum\Signature; 17 | use Bolt\protocol\Response; 18 | use Bolt\protocol\V4_4; 19 | use Bolt\protocol\V5; 20 | use Bolt\protocol\V5_1; 21 | use Bolt\protocol\V5_2; 22 | use Bolt\protocol\V5_3; 23 | use Bolt\protocol\V5_4; 24 | use Laudis\Neo4j\Exception\Neo4jException; 25 | 26 | class ResponseHelper 27 | { 28 | public static function getResponse(V4_4|V5|V5_1|V5_2|V5_3|V5_4 $protocol): Response 29 | { 30 | $response = $protocol->getResponse(); 31 | if ($response->signature === Signature::FAILURE) { 32 | throw Neo4jException::fromBoltResponse($response); 33 | } 34 | 35 | return $response; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/Common/SemaphoreFactory.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\Common; 15 | 16 | use function extension_loaded; 17 | 18 | use Laudis\Neo4j\Contracts\SemaphoreFactoryInterface; 19 | use Laudis\Neo4j\Contracts\SemaphoreInterface; 20 | use Laudis\Neo4j\Databags\DriverConfiguration; 21 | use Psr\Http\Message\UriInterface; 22 | 23 | final class SemaphoreFactory implements SemaphoreFactoryInterface 24 | { 25 | private static ?SemaphoreFactory $instance = null; 26 | /** @var callable(string, int):SemaphoreInterface */ 27 | private $constructor; 28 | 29 | /** 30 | * @param callable(string, int):SemaphoreInterface $constructor 31 | */ 32 | private function __construct($constructor) 33 | { 34 | $this->constructor = $constructor; 35 | } 36 | 37 | public static function getInstance(): self 38 | { 39 | return self::$instance ??= (extension_loaded('ext-sysvsem')) ? 40 | new self([SysVSemaphore::class, 'create']) : 41 | new self([SingleThreadedSemaphore::class, 'create']); 42 | } 43 | 44 | public function create(UriInterface $uri, DriverConfiguration $config): SemaphoreInterface 45 | { 46 | // Because interprocess switching of connections between PHP sessions is impossible, 47 | // we have to build a key to limit the amount of open connections, potentially between ALL sessions. 48 | // because of this we have to settle on a configuration basis to limit the connection pool, 49 | // not on an object basis. 50 | // The combination is between the server and the user agent as it most closely resembles an "application" 51 | // connecting to a server. The application thus supports multiple authentication methods, but they have 52 | // to be shared between the same connection pool. 53 | $key = $uri->getHost().':'.($uri->getPort() ?? '').':'.$config->getUserAgent(); 54 | 55 | return ($this->constructor)($key, $config->getMaxPoolSize()); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/Common/SingleThreadedSemaphore.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\Common; 15 | 16 | use Generator; 17 | use Laudis\Neo4j\Contracts\SemaphoreInterface; 18 | 19 | use function microtime; 20 | 21 | use RuntimeException; 22 | 23 | class SingleThreadedSemaphore implements SemaphoreInterface 24 | { 25 | private int $amount = 0; 26 | /** @var array */ 27 | private static array $instances = []; 28 | 29 | private function __construct( 30 | private readonly int $max, 31 | ) { 32 | } 33 | 34 | public static function create(string $key, int $max): self 35 | { 36 | if (!array_key_exists($key, self::$instances)) { 37 | self::$instances[$key] = new self($max); 38 | } 39 | 40 | return self::$instances[$key]; 41 | } 42 | 43 | public function wait(): Generator 44 | { 45 | $start = microtime(true); 46 | while ($this->amount >= $this->max) { 47 | /** @var bool $continue */ 48 | $continue = yield $start - microtime(true); 49 | if (!$continue) { 50 | return; 51 | } 52 | } 53 | ++$this->amount; 54 | } 55 | 56 | public function post(): void 57 | { 58 | if ($this->amount <= 0) { 59 | throw new RuntimeException('Semaphore underflow'); 60 | } 61 | --$this->amount; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/Common/SysVSemaphore.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\Common; 15 | 16 | use Generator; 17 | 18 | use function hash; 19 | 20 | use Laudis\Neo4j\Contracts\SemaphoreInterface; 21 | 22 | use function microtime; 23 | 24 | use RuntimeException; 25 | 26 | use function sem_acquire; 27 | use function sem_get; 28 | use function sem_release; 29 | 30 | class SysVSemaphore implements SemaphoreInterface 31 | { 32 | private function __construct( 33 | private readonly \SysvSemaphore $semaphore, 34 | ) { 35 | } 36 | 37 | public static function create(string $key, int $max): self 38 | { 39 | $key = hash('sha512', $key, true); 40 | $key = substr($key, 0, 8); 41 | 42 | if (!function_exists('sem_get')) { 43 | throw new RuntimeException('Can only create a semaphore if the sysv extension is installed'); 44 | } 45 | 46 | $semaphore = sem_get(hexdec($key), $max); 47 | if ($semaphore === false) { 48 | throw new RuntimeException('Could not create semaphore'); 49 | } 50 | 51 | return new self($semaphore); 52 | } 53 | 54 | public function wait(): Generator 55 | { 56 | $start = microtime(true); 57 | while (!sem_acquire($this->semaphore, true)) { 58 | /** @var bool $continue */ 59 | $continue = yield $start - microtime(true); 60 | if (!$continue) { 61 | return; 62 | } 63 | } 64 | } 65 | 66 | public function post(): void 67 | { 68 | if (!sem_release($this->semaphore)) { 69 | throw new RuntimeException('Cannot release semaphore'); 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/Common/TransactionHelper.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\Common; 15 | 16 | use Laudis\Neo4j\Contracts\CypherSequence; 17 | use Laudis\Neo4j\Contracts\TransactionInterface; 18 | use Laudis\Neo4j\Contracts\UnmanagedTransactionInterface; 19 | use Laudis\Neo4j\Exception\Neo4jException; 20 | 21 | final class TransactionHelper 22 | { 23 | public const ROLLBACK_CLASSIFICATIONS = ['ClientError', 'TransientError', 'DatabaseError']; 24 | 25 | /** 26 | * @template U 27 | * 28 | * @param callable():UnmanagedTransactionInterface $tsxFactory 29 | * @param callable(TransactionInterface):U $tsxHandler 30 | * 31 | * @return U 32 | */ 33 | public static function retry(callable $tsxFactory, callable $tsxHandler) 34 | { 35 | while (true) { 36 | $transaction = null; 37 | try { 38 | $transaction = $tsxFactory(); 39 | $tbr = $tsxHandler($transaction); 40 | self::triggerLazyResult($tbr); 41 | $transaction->commit(); 42 | 43 | return $tbr; 44 | } catch (Neo4jException $e) { 45 | if ($transaction && !in_array($e->getClassification(), self::ROLLBACK_CLASSIFICATIONS)) { 46 | $transaction->rollback(); 47 | } 48 | 49 | if ($e->getClassification() !== 'TransientError') { 50 | throw $e; 51 | } 52 | } 53 | } 54 | } 55 | 56 | private static function triggerLazyResult(mixed $tbr): void 57 | { 58 | if ($tbr instanceof CypherSequence) { 59 | $tbr->preload(); 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/Contracts/AddressResolverInterface.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\Contracts; 15 | 16 | use Generator; 17 | 18 | interface AddressResolverInterface 19 | { 20 | /** 21 | * Returns the addresses. 22 | * 23 | * @return Generator 24 | */ 25 | public function getAddresses(string $host): Generator; 26 | } 27 | -------------------------------------------------------------------------------- /src/Contracts/AuthenticateInterface.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\Contracts; 15 | 16 | use Bolt\protocol\V4_4; 17 | use Bolt\protocol\V5; 18 | use Bolt\protocol\V5_1; 19 | use Bolt\protocol\V5_2; 20 | use Bolt\protocol\V5_3; 21 | use Bolt\protocol\V5_4; 22 | use Psr\Http\Message\UriInterface; 23 | 24 | interface AuthenticateInterface 25 | { 26 | /** 27 | * Authenticates a Bolt connection with the provided configuration Uri and userAgent. 28 | * 29 | * @return array{server: string, connection_id: string, hints: list} 30 | */ 31 | public function authenticateBolt(V4_4|V5|V5_1|V5_2|V5_3|V5_4 $protocol, string $userAgent): array; 32 | 33 | /** 34 | * Returns a string representation of the authentication. 35 | */ 36 | public function toString(UriInterface $uri): string; 37 | } 38 | -------------------------------------------------------------------------------- /src/Contracts/BasicConnectionFactoryInterface.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\Contracts; 15 | 16 | use Laudis\Neo4j\Bolt\Connection; 17 | use Laudis\Neo4j\Bolt\UriConfiguration; 18 | 19 | interface BasicConnectionFactoryInterface 20 | { 21 | public function create(UriConfiguration $config): Connection; 22 | } 23 | -------------------------------------------------------------------------------- /src/Contracts/BoltConvertibleInterface.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\Contracts; 15 | 16 | use Bolt\protocol\IStructure; 17 | 18 | interface BoltConvertibleInterface 19 | { 20 | public function convertToBolt(): IStructure; 21 | } 22 | -------------------------------------------------------------------------------- /src/Contracts/BoltMessage.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\Contracts; 15 | 16 | use Bolt\protocol\Response; 17 | use Bolt\protocol\V4_4; 18 | use Bolt\protocol\V5; 19 | use Bolt\protocol\V5_1; 20 | use Bolt\protocol\V5_2; 21 | use Bolt\protocol\V5_3; 22 | use Bolt\protocol\V5_4; 23 | use Iterator; 24 | 25 | abstract class BoltMessage 26 | { 27 | public function __construct( 28 | private readonly V4_4|V5|V5_1|V5_2|V5_3|V5_4 $protocol, 29 | ) { 30 | } 31 | 32 | /** 33 | * Sends the Bolt message. 34 | */ 35 | abstract public function send(): BoltMessage; 36 | 37 | public function getResponse(): Response 38 | { 39 | return $this->protocol->getResponse(); 40 | } 41 | 42 | /** 43 | * @return Iterator 44 | */ 45 | public function getResponses(): Iterator 46 | { 47 | /** 48 | * @var Iterator 49 | */ 50 | return $this->protocol->getResponses(); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/Contracts/ConnectionPoolInterface.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\Contracts; 15 | 16 | use Generator; 17 | use Laudis\Neo4j\Databags\SessionConfiguration; 18 | 19 | /** 20 | * A connection pool acts as a connection factory by managing multiple connections. 21 | * 22 | * @template Connection of ConnectionInterface 23 | */ 24 | interface ConnectionPoolInterface 25 | { 26 | /** 27 | * Acquires a connection from the pool. 28 | * 29 | * A key will be the amount of times you have fetched the value of the generator. 30 | * The value will be the time in seconds that has passed since requesting the connection. 31 | * You can abort the process of acquiring a connection by sending false to the generator. 32 | * The returned value will be the actual connection. 33 | * 34 | * @return Generator< 35 | * int, 36 | * float, 37 | * bool, 38 | * Connection 39 | * > 40 | */ 41 | public function acquire(SessionConfiguration $config): Generator; 42 | 43 | /** 44 | * Releases a connection back to the pool. 45 | */ 46 | public function release(ConnectionInterface $connection): void; 47 | 48 | /** 49 | * Closes all connections in the pool. 50 | */ 51 | public function close(): void; 52 | } 53 | -------------------------------------------------------------------------------- /src/Contracts/DriverInterface.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\Contracts; 15 | 16 | use Laudis\Neo4j\Databags\SessionConfiguration; 17 | use Laudis\Neo4j\Types\CypherList; 18 | use Laudis\Neo4j\Types\CypherMap; 19 | 20 | /** 21 | * The driver creates sessions for carrying out work. 22 | * 23 | * @psalm-type ParsedUrl = array{host: string, pass: string|null, path: string, port: int, query: array, scheme: string, user: string|null} 24 | * @psalm-type BasicDriver = DriverInterface>> 25 | */ 26 | interface DriverInterface 27 | { 28 | /** 29 | * @psalm-mutation-free 30 | */ 31 | public function createSession(?SessionConfiguration $config = null): SessionInterface; 32 | 33 | /** 34 | * Returns true if the driver can make a valid connection with the server. 35 | */ 36 | public function verifyConnectivity(?SessionConfiguration $config = null): bool; 37 | 38 | /** 39 | * Closes all connections in the pool. 40 | */ 41 | public function closeConnections(): void; 42 | } 43 | -------------------------------------------------------------------------------- /src/Contracts/HasPropertiesInterface.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\Contracts; 15 | 16 | use BadMethodCallException; 17 | use Laudis\Neo4j\Types\CypherMap; 18 | 19 | /** 20 | * Defines how an object with properties should behave. 21 | * 22 | * @psalm-immutable 23 | * 24 | * @template T 25 | */ 26 | interface HasPropertiesInterface 27 | { 28 | /** 29 | * Returns the properties a map. 30 | * 31 | * @return CypherMap 32 | */ 33 | public function getProperties(): CypherMap; 34 | 35 | /** 36 | * @param string $name 37 | * 38 | * @return T 39 | */ 40 | public function __get($name); 41 | 42 | /** 43 | * Always throws an exception as cypher objects are immutable. 44 | * 45 | * @param string $name 46 | * @param T $value 47 | * 48 | * @throws BadMethodCallException 49 | */ 50 | public function __set($name, $value): void; 51 | 52 | /** 53 | * Checks to see if the property exists and is set. 54 | * 55 | * @param string $name 56 | */ 57 | public function __isset($name): bool; 58 | } 59 | -------------------------------------------------------------------------------- /src/Contracts/PointInterface.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\Contracts; 15 | 16 | /** 17 | * Defines a basic Point type in neo4j. 18 | * 19 | * @psalm-immutable 20 | * 21 | * @psalm-type Crs = 'wgs-84'|'wgs-84-3d'|'cartesian'|'cartesian-3d'; 22 | */ 23 | interface PointInterface 24 | { 25 | /** 26 | * Returns the x coordinate. 27 | */ 28 | public function getX(): float; 29 | 30 | /** 31 | * Returns the y coordinate. 32 | */ 33 | public function getY(): float; 34 | 35 | /** 36 | * Returns the Coordinates Reference System. 37 | * 38 | * @see https://en.wikipedia.org/wiki/Spatial_reference_system 39 | * 40 | * @return Crs 41 | */ 42 | public function getCrs(): string; 43 | 44 | /** 45 | * Returns the spacial reference identifier. 46 | * 47 | * @see https://en.wikipedia.org/wiki/Spatial_reference_system 48 | */ 49 | public function getSrid(): int; 50 | } 51 | -------------------------------------------------------------------------------- /src/Contracts/SemaphoreFactoryInterface.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\Contracts; 15 | 16 | use Laudis\Neo4j\Databags\DriverConfiguration; 17 | use Psr\Http\Message\UriInterface; 18 | 19 | interface SemaphoreFactoryInterface 20 | { 21 | public function create(UriInterface $uri, DriverConfiguration $config): SemaphoreInterface; 22 | } 23 | -------------------------------------------------------------------------------- /src/Contracts/SemaphoreInterface.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\Contracts; 15 | 16 | use Generator; 17 | 18 | interface SemaphoreInterface 19 | { 20 | /** 21 | * Returns a generator that can be used to wait for the semaphore to be released. 22 | * 23 | * The key of the generator is the amount of times you have already received a value before this one. 24 | * The value of the generator is the amount of time in seconds that has passed since calling wait. 25 | * You can stop the wait by sending false to the generator. 26 | * There is no return value in this generator. 27 | * 28 | * @return Generator 29 | */ 30 | public function wait(): Generator; 31 | 32 | /** 33 | * Releases the semaphore. 34 | */ 35 | public function post(): void; 36 | } 37 | -------------------------------------------------------------------------------- /src/Contracts/TransactionInterface.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\Contracts; 15 | 16 | use Laudis\Neo4j\Databags\Statement; 17 | use Laudis\Neo4j\Databags\SummarizedResult; 18 | use Laudis\Neo4j\Exception\Neo4jException; 19 | use Laudis\Neo4j\Types\CypherList; 20 | 21 | /** 22 | * Transactions are atomic units of work that may contain one or more query. 23 | * 24 | * @see https://neo4j.com/docs/cypher-manual/current/introduction/transactions/ 25 | */ 26 | interface TransactionInterface 27 | { 28 | /** 29 | * @param iterable $parameters 30 | */ 31 | public function run(string $statement, iterable $parameters = []): SummarizedResult; 32 | 33 | public function runStatement(Statement $statement): SummarizedResult; 34 | 35 | /** 36 | * @param iterable $statements 37 | * 38 | * @throws Neo4jException 39 | * 40 | * @return CypherList 41 | */ 42 | public function runStatements(iterable $statements): CypherList; 43 | } 44 | -------------------------------------------------------------------------------- /src/Contracts/UnmanagedTransactionInterface.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\Contracts; 15 | 16 | use Laudis\Neo4j\Databags\Statement; 17 | use Laudis\Neo4j\Databags\SummarizedResult; 18 | use Laudis\Neo4j\Types\CypherList; 19 | 20 | /** 21 | * An unmanaged transaction needs to be committed or rolled back manually. 22 | * 23 | * @see https://neo4j.com/docs/cypher-manual/current/introduction/transactions/ 24 | */ 25 | interface UnmanagedTransactionInterface extends TransactionInterface 26 | { 27 | /** 28 | * Runs the final statements provided and then commits the entire transaction. 29 | * 30 | * @param iterable $statements 31 | * 32 | * @return CypherList 33 | */ 34 | public function commit(iterable $statements = []): CypherList; 35 | 36 | /** 37 | * Rolls back the transaction. 38 | */ 39 | public function rollback(): void; 40 | 41 | /** 42 | * Returns whether the transaction has been rolled back. 43 | */ 44 | public function isRolledBack(): bool; 45 | 46 | /** 47 | * Returns whether the transaction has been committed. 48 | */ 49 | public function isCommitted(): bool; 50 | 51 | /** 52 | * Returns whether the transaction is safe to use. 53 | */ 54 | public function isFinished(): bool; 55 | } 56 | -------------------------------------------------------------------------------- /src/Databags/Bookmark.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\Databags; 15 | 16 | use function array_unique; 17 | 18 | final class Bookmark 19 | { 20 | /** @var list */ 21 | private readonly array $values; 22 | 23 | /** 24 | * @param list $bookmarks 25 | */ 26 | public function __construct(?array $bookmarks = null) 27 | { 28 | $this->values = $bookmarks ?? []; 29 | } 30 | 31 | public function isEmpty(): bool 32 | { 33 | return count($this->values) === 0; 34 | } 35 | 36 | /** 37 | * @return list 38 | */ 39 | public function values(): array 40 | { 41 | return $this->values; 42 | } 43 | 44 | /** 45 | * @param iterable|null $bookmarks 46 | */ 47 | public static function from(?iterable $bookmarks): self 48 | { 49 | $bookmarks ??= []; 50 | $values = []; 51 | 52 | foreach ($bookmarks as $bookmark) { 53 | array_push($values, ...$bookmark->values()); 54 | $values = array_values(array_unique($values)); 55 | } 56 | 57 | return new self($values); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/Databags/BookmarkHolder.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\Databags; 15 | 16 | final class BookmarkHolder 17 | { 18 | public function __construct( 19 | private Bookmark $bookmark, 20 | ) { 21 | } 22 | 23 | public function getBookmark(): Bookmark 24 | { 25 | return $this->bookmark; 26 | } 27 | 28 | public function setBookmark(Bookmark $bookmark): void 29 | { 30 | $this->bookmark = $bookmark; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/Databags/ConnectionRequestData.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\Databags; 15 | 16 | use Laudis\Neo4j\Contracts\AuthenticateInterface; 17 | use Psr\Http\Message\UriInterface; 18 | 19 | /** 20 | * @internal 21 | */ 22 | final class ConnectionRequestData 23 | { 24 | public function __construct( 25 | private readonly string $hostname, 26 | private readonly UriInterface $uri, 27 | private readonly AuthenticateInterface $auth, 28 | private readonly string $userAgent, 29 | private readonly SslConfiguration $config, 30 | ) { 31 | } 32 | 33 | public function getHostname(): string 34 | { 35 | return $this->hostname; 36 | } 37 | 38 | public function getUri(): UriInterface 39 | { 40 | return $this->uri; 41 | } 42 | 43 | public function getAuth(): AuthenticateInterface 44 | { 45 | return $this->auth; 46 | } 47 | 48 | public function getUserAgent(): string 49 | { 50 | return $this->userAgent; 51 | } 52 | 53 | public function getSslConfig(): SslConfiguration 54 | { 55 | return $this->config; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/Databags/DatabaseInfo.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\Databags; 15 | 16 | use Laudis\Neo4j\Types\AbstractCypherObject; 17 | 18 | /** 19 | * Stores relevant information of a database. 20 | * 21 | * @psalm-immutable 22 | * 23 | * @extends AbstractCypherObject 24 | */ 25 | final class DatabaseInfo extends AbstractCypherObject 26 | { 27 | public function __construct( 28 | private readonly string $name, 29 | ) { 30 | } 31 | 32 | /** 33 | * Returns the name of the database. 34 | */ 35 | public function getName(): string 36 | { 37 | return $this->name; 38 | } 39 | 40 | public function toArray(): array 41 | { 42 | return ['name' => $this->name]; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/Databags/DriverSetup.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\Databags; 15 | 16 | use Laudis\Neo4j\Contracts\AuthenticateInterface; 17 | use Psr\Http\Message\UriInterface; 18 | 19 | /** 20 | * Basic object containing all the information needed to setup a driver. 21 | * 22 | * @psalm-immutable 23 | */ 24 | final class DriverSetup 25 | { 26 | public function __construct( 27 | private readonly UriInterface $uri, 28 | private readonly AuthenticateInterface $auth, 29 | ) { 30 | } 31 | 32 | public function getAuth(): AuthenticateInterface 33 | { 34 | return $this->auth; 35 | } 36 | 37 | public function getUri(): UriInterface 38 | { 39 | return $this->uri; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/Databags/Pair.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\Databags; 15 | 16 | /** 17 | * A basic Key value Pair. 18 | * 19 | * @template TKey 20 | * @template TValue 21 | * 22 | * @psalm-immutable 23 | */ 24 | final class Pair 25 | { 26 | /** 27 | * @param TKey $key 28 | * @param TValue $value 29 | */ 30 | public function __construct( 31 | private $key, 32 | private $value, 33 | ) { 34 | } 35 | 36 | /** 37 | * @return TKey 38 | */ 39 | public function getKey() 40 | { 41 | return $this->key; 42 | } 43 | 44 | /** 45 | * @return TValue 46 | */ 47 | public function getValue() 48 | { 49 | return $this->value; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/Databags/Plan.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\Databags; 15 | 16 | /** 17 | * This describes the plan that the database planner produced and used (or will use) to execute your query. 18 | * 19 | * @see https://neo4j.com/docs/cypher-manual/current/execution-plans/ 20 | */ 21 | final class Plan 22 | { 23 | /** 24 | * @param list $children 25 | * @param list $identifiers 26 | */ 27 | public function __construct( 28 | private readonly PlanArguments $args, 29 | private readonly array $children, 30 | private readonly array $identifiers, 31 | private readonly string $operator, 32 | ) { 33 | } 34 | 35 | /** 36 | * Returns the arguments for the operator. 37 | */ 38 | public function getArgs(): PlanArguments 39 | { 40 | return $this->args; 41 | } 42 | 43 | /** 44 | * Returns the sub-plans. 45 | * 46 | * @return list 47 | */ 48 | public function getChildren(): array 49 | { 50 | return $this->children; 51 | } 52 | 53 | /** 54 | * Identifiers used by this part of the plan. 55 | * 56 | * @return list 57 | */ 58 | public function getIdentifiers(): array 59 | { 60 | return $this->identifiers; 61 | } 62 | 63 | /** 64 | * The operation this plan is performing. 65 | */ 66 | public function getOperator(): string 67 | { 68 | return $this->operator; 69 | } 70 | 71 | public function toArray(): array 72 | { 73 | return [ 74 | 'arguments' => $this->args, 75 | 'list' => $this->children, 76 | 'identifiers' => $this->identifiers, 77 | 'operator' => $this->operator, 78 | ]; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/Databags/Position.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\Databags; 15 | 16 | /** 17 | * @psalm-immutable 18 | */ 19 | final class Position 20 | { 21 | public function __construct( 22 | private int $column, 23 | private int $offset, 24 | private int $line, 25 | ) { 26 | } 27 | 28 | public function getLine(): int 29 | { 30 | return $this->line; 31 | } 32 | 33 | public function getOffset(): int 34 | { 35 | return $this->offset; 36 | } 37 | 38 | public function getColumn(): int 39 | { 40 | return $this->column; 41 | } 42 | 43 | public function toArray(): array 44 | { 45 | return [ 46 | 'column' => $this->column, 47 | 'offset' => $this->offset, 48 | 'line' => $this->line, 49 | ]; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/Databags/ProfiledQueryPlan.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\Databags; 15 | 16 | final class ProfiledQueryPlan 17 | { 18 | /** 19 | * @param list $children 20 | * @param list $identifiers 21 | */ 22 | public function __construct( 23 | public readonly PlanArguments $arguments, 24 | public readonly int $dbHits = 0, 25 | public readonly int $records = 0, 26 | public readonly bool $hasPageCacheStats = false, 27 | public readonly int $pageCacheHits = 0, 28 | public readonly int $pageCacheMisses = 0, 29 | public readonly float $pageCacheHitRatio = 0.0, 30 | public readonly int $time = 0, 31 | public readonly string $operatorType = '', 32 | public readonly array $children = [], 33 | public readonly array $identifiers = [], 34 | ) { 35 | } 36 | 37 | public function toArray(): array 38 | { 39 | return [ 40 | 'arguments' => $this->arguments->toArray(), 41 | 'dbHits' => $this->dbHits, 42 | 'records' => $this->records, 43 | 'hasPageCacheStats' => $this->hasPageCacheStats, 44 | 'pageCacheHits' => $this->pageCacheHits, 45 | 'pageCacheMisses' => $this->pageCacheMisses, 46 | 'pageCacheHitRatio' => $this->pageCacheHitRatio, 47 | 'time' => $this->time, 48 | 'operatorType' => $this->operatorType, 49 | 'children' => array_map( 50 | static fn (ProfiledQueryPlan $child): array => $child->toArray(), 51 | $this->children 52 | ), 53 | 'identifiers' => $this->identifiers, 54 | ]; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/Databags/ServerInfo.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\Databags; 15 | 16 | use Laudis\Neo4j\Enum\ConnectionProtocol; 17 | use Laudis\Neo4j\Types\AbstractCypherObject; 18 | use Psr\Http\Message\UriInterface; 19 | 20 | /** 21 | * Provides some basic information of the server where the result is obtained from. 22 | * 23 | * @psalm-immutable 24 | * 25 | * @extends AbstractCypherObject 26 | */ 27 | final class ServerInfo extends AbstractCypherObject 28 | { 29 | public function __construct( 30 | private readonly UriInterface $address, 31 | private readonly ConnectionProtocol $protocol, 32 | private readonly string $agent, 33 | ) { 34 | } 35 | 36 | /** 37 | * Returns the uri of the server the query was executed. 38 | */ 39 | public function getAddress(): UriInterface 40 | { 41 | return $this->address; 42 | } 43 | 44 | /** 45 | * Returns Connection Protocol version with which the remote server communicates. 46 | */ 47 | public function getProtocol(): ConnectionProtocol 48 | { 49 | return $this->protocol; 50 | } 51 | 52 | /** 53 | * Returns server agent string by which the remote server identifies itself. 54 | */ 55 | public function getAgent(): string 56 | { 57 | return $this->agent; 58 | } 59 | 60 | public function toArray(): array 61 | { 62 | return [ 63 | 'address' => $this->address, 64 | 'protocolVersion' => $this->protocol, 65 | 'agent' => $this->agent, 66 | ]; 67 | } 68 | 69 | /** 70 | * @psalm-suppress ImpureMethodCall 71 | */ 72 | public function jsonSerialize(): array 73 | { 74 | return [ 75 | 'address' => "{$this->address->getHost()}:{$this->address->getPort()}", 76 | 'protocolVersion' => $this->protocol, 77 | 'agent' => $this->agent, 78 | ]; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/Databags/SslConfiguration.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\Databags; 15 | 16 | use Laudis\Neo4j\Enum\SslMode; 17 | 18 | /** 19 | * @psalm-immutable 20 | */ 21 | final class SslConfiguration 22 | { 23 | public function __construct( 24 | private SslMode $mode, 25 | private bool $verifyPeer, 26 | ) { 27 | } 28 | 29 | public function getMode(): SslMode 30 | { 31 | return $this->mode; 32 | } 33 | 34 | public function isVerifyPeer(): bool 35 | { 36 | return $this->verifyPeer; 37 | } 38 | 39 | /** 40 | * @pure 41 | */ 42 | public static function create(SslMode $mode, bool $verifyPeer): self 43 | { 44 | return new self($mode, $verifyPeer); 45 | } 46 | 47 | /** 48 | * @pure 49 | */ 50 | public static function default(): self 51 | { 52 | /** @psalm-suppress ImpureMethodCall */ 53 | return self::create(SslMode::FROM_URL(), true); 54 | } 55 | 56 | public function withMode(SslMode $mode): self 57 | { 58 | $tbr = clone $this; 59 | $tbr->mode = $mode; 60 | 61 | return $tbr; 62 | } 63 | 64 | public function withVerifyPeer(bool $verify): self 65 | { 66 | $tbr = clone $this; 67 | $tbr->verifyPeer = $verify; 68 | 69 | return $tbr; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/Databags/Statement.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\Databags; 15 | 16 | use Laudis\Neo4j\Types\AbstractCypherObject; 17 | 18 | /** 19 | * The components of a Cypher query, containing the query text and parameter mapping. 20 | * 21 | * @todo deprecate and create Query Object 22 | * 23 | * @psalm-immutable 24 | * 25 | * @extends AbstractCypherObject 26 | */ 27 | final class Statement extends AbstractCypherObject 28 | { 29 | /** 30 | * @param iterable $parameters 31 | */ 32 | public function __construct( 33 | private readonly string $text, 34 | private readonly iterable $parameters, 35 | ) { 36 | } 37 | 38 | /** 39 | * @pure 40 | * 41 | * @param iterable|null $parameters 42 | */ 43 | public static function create(string $text, ?iterable $parameters = null): Statement 44 | { 45 | return new self($text, $parameters ?? []); 46 | } 47 | 48 | /** 49 | * The query text. 50 | */ 51 | public function getText(): string 52 | { 53 | return $this->text; 54 | } 55 | 56 | /** 57 | * The parameter mapping. 58 | * 59 | * @return iterable 60 | */ 61 | public function getParameters(): iterable 62 | { 63 | return $this->parameters; 64 | } 65 | 66 | public function toArray(): array 67 | { 68 | return [ 69 | 'text' => $this->text, 70 | 'parameters' => $this->parameters, 71 | ]; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/Databags/SummarizedResult.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\Databags; 15 | 16 | use Generator; 17 | use Laudis\Neo4j\Formatter\SummarizedResultFormatter; 18 | use Laudis\Neo4j\Types\CypherList; 19 | use Laudis\Neo4j\Types\CypherMap; 20 | 21 | /** 22 | * A result containing the values and the summary. 23 | * 24 | * @psalm-import-type OGMTypes from SummarizedResultFormatter 25 | * 26 | * @extends CypherList> 27 | */ 28 | final class SummarizedResult extends CypherList 29 | { 30 | private ?ResultSummary $summary = null; 31 | 32 | /** 33 | * @param iterable>|callable():Generator> $iterable 34 | * 35 | * @psalm-mutation-free 36 | */ 37 | public function __construct(?ResultSummary &$summary, iterable|callable $iterable = []) 38 | { 39 | parent::__construct($iterable); 40 | $this->summary = &$summary; 41 | } 42 | 43 | /** 44 | * Returns the result summary. 45 | */ 46 | public function getSummary(): ResultSummary 47 | { 48 | while ($this->summary === null && $this->valid()) { 49 | $this->next(); 50 | } 51 | 52 | /** @var ResultSummary */ 53 | return $this->summary; 54 | } 55 | 56 | /** 57 | * @return CypherList> 58 | */ 59 | public function getResults(): CypherList 60 | { 61 | return new CypherList($this); 62 | } 63 | 64 | /** 65 | * @return array{summary: ResultSummary|null, result: mixed} 66 | */ 67 | public function jsonSerialize(): array 68 | { 69 | return [ 70 | 'summary' => $this->summary, 71 | 'result' => parent::jsonSerialize(), 72 | ]; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/Enum/AccessMode.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\Enum; 15 | 16 | use JsonSerializable; 17 | use Laudis\TypedEnum\TypedEnum; 18 | 19 | /** 20 | * Defines the access mode of a connection. 21 | * 22 | * @method static self READ() 23 | * @method static self WRITE() 24 | * 25 | * @extends TypedEnum 26 | * 27 | * @psalm-immutable 28 | * 29 | * @psalm-suppress MutableDependency 30 | */ 31 | final class AccessMode extends TypedEnum implements JsonSerializable 32 | { 33 | private const READ = 'read'; 34 | private const WRITE = 'write'; 35 | 36 | public function jsonSerialize(): string 37 | { 38 | return $this->getValue(); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/Enum/QueryTypeEnum.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\Enum; 15 | 16 | use JsonSerializable; 17 | use Laudis\Neo4j\Databags\SummaryCounters; 18 | use Laudis\TypedEnum\TypedEnum; 19 | use Stringable; 20 | 21 | /** 22 | * The actual type of query after is has been run. 23 | * 24 | * @method static self READ_ONLY() 25 | * @method static self READ_WRITE() 26 | * @method static self SCHEMA_WRITE() 27 | * @method static self WRITE_ONLY() 28 | * 29 | * @psalm-immutable 30 | * 31 | * @extends TypedEnum 32 | * 33 | * @psalm-suppress MutableDependency 34 | */ 35 | final class QueryTypeEnum extends TypedEnum implements JsonSerializable, Stringable 36 | { 37 | private const READ_ONLY = 'r'; 38 | private const READ_WRITE = 'rw'; 39 | private const SCHEMA_WRITE = 's'; 40 | private const WRITE_ONLY = 'w'; 41 | 42 | /** 43 | * Decide the type of the query from the provided counters. 44 | * 45 | * @pure 46 | * 47 | * @psalm-suppress ImpureMethodCall 48 | */ 49 | public static function fromCounters(SummaryCounters $counters): self 50 | { 51 | if ($counters->containsUpdates() || $counters->containsSystemUpdates()) { 52 | return self::READ_WRITE(); 53 | } 54 | 55 | if ($counters->constraintsAdded() || $counters->constraintsRemoved() || $counters->indexesAdded() || $counters->indexesRemoved()) { 56 | return self::SCHEMA_WRITE(); 57 | } 58 | 59 | return self::READ_ONLY(); 60 | } 61 | 62 | public function __toString(): string 63 | { 64 | return $this->getValue(); 65 | } 66 | 67 | public function jsonSerialize(): string 68 | { 69 | return $this->getValue(); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/Enum/RoutingRoles.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\Enum; 15 | 16 | use JsonSerializable; 17 | use Laudis\TypedEnum\TypedEnum; 18 | 19 | /** 20 | * The possible routing roles. 21 | * 22 | * @method static RoutingRoles LEADER() 23 | * @method static RoutingRoles FOLLOWER() 24 | * @method static RoutingRoles ROUTE() 25 | * 26 | * @extends TypedEnum> 27 | * 28 | * @psalm-immutable 29 | * 30 | * @psalm-suppress MutableDependency 31 | */ 32 | final class RoutingRoles extends TypedEnum implements JsonSerializable 33 | { 34 | private const LEADER = ['WRITE', 'LEADER']; 35 | private const FOLLOWER = ['READ', 'FOLLOWER']; 36 | private const ROUTE = ['ROUTE']; 37 | 38 | /** 39 | * @psalm-suppress ImpureMethodCall 40 | */ 41 | public function jsonSerialize(): string 42 | { 43 | if ($this === self::LEADER()) { 44 | return 'LEADER'; 45 | } 46 | 47 | if ($this === self::FOLLOWER()) { 48 | return 'FOLLOWER'; 49 | } 50 | 51 | return 'ROUTE'; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/Enum/SslMode.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\Enum; 15 | 16 | use JsonSerializable; 17 | use Laudis\TypedEnum\TypedEnum; 18 | use Stringable; 19 | 20 | /** 21 | * @method static self ENABLE() 22 | * @method static self DISABLE() 23 | * @method static self FROM_URL() 24 | * @method static self ENABLE_WITH_SELF_SIGNED() 25 | * 26 | * @extends TypedEnum 27 | * 28 | * @psalm-immutable 29 | * 30 | * @psalm-suppress MutableDependency 31 | */ 32 | final class SslMode extends TypedEnum implements JsonSerializable, Stringable 33 | { 34 | private const ENABLE = 'enable'; 35 | private const ENABLE_WITH_SELF_SIGNED = 'enable_with_self_signed'; 36 | private const DISABLE = 'disable'; 37 | private const FROM_URL = 'from_url'; 38 | 39 | public function __toString(): string 40 | { 41 | /** @noinspection MagicMethodsValidityInspection */ 42 | return $this->getValue(); 43 | } 44 | 45 | public function jsonSerialize(): string 46 | { 47 | return $this->getValue(); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/Enum/TransactionState.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\Enum; 15 | 16 | /** 17 | * The state of a transaction. 18 | */ 19 | enum TransactionState 20 | { 21 | /** 22 | * The transaction is running with no explicit success or failure marked. 23 | */ 24 | case ACTIVE; 25 | 26 | /** 27 | * This transaction has been terminated because of a fatal connection error. 28 | */ 29 | case TERMINATED; 30 | 31 | /** 32 | * This transaction has successfully committed. 33 | */ 34 | case COMMITTED; 35 | 36 | /** 37 | * This transaction has been rolled back. 38 | */ 39 | case ROLLED_BACK; 40 | } 41 | -------------------------------------------------------------------------------- /src/Exception/ClientException.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\Exception; 15 | 16 | use RuntimeException; 17 | use Throwable; 18 | 19 | /** 20 | * Exception when a Client Error occurs. 21 | * 22 | * @psalm-immutable 23 | * 24 | * @psalm-suppress MutableDependency 25 | */ 26 | final class ClientException extends RuntimeException 27 | { 28 | public function __construct(string $message, ?Throwable $previous = null) 29 | { 30 | parent::__construct($message, 0, $previous); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/Exception/ConnectionPoolException.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\Exception; 15 | 16 | use RuntimeException; 17 | 18 | final class ConnectionPoolException extends RuntimeException 19 | { 20 | } 21 | -------------------------------------------------------------------------------- /src/Exception/InvalidCacheArgumentException.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\Exception; 15 | 16 | use Psr\SimpleCache\InvalidArgumentException; 17 | use RuntimeException; 18 | 19 | class InvalidCacheArgumentException extends RuntimeException implements InvalidArgumentException 20 | { 21 | } 22 | -------------------------------------------------------------------------------- /src/Exception/Neo4jException.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\Exception; 15 | 16 | use Bolt\protocol\Response; 17 | use Laudis\Neo4j\Databags\Neo4jError; 18 | use RuntimeException; 19 | use Throwable; 20 | 21 | /** 22 | * Exception when a Neo4j Error occurs. 23 | * 24 | * @psalm-immutable 25 | * 26 | * @psalm-suppress MutableDependency 27 | */ 28 | final class Neo4jException extends RuntimeException 29 | { 30 | private const MESSAGE_TEMPLATE = 'Neo4j errors detected. First one with code "%s" and message "%s"'; 31 | /** @var non-empty-list */ 32 | private array $errors; 33 | 34 | /** 35 | * @param non-empty-list $errors 36 | */ 37 | public function __construct(array $errors, ?Throwable $previous = null) 38 | { 39 | $error = $errors[0]; 40 | $message = sprintf(self::MESSAGE_TEMPLATE, $error->getCode(), $error->getMessage() ?? 'NULL'); 41 | parent::__construct($message, 0, $previous); 42 | $this->errors = $errors; 43 | } 44 | 45 | /** 46 | * @pure 47 | */ 48 | public static function fromBoltResponse(Response $response): self 49 | { 50 | return new self([Neo4jError::fromBoltResponse($response)]); 51 | } 52 | 53 | /** 54 | * @return non-empty-list 55 | */ 56 | public function getErrors(): array 57 | { 58 | return $this->errors; 59 | } 60 | 61 | public function getNeo4jCode(): string 62 | { 63 | return $this->errors[0]->getCode(); 64 | } 65 | 66 | public function getNeo4jMessage(): ?string 67 | { 68 | return $this->errors[0]->getMessage(); 69 | } 70 | 71 | public function getCategory(): string 72 | { 73 | return $this->errors[0]->getCategory(); 74 | } 75 | 76 | public function getClassification(): string 77 | { 78 | return $this->errors[0]->getClassification(); 79 | } 80 | 81 | public function getTitle(): string 82 | { 83 | return $this->errors[0]->getTitle(); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/Exception/PropertyDoesNotExistException.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\Exception; 15 | 16 | use RuntimeException; 17 | 18 | /** 19 | * Exception when accessing a property which does not exist. 20 | * 21 | * @psalm-immutable 22 | * 23 | * @psalm-suppress MutableDependency 24 | */ 25 | final class PropertyDoesNotExistException extends RuntimeException 26 | { 27 | } 28 | -------------------------------------------------------------------------------- /src/Exception/RuntimeTypeException.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\Exception; 15 | 16 | use function get_debug_type; 17 | 18 | use RuntimeException; 19 | 20 | final class RuntimeTypeException extends RuntimeException 21 | { 22 | public function __construct(mixed $value, string $type) 23 | { 24 | $actualType = get_debug_type($value); 25 | $message = sprintf('Cannot cast %s to type: %s', $actualType, $type); 26 | parent::__construct($message); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Exception/UnsupportedScheme.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\Exception; 15 | 16 | use function implode; 17 | 18 | use RuntimeException; 19 | 20 | /** 21 | * Exception when a requested scheme cannot be handled by the drivers available in the client. 22 | * 23 | * @psalm-immutable 24 | * 25 | * @psalm-suppress MutableDependency 26 | */ 27 | final class UnsupportedScheme extends RuntimeException 28 | { 29 | /** 30 | * @param list $supportedSchemas 31 | */ 32 | public static function make(string $schema, array $supportedSchemas): self 33 | { 34 | return new self('Unsupported schema: '.$schema.', available schema\'s are: '.implode(',', $supportedSchemas)); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/Neo4j/RoutingTable.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\Neo4j; 15 | 16 | use function in_array; 17 | 18 | use Laudis\Neo4j\Enum\RoutingRoles; 19 | 20 | /** 21 | * Table containing possible routes to nodes in the cluster. 22 | * 23 | * @psalm-immutable 24 | */ 25 | final class RoutingTable 26 | { 27 | /** 28 | * @param iterable, role:string}> $servers 29 | */ 30 | public function __construct( 31 | private readonly iterable $servers, 32 | private readonly int $ttl, 33 | ) { 34 | } 35 | 36 | /** 37 | * Returns the time to live in seconds. 38 | */ 39 | public function getTtl(): int 40 | { 41 | return $this->ttl; 42 | } 43 | 44 | /** 45 | * Returns the routes with a given role. If no role is provided it will return all routes. 46 | * 47 | * @return list 48 | */ 49 | public function getWithRole(?RoutingRoles $role = null): array 50 | { 51 | /** @psalm-var list $tbr */ 52 | $tbr = []; 53 | foreach ($this->servers as $server) { 54 | if ($role === null || in_array($server['role'], $role->getValue(), true)) { 55 | foreach ($server['addresses'] as $address) { 56 | $tbr[] = $address; 57 | } 58 | } 59 | } 60 | 61 | return array_values(array_unique($tbr)); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/Types/Abstract3DPoint.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\Types; 15 | 16 | use Bolt\protocol\IStructure; 17 | use Bolt\protocol\v1\structures\Point3D; 18 | use Laudis\Neo4j\Contracts\BoltConvertibleInterface; 19 | use Laudis\Neo4j\Contracts\PointInterface; 20 | 21 | /** 22 | * A cartesian point in three-dimensional space. 23 | * 24 | * @see https://neo4j.com/docs/cypher-manual/current/functions/spatial/#functions-point-cartesian-3d 25 | * 26 | * @psalm-immutable 27 | * 28 | * @psalm-import-type Crs from PointInterface 29 | */ 30 | abstract class Abstract3DPoint extends AbstractPoint implements PointInterface, BoltConvertibleInterface 31 | { 32 | public function convertToBolt(): IStructure 33 | { 34 | return new Point3D($this->getSrid(), $this->getX(), $this->getY(), $this->getZ()); 35 | } 36 | 37 | public function __construct( 38 | float $x, 39 | float $y, 40 | private float $z, 41 | ) { 42 | parent::__construct($x, $y); 43 | } 44 | 45 | public function getZ(): float 46 | { 47 | return $this->z; 48 | } 49 | 50 | /** 51 | * @psalm-suppress ImplementedReturnTypeMismatch 52 | * 53 | * @return array{x: float, y: float, z: float, srid: int, crs: Crs} 54 | */ 55 | public function toArray(): array 56 | { 57 | $tbr = parent::toArray(); 58 | 59 | $tbr['z'] = $this->z; 60 | 61 | return $tbr; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/Types/AbstractPoint.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\Types; 15 | 16 | use Bolt\protocol\IStructure; 17 | use Bolt\protocol\v1\structures\Point2D; 18 | use Laudis\Neo4j\Contracts\BoltConvertibleInterface; 19 | use Laudis\Neo4j\Contracts\PointInterface; 20 | 21 | /** 22 | * A cartesian point in two-dimensional space. 23 | * 24 | * @see https://neo4j.com/docs/cypher-manual/current/functions/spatial/#functions-point-cartesian-2d 25 | * 26 | * @psalm-immutable 27 | * 28 | * @psalm-import-type Crs from PointInterface 29 | * 30 | * @extends AbstractPropertyObject 31 | */ 32 | abstract class AbstractPoint extends AbstractPropertyObject implements PointInterface, BoltConvertibleInterface 33 | { 34 | public function __construct( 35 | private readonly float $x, 36 | private readonly float $y, 37 | ) { 38 | } 39 | 40 | abstract public function getCrs(): string; 41 | 42 | abstract public function getSrid(): int; 43 | 44 | public function convertToBolt(): IStructure 45 | { 46 | return new Point2D($this->getSrid(), $this->getX(), $this->getY()); 47 | } 48 | 49 | public function getX(): float 50 | { 51 | return $this->x; 52 | } 53 | 54 | public function getY(): float 55 | { 56 | return $this->y; 57 | } 58 | 59 | public function getProperties(): CypherMap 60 | { 61 | /** @psalm-suppress InvalidReturnStatement False positive */ 62 | return new CypherMap($this); 63 | } 64 | 65 | /** 66 | * @psalm-suppress ImplementedReturnTypeMismatch False positive 67 | * 68 | * @return array{x: float, y: float, crs: Crs, srid: int} 69 | */ 70 | public function toArray(): array 71 | { 72 | return [ 73 | 'x' => $this->x, 74 | 'y' => $this->y, 75 | 'crs' => $this->getCrs(), 76 | 'srid' => $this->getSrid(), 77 | ]; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/Types/AbstractPropertyObject.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\Types; 15 | 16 | use BadMethodCallException; 17 | use Laudis\Neo4j\Contracts\HasPropertiesInterface; 18 | use Laudis\Neo4j\Formatter\SummarizedResultFormatter; 19 | 20 | use function sprintf; 21 | 22 | /** 23 | * @psalm-import-type OGMTypes from SummarizedResultFormatter 24 | * 25 | * @template PropertyTypes 26 | * @template ObjectTypes 27 | * 28 | * @extends AbstractCypherObject 29 | * 30 | * @implements HasPropertiesInterface 31 | * 32 | * @psalm-immutable 33 | */ 34 | abstract class AbstractPropertyObject extends AbstractCypherObject implements HasPropertiesInterface 35 | { 36 | public function __get($name) 37 | { 38 | /** @psalm-suppress ImpureMethodCall */ 39 | return $this->getProperties()->get($name); 40 | } 41 | 42 | public function __set($name, $value): void 43 | { 44 | throw new BadMethodCallException(sprintf('%s is immutable', static::class)); 45 | } 46 | 47 | public function __isset($name): bool 48 | { 49 | /** @psalm-suppress ImpureMethodCall */ 50 | return $this->getProperties()->offsetExists($name); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/Types/Cartesian3DPoint.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\Types; 15 | 16 | use Laudis\Neo4j\Contracts\BoltConvertibleInterface; 17 | use Laudis\Neo4j\Contracts\PointInterface; 18 | 19 | /** 20 | * A cartesian point in three dimensional space. 21 | * 22 | * @see https://neo4j.com/docs/cypher-manual/current/functions/spatial/#functions-point-cartesian-3d 23 | * 24 | * @psalm-immutable 25 | * 26 | * @psalm-import-type Crs from \Laudis\Neo4j\Contracts\PointInterface 27 | */ 28 | final class Cartesian3DPoint extends Abstract3DPoint implements PointInterface, BoltConvertibleInterface 29 | { 30 | public const SRID = 9157; 31 | public const CRS = 'cartesian-3d'; 32 | 33 | public function getSrid(): int 34 | { 35 | return self::SRID; 36 | } 37 | 38 | public function getCrs(): string 39 | { 40 | return self::CRS; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/Types/CartesianPoint.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\Types; 15 | 16 | use Laudis\Neo4j\Contracts\BoltConvertibleInterface; 17 | use Laudis\Neo4j\Contracts\PointInterface; 18 | 19 | /** 20 | * A cartesian point in two dimensional space. 21 | * 22 | * @see https://neo4j.com/docs/cypher-manual/current/functions/spatial/#functions-point-cartesian-2d 23 | * 24 | * @psalm-immutable 25 | * 26 | * @psalm-import-type Crs from \Laudis\Neo4j\Contracts\PointInterface 27 | */ 28 | final class CartesianPoint extends AbstractPoint implements PointInterface, BoltConvertibleInterface 29 | { 30 | /** @var Crs */ 31 | public const CRS = 'cartesian'; 32 | public const SRID = 7203; 33 | 34 | public function getCrs(): string 35 | { 36 | return self::CRS; 37 | } 38 | 39 | public function getSrid(): int 40 | { 41 | return self::SRID; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/Types/Date.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\Types; 15 | 16 | use Bolt\protocol\IStructure; 17 | use DateTimeImmutable; 18 | use Exception; 19 | use Laudis\Neo4j\Contracts\BoltConvertibleInterface; 20 | use UnexpectedValueException; 21 | 22 | /** 23 | * A date represented by days since unix epoch. 24 | * 25 | * @psalm-immutable 26 | * 27 | * @extends AbstractPropertyObject 28 | * 29 | * @psalm-suppress TypeDoesNotContainType 30 | */ 31 | final class Date extends AbstractPropertyObject implements BoltConvertibleInterface 32 | { 33 | public function __construct( 34 | private readonly int $days, 35 | ) { 36 | } 37 | 38 | /** 39 | * The amount of days since unix epoch. 40 | */ 41 | public function getDays(): int 42 | { 43 | return $this->days; 44 | } 45 | 46 | /** 47 | * Casts to an immutable date time. 48 | * 49 | * @throws Exception 50 | */ 51 | public function toDateTime(): DateTimeImmutable 52 | { 53 | $dateTimeImmutable = (new DateTimeImmutable('@0'))->modify(sprintf('+%s days', $this->days)); 54 | 55 | if ($dateTimeImmutable === false) { 56 | throw new UnexpectedValueException('Expected DateTimeImmutable'); 57 | } 58 | 59 | return $dateTimeImmutable; 60 | } 61 | 62 | public function getProperties(): CypherMap 63 | { 64 | return new CypherMap($this); 65 | } 66 | 67 | public function toArray(): array 68 | { 69 | return ['days' => $this->days]; 70 | } 71 | 72 | public function convertToBolt(): IStructure 73 | { 74 | return new \Bolt\protocol\v1\structures\Date($this->getDays()); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/Types/LocalTime.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\Types; 15 | 16 | use Bolt\protocol\IStructure; 17 | use Laudis\Neo4j\Contracts\BoltConvertibleInterface; 18 | 19 | /** 20 | * The time of day represented in nanoseconds. 21 | * 22 | * @psalm-immutable 23 | * 24 | * @extends AbstractPropertyObject 25 | */ 26 | final class LocalTime extends AbstractPropertyObject implements BoltConvertibleInterface 27 | { 28 | public function __construct( 29 | private readonly int $nanoseconds, 30 | ) { 31 | } 32 | 33 | /** 34 | * The nanoseconds that have passed since midnight. 35 | */ 36 | public function getNanoseconds(): int 37 | { 38 | return $this->nanoseconds; 39 | } 40 | 41 | /** 42 | * @return array{nanoseconds: int} 43 | */ 44 | public function toArray(): array 45 | { 46 | return ['nanoseconds' => $this->nanoseconds]; 47 | } 48 | 49 | public function getProperties(): CypherMap 50 | { 51 | return new CypherMap($this); 52 | } 53 | 54 | public function convertToBolt(): IStructure 55 | { 56 | return new \Bolt\protocol\v1\structures\LocalTime($this->getNanoseconds()); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/Types/Path.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\Types; 15 | 16 | /** 17 | * A Path class representing a Path in cypher. 18 | * 19 | * @psalm-immutable 20 | * 21 | * @extends AbstractPropertyObject|CypherList|CypherList, CypherList|CypherList|CypherList> 22 | */ 23 | final class Path extends AbstractPropertyObject 24 | { 25 | /** 26 | * @param CypherList $nodes 27 | * @param CypherList $relationships 28 | * @param CypherList $ids 29 | */ 30 | public function __construct( 31 | private readonly CypherList $nodes, 32 | private readonly CypherList $relationships, 33 | private readonly CypherList $ids, 34 | ) { 35 | } 36 | 37 | /** 38 | * Returns the node in the path. 39 | * 40 | * @return CypherList 41 | */ 42 | public function getNodes(): CypherList 43 | { 44 | return $this->nodes; 45 | } 46 | 47 | /** 48 | * Returns the relationships in the path. 49 | * 50 | * @return CypherList 51 | */ 52 | public function getRelationships(): CypherList 53 | { 54 | return $this->relationships; 55 | } 56 | 57 | /** 58 | * Returns the ids of the items in the path. 59 | * 60 | * @return CypherList 61 | */ 62 | public function getIds(): CypherList 63 | { 64 | return $this->ids; 65 | } 66 | 67 | /** 68 | * @return array{ids: CypherList, nodes: CypherList, relationships: CypherList} 69 | */ 70 | public function toArray(): array 71 | { 72 | return [ 73 | 'ids' => $this->ids, 74 | 'nodes' => $this->nodes, 75 | 'relationships' => $this->relationships, 76 | ]; 77 | } 78 | 79 | public function getProperties(): CypherMap 80 | { 81 | return new CypherMap($this); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/Types/Relationship.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\Types; 15 | 16 | use Laudis\Neo4j\Formatter\SummarizedResultFormatter; 17 | 18 | /** 19 | * A Relationship class representing a Relationship in cypher. 20 | * 21 | * @psalm-import-type OGMTypes from SummarizedResultFormatter 22 | * 23 | * @psalm-immutable 24 | */ 25 | final class Relationship extends UnboundRelationship 26 | { 27 | /** 28 | * @param CypherMap $properties 29 | */ 30 | public function __construct( 31 | int $id, 32 | private readonly int $startNodeId, 33 | private readonly int $endNodeId, 34 | string $type, 35 | CypherMap $properties, 36 | ?string $elementId, 37 | ) { 38 | parent::__construct($id, $type, $properties, $elementId); 39 | } 40 | 41 | /** 42 | * Returns the id of the start node. 43 | */ 44 | public function getStartNodeId(): int 45 | { 46 | return $this->startNodeId; 47 | } 48 | 49 | /** 50 | * Returns the id of the end node. 51 | */ 52 | public function getEndNodeId(): int 53 | { 54 | return $this->endNodeId; 55 | } 56 | 57 | /** 58 | * @psalm-suppress ImplementedReturnTypeMismatch False positive. 59 | * 60 | * @return array{ 61 | * id: int, 62 | * type: string, 63 | * startNodeId: int, 64 | * endNodeId: int, 65 | * properties: CypherMap 66 | * } 67 | */ 68 | public function toArray(): array 69 | { 70 | $tbr = parent::toArray(); 71 | 72 | $tbr['startNodeId'] = $this->getStartNodeId(); 73 | $tbr['endNodeId'] = $this->getEndNodeId(); 74 | 75 | return $tbr; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/Types/Time.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\Types; 15 | 16 | use Bolt\protocol\IStructure; 17 | use Laudis\Neo4j\Contracts\BoltConvertibleInterface; 18 | 19 | /** 20 | * A time object represented in seconds since the unix epoch. 21 | * 22 | * @psalm-immutable 23 | * 24 | * @extends AbstractPropertyObject 25 | */ 26 | final class Time extends AbstractPropertyObject implements BoltConvertibleInterface 27 | { 28 | public function __construct( 29 | private readonly int $nanoSeconds, 30 | private readonly int $tzOffsetSeconds, 31 | ) { 32 | } 33 | 34 | /** 35 | * @return array{nanoSeconds: int, tzOffsetSeconds: int} 36 | */ 37 | public function toArray(): array 38 | { 39 | return ['nanoSeconds' => $this->nanoSeconds, 'tzOffsetSeconds' => $this->tzOffsetSeconds]; 40 | } 41 | 42 | public function getTzOffsetSeconds(): int 43 | { 44 | return $this->tzOffsetSeconds; 45 | } 46 | 47 | public function getNanoSeconds(): int 48 | { 49 | return $this->nanoSeconds; 50 | } 51 | 52 | public function getProperties(): CypherMap 53 | { 54 | return new CypherMap($this); 55 | } 56 | 57 | public function convertToBolt(): IStructure 58 | { 59 | return new \Bolt\protocol\v1\structures\Time($this->getNanoSeconds(), $this->getTzOffsetSeconds()); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/Types/WGS843DPoint.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\Types; 15 | 16 | use Laudis\Neo4j\Contracts\BoltConvertibleInterface; 17 | use Laudis\Neo4j\Contracts\PointInterface; 18 | 19 | /** 20 | * A WGS84 Point in three-dimensional space. 21 | * 22 | * @see https://neo4j.com/docs/cypher-manual/current/functions/spatial/#functions-point-wgs84-3d 23 | * 24 | * @psalm-immutable 25 | * 26 | * @psalm-import-type Crs from PointInterface 27 | */ 28 | final class WGS843DPoint extends Abstract3DPoint implements PointInterface, BoltConvertibleInterface 29 | { 30 | public const SRID = 4979; 31 | public const CRS = 'wgs-84-3d'; 32 | 33 | public function getSrid(): int 34 | { 35 | return self::SRID; 36 | } 37 | 38 | public function getLongitude(): float 39 | { 40 | return $this->getX(); 41 | } 42 | 43 | public function getLatitude(): float 44 | { 45 | return $this->getY(); 46 | } 47 | 48 | public function getHeight(): float 49 | { 50 | return $this->getZ(); 51 | } 52 | 53 | public function getCrs(): string 54 | { 55 | return self::CRS; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/Types/WGS84Point.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\Types; 15 | 16 | use Laudis\Neo4j\Contracts\BoltConvertibleInterface; 17 | use Laudis\Neo4j\Contracts\PointInterface; 18 | 19 | /** 20 | * A WGS84 Point in two dimensional space. 21 | * 22 | * @psalm-immutable 23 | * 24 | * @see https://neo4j.com/docs/cypher-manual/current/functions/spatial/#functions-point-wgs84-2d 25 | * 26 | * @psalm-import-type Crs from \Laudis\Neo4j\Contracts\PointInterface 27 | */ 28 | final class WGS84Point extends AbstractPoint implements PointInterface, BoltConvertibleInterface 29 | { 30 | public const SRID = 4326; 31 | public const CRS = 'wgs-84'; 32 | 33 | public function getSrid(): int 34 | { 35 | return self::SRID; 36 | } 37 | 38 | public function getCrs(): string 39 | { 40 | return self::CRS; 41 | } 42 | 43 | /** 44 | * A numeric expression that represents the longitude/x value in decimal degrees. 45 | */ 46 | public function getLongitude(): float 47 | { 48 | return $this->getX(); 49 | } 50 | 51 | /** 52 | * A numeric expression that represents the latitude/y value in decimal degrees. 53 | */ 54 | public function getLatitude(): float 55 | { 56 | return $this->getY(); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /testkit-backend/blacklist.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | return [ 15 | 'neo4j' => [ 16 | 'datatypes' => [ 17 | 'TestDataTypes' => [ 18 | 'test_should_echo_very_long_map' => 'Work in progress on testkit frontend', 19 | ], 20 | ], 21 | 'sessionrun' => [ 22 | 'TestSessionRun' => [ 23 | 'test_autocommit_transactions_should_support_metadata' => 'Meta data isn\'t supported yet', 24 | 'test_autocommit_transactions_should_support_timeout' => 'Waiting on bookmarks isn\'t supported yet', 25 | ], 26 | ], 27 | 'test_direct_driver' => [ 28 | 'TestDirectDriver' => [ 29 | 'test_custom_resolver' => 'No custom resolver implemented', 30 | 'test_fail_nicely_when_using_http_port' => 'Not implemented yet', 31 | ], 32 | ], 33 | 'test_summary' => [ 34 | 'TestDirectDriver' => [ 35 | 'test_agent_string' => 'This is not an official driver yet', 36 | ], 37 | ], 38 | 'txrun' => [ 39 | 'TestTxRun' => [ 40 | 'test_should_fail_to_run_query_for_invalid_bookmark' => 'Waiting on bookmarks isn\'t supported yet', 41 | ], 42 | ], 43 | 'txfuncrun' => [ 44 | 'TestTxFuncRun' => [ 45 | 'test_iteration_nested' => 'Buffers not supported yet', 46 | 'test_updates_last_bookmark_on_commit' => 'Waiting on bookmarks isn\'t supported yet', 47 | ], 48 | ], 49 | ], 50 | ]; 51 | -------------------------------------------------------------------------------- /testkit-backend/index.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | require_once __DIR__.'/../vendor/autoload.php'; 15 | 16 | use Laudis\Neo4j\TestkitBackend\Backend; 17 | 18 | $backend = Backend::boot(); 19 | while (true) { 20 | $backend->handle(); 21 | } 22 | -------------------------------------------------------------------------------- /testkit-backend/register.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | use Laudis\Neo4j\TestkitBackend\Handlers\GetFeatures; 15 | use Laudis\Neo4j\TestkitBackend\Handlers\StartTest; 16 | use Laudis\Neo4j\TestkitBackend\MainRepository; 17 | use Monolog\Handler\StreamHandler; 18 | use Monolog\Logger; 19 | use Psr\Log\LoggerInterface; 20 | 21 | return [ 22 | LoggerInterface::class => static function () { 23 | $logger = new Logger('testkit-backend'); 24 | $logger->pushHandler(new StreamHandler('php://stdout', Logger::DEBUG)); 25 | 26 | return $logger; 27 | }, 28 | 29 | GetFeatures::class => static function () { 30 | $featuresConfig = require __DIR__.'/features.php'; 31 | 32 | return new GetFeatures($featuresConfig); 33 | }, 34 | 35 | StartTest::class => static function () { 36 | $acceptedTests = require __DIR__.'/blacklist.php'; 37 | 38 | return new StartTest($acceptedTests); 39 | }, 40 | 41 | MainRepository::class => static function () { 42 | return new MainRepository( 43 | [], 44 | [], 45 | [], 46 | [], 47 | ); 48 | }, 49 | ]; 50 | -------------------------------------------------------------------------------- /testkit-backend/src/Contracts/RequestHandlerInterface.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\TestkitBackend\Contracts; 15 | 16 | /** 17 | * @template T 18 | */ 19 | interface RequestHandlerInterface 20 | { 21 | /** 22 | * @param T $request 23 | */ 24 | public function handle($request): TestkitResponseInterface; 25 | } 26 | -------------------------------------------------------------------------------- /testkit-backend/src/Contracts/TestkitResponseInterface.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\TestkitBackend\Contracts; 15 | 16 | use JsonSerializable; 17 | 18 | interface TestkitResponseInterface extends JsonSerializable 19 | { 20 | /** 21 | * @return array{name:string, data?:iterable} 22 | */ 23 | public function jsonSerialize(): array; 24 | } 25 | -------------------------------------------------------------------------------- /testkit-backend/src/Handlers/CheckMultiDBSupport.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\TestkitBackend\Handlers; 15 | 16 | use Exception; 17 | use Laudis\Neo4j\Databags\SessionConfiguration; 18 | use Laudis\Neo4j\TestkitBackend\Contracts\RequestHandlerInterface; 19 | use Laudis\Neo4j\TestkitBackend\Contracts\TestkitResponseInterface; 20 | use Laudis\Neo4j\TestkitBackend\MainRepository; 21 | use Laudis\Neo4j\TestkitBackend\Requests\CheckMultiDBSupportRequest; 22 | use Laudis\Neo4j\TestkitBackend\Responses\MultiDBSupportResponse; 23 | 24 | /** 25 | * @implements RequestHandlerInterface 26 | */ 27 | final class CheckMultiDBSupport implements RequestHandlerInterface 28 | { 29 | private MainRepository $repository; 30 | 31 | public function __construct(MainRepository $repository) 32 | { 33 | $this->repository = $repository; 34 | } 35 | 36 | /** 37 | * @param CheckMultiDBSupportRequest $request 38 | */ 39 | public function handle($request): TestkitResponseInterface 40 | { 41 | $driver = $this->repository->getDriver($request->getDriverId()); 42 | 43 | try { 44 | $session = $driver->createSession(SessionConfiguration::default()->withDatabase('system')); 45 | $session->run('SHOW databases'); 46 | } catch (Exception $e) { 47 | return new MultiDBSupportResponse($request->getDriverId(), false); 48 | } 49 | 50 | return new MultiDBSupportResponse($request->getDriverId(), true); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /testkit-backend/src/Handlers/DomainNameResolutionCompleted.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\TestkitBackend\Handlers; 15 | 16 | use Laudis\Neo4j\TestkitBackend\Contracts\RequestHandlerInterface; 17 | use Laudis\Neo4j\TestkitBackend\Contracts\TestkitResponseInterface; 18 | use Laudis\Neo4j\TestkitBackend\Requests\DomainNameResolutionCompletedRequest; 19 | use Laudis\Neo4j\TestkitBackend\Responses\BackendErrorResponse; 20 | 21 | /** 22 | * @implements RequestHandlerInterface 23 | */ 24 | final class DomainNameResolutionCompleted implements RequestHandlerInterface 25 | { 26 | /** 27 | * @param DomainNameResolutionCompletedRequest $request 28 | */ 29 | public function handle($request): TestkitResponseInterface 30 | { 31 | return new BackendErrorResponse('Domain name resolution not implemented yet'); // TODO 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /testkit-backend/src/Handlers/DriverClose.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\TestkitBackend\Handlers; 15 | 16 | use Laudis\Neo4j\TestkitBackend\Contracts\RequestHandlerInterface; 17 | use Laudis\Neo4j\TestkitBackend\MainRepository; 18 | use Laudis\Neo4j\TestkitBackend\Requests\DriverCloseRequest; 19 | use Laudis\Neo4j\TestkitBackend\Responses\DriverResponse; 20 | 21 | /** 22 | * @implements RequestHandlerInterface 23 | */ 24 | final class DriverClose implements RequestHandlerInterface 25 | { 26 | private MainRepository $repository; 27 | 28 | public function __construct(MainRepository $repository) 29 | { 30 | $this->repository = $repository; 31 | } 32 | 33 | /** 34 | * @param DriverCloseRequest $request 35 | */ 36 | public function handle($request): DriverResponse 37 | { 38 | $this->repository->removeDriver($request->getDriverId()); 39 | 40 | return new DriverResponse($request->getDriverId()); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /testkit-backend/src/Handlers/ForcedRoutingTableUpdate.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\TestkitBackend\Handlers; 15 | 16 | use Exception; 17 | use Laudis\Neo4j\Contracts\ConnectionPoolInterface; 18 | use Laudis\Neo4j\Neo4j\Neo4jConnectionPool; 19 | use Laudis\Neo4j\Neo4j\Neo4jDriver; 20 | use Laudis\Neo4j\TestkitBackend\Contracts\RequestHandlerInterface; 21 | use Laudis\Neo4j\TestkitBackend\Contracts\TestkitResponseInterface; 22 | use Laudis\Neo4j\TestkitBackend\MainRepository; 23 | use Laudis\Neo4j\TestkitBackend\Requests\ForcedRoutingTableUpdateRequest; 24 | use Laudis\Neo4j\TestkitBackend\Responses\DriverResponse; 25 | use ReflectionClass; 26 | use ReflectionException; 27 | 28 | /** 29 | * @implements RequestHandlerInterface 30 | */ 31 | final class ForcedRoutingTableUpdate implements RequestHandlerInterface 32 | { 33 | private MainRepository $repository; 34 | 35 | public function __construct(MainRepository $repository) 36 | { 37 | $this->repository = $repository; 38 | } 39 | 40 | /** 41 | * @param ForcedRoutingTableUpdateRequest $request 42 | * 43 | * @throws ReflectionException 44 | * @throws Exception 45 | */ 46 | public function handle($request): TestkitResponseInterface 47 | { 48 | $driver = $this->repository->getDriver($request->getDriverId()); 49 | 50 | if ($driver instanceof Neo4jDriver) { 51 | $poolProperty = (new ReflectionClass(Neo4jDriver::class))->getProperty('pool'); 52 | $poolProperty->setAccessible(true); 53 | /** @var ConnectionPoolInterface $pool */ 54 | $pool = $poolProperty->getValue($driver); 55 | 56 | $tableProperty = (new ReflectionClass(Neo4jConnectionPool::class))->getProperty('table'); 57 | $tableProperty->setAccessible(true); 58 | $tableProperty->setValue($pool, null); 59 | } 60 | 61 | $driver->createSession()->run('RETURN 1 AS x'); 62 | 63 | return new DriverResponse($request->getDriverId()); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /testkit-backend/src/Handlers/GetFeatures.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\TestkitBackend\Handlers; 15 | 16 | use Laudis\Neo4j\TestkitBackend\Contracts\RequestHandlerInterface; 17 | use Laudis\Neo4j\TestkitBackend\Requests\GetFeaturesRequest; 18 | use Laudis\Neo4j\TestkitBackend\Responses\FeatureListResponse; 19 | 20 | /** 21 | * @implements RequestHandlerInterface 22 | */ 23 | final class GetFeatures implements RequestHandlerInterface 24 | { 25 | /** @var iterable */ 26 | private iterable $featuresConfig; 27 | 28 | /** 29 | * @param iterable $featuresConfig 30 | */ 31 | public function __construct(iterable $featuresConfig) 32 | { 33 | $this->featuresConfig = $featuresConfig; 34 | } 35 | 36 | /** 37 | * @param GetFeaturesRequest $request 38 | */ 39 | public function handle($request): FeatureListResponse 40 | { 41 | $features = []; 42 | foreach ($this->featuresConfig as $feature => $available) { 43 | if ($available) { 44 | $features[] = $feature; 45 | } 46 | } 47 | 48 | return new FeatureListResponse($features); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /testkit-backend/src/Handlers/NewDriver.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\TestkitBackend\Handlers; 15 | 16 | use Laudis\Neo4j\Authentication\Authenticate; 17 | use Laudis\Neo4j\Databags\DriverConfiguration; 18 | use Laudis\Neo4j\DriverFactory; 19 | use Laudis\Neo4j\TestkitBackend\Contracts\RequestHandlerInterface; 20 | use Laudis\Neo4j\TestkitBackend\MainRepository; 21 | use Laudis\Neo4j\TestkitBackend\Requests\NewDriverRequest; 22 | use Laudis\Neo4j\TestkitBackend\Responses\DriverResponse; 23 | use Symfony\Component\Uid\Uuid; 24 | 25 | /** 26 | * @implements RequestHandlerInterface 27 | */ 28 | final class NewDriver implements RequestHandlerInterface 29 | { 30 | private MainRepository $repository; 31 | 32 | public function __construct(MainRepository $repository) 33 | { 34 | $this->repository = $repository; 35 | } 36 | 37 | /** 38 | * @param NewDriverRequest $request 39 | */ 40 | public function handle($request): DriverResponse 41 | { 42 | $user = $request->authToken->principal; 43 | $pass = $request->authToken->credentials; 44 | 45 | $ua = $request->userAgent; 46 | $timeout = $request->connectionTimeoutMs; 47 | $config = DriverConfiguration::default() 48 | ->withAcquireConnectionTimeout($timeout); 49 | 50 | if ($ua) { 51 | $config = $config->withUserAgent($ua); 52 | } 53 | 54 | $authenticate = Authenticate::basic($user, $pass); 55 | $driver = DriverFactory::create($request->uri, $config, $authenticate); 56 | 57 | $id = Uuid::v4(); 58 | $this->repository->addDriver($id, $driver); 59 | 60 | return new DriverResponse($id); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /testkit-backend/src/Handlers/NewSession.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\TestkitBackend\Handlers; 15 | 16 | use Laudis\Neo4j\Databags\Bookmark; 17 | use Laudis\Neo4j\Databags\SessionConfiguration; 18 | use Laudis\Neo4j\Enum\AccessMode; 19 | use Laudis\Neo4j\TestkitBackend\Contracts\RequestHandlerInterface; 20 | use Laudis\Neo4j\TestkitBackend\MainRepository; 21 | use Laudis\Neo4j\TestkitBackend\Requests\NewSessionRequest; 22 | use Laudis\Neo4j\TestkitBackend\Responses\SessionResponse; 23 | use Symfony\Component\Uid\Uuid; 24 | 25 | /** 26 | * @implements RequestHandlerInterface 27 | */ 28 | final class NewSession implements RequestHandlerInterface 29 | { 30 | private MainRepository $repository; 31 | 32 | public function __construct(MainRepository $repository) 33 | { 34 | $this->repository = $repository; 35 | } 36 | 37 | /** 38 | * @param NewSessionRequest $request 39 | */ 40 | public function handle($request): SessionResponse 41 | { 42 | $driver = $this->repository->getDriver($request->driverId); 43 | 44 | $config = SessionConfiguration::default() 45 | ->withAccessMode($request->accessMode === 'r' ? AccessMode::READ() : AccessMode::WRITE()); 46 | 47 | if ($request->bookmarks !== null) { 48 | $config = $config->withBookmarks([new Bookmark($request->bookmarks)]); 49 | } 50 | 51 | if ($request->database !== null) { 52 | $config = $config->withDatabase($request->database); 53 | } 54 | 55 | $config = $config->withFetchSize($request->fetchSize ?? 1); 56 | 57 | $session = $driver->createSession($config); 58 | $id = Uuid::v4(); 59 | $this->repository->addSession($id, $session); 60 | 61 | return new SessionResponse($id); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /testkit-backend/src/Handlers/ResolverResolutionCompleted.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\TestkitBackend\Handlers; 15 | 16 | use Laudis\Neo4j\TestkitBackend\Contracts\RequestHandlerInterface; 17 | use Laudis\Neo4j\TestkitBackend\Contracts\TestkitResponseInterface; 18 | use Laudis\Neo4j\TestkitBackend\Requests\ResolverResolutionCompletedRequest; 19 | use Laudis\Neo4j\TestkitBackend\Responses\BackendErrorResponse; 20 | 21 | /** 22 | * @implements RequestHandlerInterface 23 | */ 24 | final class ResolverResolutionCompleted implements RequestHandlerInterface 25 | { 26 | /** 27 | * @param ResolverResolutionCompletedRequest $request 28 | */ 29 | public function handle($request): TestkitResponseInterface 30 | { 31 | return new BackendErrorResponse('Resolver resolution not implemented yet'); // TODO 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /testkit-backend/src/Handlers/ResultConsume.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\TestkitBackend\Handlers; 15 | 16 | use Laudis\Neo4j\TestkitBackend\Contracts\RequestHandlerInterface; 17 | use Laudis\Neo4j\TestkitBackend\Contracts\TestkitResponseInterface; 18 | use Laudis\Neo4j\TestkitBackend\MainRepository; 19 | use Laudis\Neo4j\TestkitBackend\Requests\ResultConsumeRequest; 20 | use Laudis\Neo4j\TestkitBackend\Responses\SummaryResponse; 21 | 22 | /** 23 | * @implements RequestHandlerInterface 24 | */ 25 | final class ResultConsume implements RequestHandlerInterface 26 | { 27 | private MainRepository $repository; 28 | 29 | public function __construct(MainRepository $repository) 30 | { 31 | $this->repository = $repository; 32 | } 33 | 34 | /** 35 | * @param ResultConsumeRequest $request 36 | */ 37 | public function handle($request): TestkitResponseInterface 38 | { 39 | $result = $this->repository->getRecords($request->getResultId()); 40 | 41 | if ($result instanceof TestkitResponseInterface) { 42 | return $result; 43 | } 44 | 45 | return new SummaryResponse($result); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /testkit-backend/src/Handlers/ResultSingle.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\TestkitBackend\Handlers; 15 | 16 | use Laudis\Neo4j\TestkitBackend\Contracts\RequestHandlerInterface; 17 | use Laudis\Neo4j\TestkitBackend\Contracts\TestkitResponseInterface; 18 | use Laudis\Neo4j\TestkitBackend\MainRepository; 19 | use Laudis\Neo4j\TestkitBackend\Requests\ResultSingleRequest; 20 | use Laudis\Neo4j\TestkitBackend\Responses\BackendErrorResponse; 21 | use Laudis\Neo4j\TestkitBackend\Responses\RecordResponse; 22 | 23 | /** 24 | * Request to expect and return exactly one record in the result stream. 25 | * 26 | * Backend should respond with a Record if exactly one record was found. 27 | * If more or fewer records are left in the result stream, or if any other 28 | * error occurs while retrieving the records, an Error response should be 29 | * returned. 30 | * 31 | * @implements RequestHandlerInterface 32 | */ 33 | final class ResultSingle implements RequestHandlerInterface 34 | { 35 | private function __construct( 36 | private readonly MainRepository $repository, 37 | ) { 38 | } 39 | 40 | public function handle($request): TestkitResponseInterface 41 | { 42 | $record = $this->repository->getRecords($request->getResultId()); 43 | if ($record instanceof TestkitResponseInterface) { 44 | return new BackendErrorResponse('Something went wrong with the result handling'); 45 | } 46 | 47 | $count = $record->count(); 48 | if ($count !== 1) { 49 | return new BackendErrorResponse(sprintf('Found exactly %s result rows, but expected just one.', $count)); 50 | } 51 | 52 | $values = []; 53 | foreach ($record->getAsCypherMap(0) as $value) { 54 | $values[] = $value; 55 | } 56 | 57 | return new RecordResponse($values); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /testkit-backend/src/Handlers/RetryableNegative.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\TestkitBackend\Handlers; 15 | 16 | use Laudis\Neo4j\TestkitBackend\Contracts\RequestHandlerInterface; 17 | use Laudis\Neo4j\TestkitBackend\Contracts\TestkitResponseInterface; 18 | use Laudis\Neo4j\TestkitBackend\Requests\RetryableNegativeRequest; 19 | use Laudis\Neo4j\TestkitBackend\Responses\BackendErrorResponse; 20 | 21 | /** 22 | * @implements RequestHandlerInterface 23 | */ 24 | final class RetryableNegative implements RequestHandlerInterface 25 | { 26 | /** 27 | * @param RetryableNegativeRequest $request 28 | */ 29 | public function handle($request): TestkitResponseInterface 30 | { 31 | return new BackendErrorResponse('Retryable negative not implemented yet'); // TODO 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /testkit-backend/src/Handlers/RetryablePositive.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\TestkitBackend\Handlers; 15 | 16 | use Laudis\Neo4j\TestkitBackend\Contracts\RequestHandlerInterface; 17 | use Laudis\Neo4j\TestkitBackend\Contracts\TestkitResponseInterface; 18 | use Laudis\Neo4j\TestkitBackend\Requests\RetryablePositiveRequest; 19 | use Laudis\Neo4j\TestkitBackend\Responses\RetryableDoneResponse; 20 | 21 | /** 22 | * @implements RequestHandlerInterface 23 | */ 24 | final class RetryablePositive implements RequestHandlerInterface 25 | { 26 | /** 27 | * @param RetryablePositiveRequest $request 28 | */ 29 | public function handle($request): TestkitResponseInterface 30 | { 31 | return new RetryableDoneResponse(); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /testkit-backend/src/Handlers/SessionClose.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\TestkitBackend\Handlers; 15 | 16 | use Laudis\Neo4j\TestkitBackend\Contracts\RequestHandlerInterface; 17 | use Laudis\Neo4j\TestkitBackend\MainRepository; 18 | use Laudis\Neo4j\TestkitBackend\Requests\SessionCloseRequest; 19 | use Laudis\Neo4j\TestkitBackend\Responses\SessionResponse; 20 | 21 | /** 22 | * @implements RequestHandlerInterface 23 | */ 24 | final class SessionClose implements RequestHandlerInterface 25 | { 26 | private MainRepository $repository; 27 | 28 | public function __construct(MainRepository $repository) 29 | { 30 | $this->repository = $repository; 31 | } 32 | 33 | /** 34 | * @param SessionCloseRequest $request 35 | */ 36 | public function handle($request): SessionResponse 37 | { 38 | $this->repository->removeSession($request->getSessionId()); 39 | 40 | return new SessionResponse($request->getSessionId()); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /testkit-backend/src/Handlers/SessionLastBookmarks.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\TestkitBackend\Handlers; 15 | 16 | use Laudis\Neo4j\TestkitBackend\Contracts\RequestHandlerInterface; 17 | use Laudis\Neo4j\TestkitBackend\Contracts\TestkitResponseInterface; 18 | use Laudis\Neo4j\TestkitBackend\MainRepository; 19 | use Laudis\Neo4j\TestkitBackend\Requests\SessionLastBookmarksRequest; 20 | use Laudis\Neo4j\TestkitBackend\Responses\BookmarksResponse; 21 | use Symfony\Component\Uid\Uuid; 22 | 23 | /** 24 | * @implements AbstractRunner 25 | */ 26 | final class SessionLastBookmarks implements RequestHandlerInterface 27 | { 28 | public function __construct( 29 | private readonly MainRepository $repository, 30 | ) { 31 | } 32 | 33 | /** 34 | * @param SessionLastBookmarksRequest $request 35 | * 36 | * @return TestkitResponseInterface 37 | */ 38 | public function handle($request): TestkitResponseInterface 39 | { 40 | $session = $this->repository->getSession($request->getSessionId()); 41 | 42 | $bookmarks = $session->getLastBookmark()->values(); 43 | 44 | return new BookmarksResponse($bookmarks); 45 | } 46 | 47 | protected function getId($request): Uuid 48 | { 49 | return $request->getSessionId(); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /testkit-backend/src/Handlers/SessionRun.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\TestkitBackend\Handlers; 15 | 16 | use Laudis\Neo4j\Contracts\SessionInterface; 17 | use Laudis\Neo4j\TestkitBackend\Requests\SessionRunRequest; 18 | use Symfony\Component\Uid\Uuid; 19 | 20 | /** 21 | * @extends AbstractRunner 22 | */ 23 | final class SessionRun extends AbstractRunner 24 | { 25 | protected function getRunner($request): SessionInterface 26 | { 27 | return $this->repository->getSession($request->getSessionId()); 28 | } 29 | 30 | protected function getId($request): Uuid 31 | { 32 | return $request->getSessionId(); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /testkit-backend/src/Handlers/SessionWriteTransaction.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\TestkitBackend\Handlers; 15 | 16 | use Laudis\Neo4j\TestkitBackend\Contracts\RequestHandlerInterface; 17 | use Laudis\Neo4j\TestkitBackend\Contracts\TestkitResponseInterface; 18 | use Laudis\Neo4j\TestkitBackend\MainRepository; 19 | use Laudis\Neo4j\TestkitBackend\Requests\SessionWriteTransactionRequest; 20 | use Laudis\Neo4j\TestkitBackend\Responses\RetryableTryResponse; 21 | use Symfony\Component\Uid\Uuid; 22 | 23 | /** 24 | * @implements RequestHandlerInterface 25 | */ 26 | final class SessionWriteTransaction implements RequestHandlerInterface 27 | { 28 | private MainRepository $repository; 29 | 30 | public function __construct(MainRepository $repository) 31 | { 32 | $this->repository = $repository; 33 | } 34 | 35 | /** 36 | * @param SessionWriteTransactionRequest $request 37 | */ 38 | public function handle($request): TestkitResponseInterface 39 | { 40 | $session = $this->repository->getSession($request->getSessionId()); 41 | 42 | $id = Uuid::v4(); 43 | 44 | $this->repository->addTransaction($id, $session); 45 | $this->repository->bindTransactionToSession($request->getSessionId(), $id); 46 | 47 | return new RetryableTryResponse($id); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /testkit-backend/src/Handlers/StartTest.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\TestkitBackend\Handlers; 15 | 16 | use function is_string; 17 | 18 | use Laudis\Neo4j\TestkitBackend\Contracts\RequestHandlerInterface; 19 | use Laudis\Neo4j\TestkitBackend\Contracts\TestkitResponseInterface; 20 | use Laudis\Neo4j\TestkitBackend\Requests\StartTestRequest; 21 | use Laudis\Neo4j\TestkitBackend\Responses\RunTestResponse; 22 | use Laudis\Neo4j\TestkitBackend\Responses\SkipTestResponse; 23 | 24 | /** 25 | * @implements RequestHandlerInterface 26 | */ 27 | final class StartTest implements RequestHandlerInterface 28 | { 29 | /** @var array */ 30 | private array $acceptedTests; 31 | 32 | /** 33 | * @param array $acceptedTests 34 | */ 35 | public function __construct(array $acceptedTests) 36 | { 37 | $this->acceptedTests = $acceptedTests; 38 | } 39 | 40 | /** 41 | * @param StartTestRequest $request 42 | */ 43 | public function handle($request): TestkitResponseInterface 44 | { 45 | $section = $this->acceptedTests; 46 | foreach (explode('.', $request->getTestName()) as $key) { 47 | if (array_key_exists($key, $section)) { 48 | if ($section[$key] === false) { 49 | return new SkipTestResponse('Test disabled in backend'); 50 | } 51 | if (is_string($section[$key])) { 52 | return new SkipTestResponse($section[$key]); 53 | } 54 | /** @var array $section */ 55 | $section = $section[$key]; 56 | } else { 57 | break; 58 | } 59 | } 60 | 61 | return new RunTestResponse(); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /testkit-backend/src/Handlers/TransactionCommit.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\TestkitBackend\Handlers; 15 | 16 | use Laudis\Neo4j\Contracts\UnmanagedTransactionInterface; 17 | use Laudis\Neo4j\Exception\Neo4jException; 18 | use Laudis\Neo4j\TestkitBackend\Contracts\RequestHandlerInterface; 19 | use Laudis\Neo4j\TestkitBackend\Contracts\TestkitResponseInterface; 20 | use Laudis\Neo4j\TestkitBackend\MainRepository; 21 | use Laudis\Neo4j\TestkitBackend\Requests\TransactionCommitRequest; 22 | use Laudis\Neo4j\TestkitBackend\Responses\BackendErrorResponse; 23 | use Laudis\Neo4j\TestkitBackend\Responses\DriverErrorResponse; 24 | use Laudis\Neo4j\TestkitBackend\Responses\TransactionResponse; 25 | 26 | /** 27 | * @implements RequestHandlerInterface 28 | */ 29 | final class TransactionCommit implements RequestHandlerInterface 30 | { 31 | private MainRepository $repository; 32 | 33 | public function __construct(MainRepository $repository) 34 | { 35 | $this->repository = $repository; 36 | } 37 | 38 | /** 39 | * @param TransactionCommitRequest $request 40 | */ 41 | public function handle($request): TestkitResponseInterface 42 | { 43 | $tsx = $this->repository->getTransaction($request->getTxId()); 44 | 45 | if (!$tsx instanceof UnmanagedTransactionInterface) { 46 | return new BackendErrorResponse('Transaction not found'); 47 | } 48 | 49 | try { 50 | $tsx->commit(); 51 | } catch (Neo4jException $e) { 52 | return new DriverErrorResponse($request->getTxId(), $e); 53 | } 54 | 55 | return new TransactionResponse($request->getTxId()); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /testkit-backend/src/Handlers/TransactionRollback.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\TestkitBackend\Handlers; 15 | 16 | use Laudis\Neo4j\Contracts\TransactionInterface; 17 | use Laudis\Neo4j\Exception\Neo4jException; 18 | use Laudis\Neo4j\TestkitBackend\Contracts\RequestHandlerInterface; 19 | use Laudis\Neo4j\TestkitBackend\Contracts\TestkitResponseInterface; 20 | use Laudis\Neo4j\TestkitBackend\MainRepository; 21 | use Laudis\Neo4j\TestkitBackend\Requests\TransactionRollbackRequest; 22 | use Laudis\Neo4j\TestkitBackend\Responses\BackendErrorResponse; 23 | use Laudis\Neo4j\TestkitBackend\Responses\DriverErrorResponse; 24 | use Laudis\Neo4j\TestkitBackend\Responses\TransactionResponse; 25 | 26 | /** 27 | * @implements RequestHandlerInterface 28 | */ 29 | final class TransactionRollback implements RequestHandlerInterface 30 | { 31 | private MainRepository $repository; 32 | 33 | public function __construct(MainRepository $repository) 34 | { 35 | $this->repository = $repository; 36 | } 37 | 38 | /** 39 | * @param TransactionRollbackRequest $request 40 | */ 41 | public function handle($request): TestkitResponseInterface 42 | { 43 | $tsx = $this->repository->getTransaction($request->getTxId()); 44 | 45 | if ($tsx === null || !$tsx instanceof TransactionInterface) { 46 | return new BackendErrorResponse('Transaction not found'); 47 | } 48 | 49 | try { 50 | $tsx->rollback(); 51 | } catch (Neo4jException $e) { 52 | return new DriverErrorResponse($request->getTxId(), $e); 53 | } 54 | 55 | return new TransactionResponse($request->getTxId()); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /testkit-backend/src/Handlers/TransactionRun.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\TestkitBackend\Handlers; 15 | 16 | use Laudis\Neo4j\Contracts\TransactionInterface; 17 | use Laudis\Neo4j\TestkitBackend\Requests\TransactionRunRequest; 18 | use Symfony\Component\Uid\Uuid; 19 | 20 | /** 21 | * @extends AbstractRunner 22 | */ 23 | final class TransactionRun extends AbstractRunner 24 | { 25 | /** 26 | * @param TransactionRunRequest $request 27 | */ 28 | protected function getRunner($request): TransactionInterface 29 | { 30 | return $this->repository->getTransaction($request->getTxId()); 31 | } 32 | 33 | protected function getId($request): Uuid 34 | { 35 | return $request->getTxId(); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /testkit-backend/src/Handlers/VerifyConnectivity.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\TestkitBackend\Handlers; 15 | 16 | use Laudis\Neo4j\TestkitBackend\Contracts\RequestHandlerInterface; 17 | use Laudis\Neo4j\TestkitBackend\Contracts\TestkitResponseInterface; 18 | use Laudis\Neo4j\TestkitBackend\MainRepository; 19 | use Laudis\Neo4j\TestkitBackend\Requests\VerifyConnectivityRequest; 20 | use Laudis\Neo4j\TestkitBackend\Responses\DriverResponse; 21 | 22 | /** 23 | * @implements RequestHandlerInterface 24 | */ 25 | final class VerifyConnectivity implements RequestHandlerInterface 26 | { 27 | private MainRepository $repository; 28 | 29 | public function __construct(MainRepository $repository) 30 | { 31 | $this->repository = $repository; 32 | } 33 | 34 | /** 35 | * @param VerifyConnectivityRequest $request 36 | */ 37 | public function handle($request): TestkitResponseInterface 38 | { 39 | $driver = $this->repository->getDriver($request->getDriverId()); 40 | 41 | $driver->createSession()->run('RETURN 2 as x'); 42 | 43 | return new DriverResponse($request->getDriverId()); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /testkit-backend/src/Request.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\TestkitBackend; 15 | 16 | final class Request 17 | { 18 | public function __construct(string $name, array $data) 19 | { 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /testkit-backend/src/Requests/AuthorizationTokenRequest.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\TestkitBackend\Requests; 15 | 16 | final class AuthorizationTokenRequest 17 | { 18 | public function __construct( 19 | public readonly string $scheme, 20 | public readonly string $realm, 21 | public readonly string $principal, 22 | public readonly string $credentials, 23 | ) { 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /testkit-backend/src/Requests/CheckMultiDBSupportRequest.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\TestkitBackend\Requests; 15 | 16 | use Symfony\Component\Uid\Uuid; 17 | 18 | final class CheckMultiDBSupportRequest 19 | { 20 | private Uuid $driverId; 21 | 22 | public function __construct(Uuid $driverId) 23 | { 24 | $this->driverId = $driverId; 25 | } 26 | 27 | public function getDriverId(): Uuid 28 | { 29 | return $this->driverId; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /testkit-backend/src/Requests/DomainNameResolutionCompletedRequest.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\TestkitBackend\Requests; 15 | 16 | use Symfony\Component\Uid\Uuid; 17 | 18 | final class DomainNameResolutionCompletedRequest 19 | { 20 | private Uuid $requestId; 21 | /** @var iterable */ 22 | private iterable $addresses; 23 | 24 | /** 25 | * @param iterable $addresses 26 | */ 27 | public function __construct(Uuid $requestId, iterable $addresses) 28 | { 29 | $this->requestId = $requestId; 30 | $this->addresses = $addresses; 31 | } 32 | 33 | public function getRequestId(): Uuid 34 | { 35 | return $this->requestId; 36 | } 37 | 38 | /** 39 | * @return iterable 40 | */ 41 | public function getAddresses(): iterable 42 | { 43 | return $this->addresses; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /testkit-backend/src/Requests/DriverCloseRequest.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\TestkitBackend\Requests; 15 | 16 | use Symfony\Component\Uid\Uuid; 17 | 18 | final class DriverCloseRequest 19 | { 20 | private Uuid $driverId; 21 | 22 | public function __construct(Uuid $driverId) 23 | { 24 | $this->driverId = $driverId; 25 | } 26 | 27 | public function getDriverId(): Uuid 28 | { 29 | return $this->driverId; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /testkit-backend/src/Requests/ForcedRoutingTableUpdateRequest.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\TestkitBackend\Requests; 15 | 16 | use Symfony\Component\Uid\Uuid; 17 | 18 | final class ForcedRoutingTableUpdateRequest 19 | { 20 | private Uuid $driverId; 21 | private ?string $database; 22 | /** @var iterable */ 23 | private ?iterable $bookmarks; 24 | 25 | /** 26 | * @param iterable $bookmarks 27 | */ 28 | public function __construct(Uuid $driverId, ?string $database, ?iterable $bookmarks) 29 | { 30 | $this->driverId = $driverId; 31 | $this->database = $database; 32 | $this->bookmarks = $bookmarks; 33 | } 34 | 35 | public function getDriverId(): Uuid 36 | { 37 | return $this->driverId; 38 | } 39 | 40 | public function getDatabase(): ?string 41 | { 42 | return $this->database; 43 | } 44 | 45 | /** 46 | * @return iterable 47 | */ 48 | public function getBookmarks(): ?iterable 49 | { 50 | return $this->bookmarks; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /testkit-backend/src/Requests/GetFeaturesRequest.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\TestkitBackend\Requests; 15 | 16 | final class GetFeaturesRequest 17 | { 18 | } 19 | -------------------------------------------------------------------------------- /testkit-backend/src/Requests/GetRoutingTableRequest.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\TestkitBackend\Requests; 15 | 16 | use Symfony\Component\Uid\Uuid; 17 | 18 | final class GetRoutingTableRequest 19 | { 20 | private Uuid $driverId; 21 | private ?string $database; 22 | 23 | public function __construct(Uuid $driverId, ?string $database) 24 | { 25 | $this->driverId = $driverId; 26 | $this->database = $database; 27 | } 28 | 29 | public function getDriverId(): Uuid 30 | { 31 | return $this->driverId; 32 | } 33 | 34 | public function getDatabase(): ?string 35 | { 36 | return $this->database; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /testkit-backend/src/Requests/NewDriverRequest.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\TestkitBackend\Requests; 15 | 16 | /* 17 | * This file is part of the Laudis Neo4j package. 18 | * 19 | * (c) Laudis technologies 20 | * 21 | * For the full copyright and license information, please view the LICENSE 22 | * file that was distributed with this source code. 23 | */ 24 | 25 | namespace Laudis\Neo4j\TestkitBackend\Requests; 26 | 27 | final class NewDriverRequest 28 | { 29 | public function __construct( 30 | public readonly string $uri, 31 | public readonly AuthorizationTokenRequest $authToken, 32 | public readonly ?string $authTokenManagerId = null, 33 | public readonly ?string $userAgent = null, 34 | public readonly ?bool $resolverRegistered = null, 35 | public readonly ?bool $domainNameResolverRegistered = null, 36 | public readonly ?int $connectionTimeoutMs = null, 37 | public readonly ?int $fetchSize = null, 38 | public readonly ?int $maxTxRetryTimeMs = null, 39 | public readonly ?int $livenessCheckTimeoutMs = null, 40 | public readonly ?int $maxConnectionPoolSize = null, 41 | public readonly ?int $connectionAcquisitionTimeoutMs = null, 42 | public readonly mixed $clientCertificate = null, 43 | public readonly ?string $clientCertificateProviderId = null, 44 | ) { 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /testkit-backend/src/Requests/NewSessionRequest.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\TestkitBackend\Requests; 15 | 16 | use Symfony\Component\Uid\Uuid; 17 | 18 | final class NewSessionRequest 19 | { 20 | /** 21 | * @param list|null $bookmarks 22 | */ 23 | public function __construct( 24 | public Uuid $driverId, 25 | public string $accessMode, 26 | public ?array $bookmarks, 27 | public ?string $database, 28 | public ?int $fetchSize, 29 | public ?string $impersonatedUser, 30 | ) { 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /testkit-backend/src/Requests/ResolverResolutionCompletedRequest.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\TestkitBackend\Requests; 15 | 16 | use Symfony\Component\Uid\Uuid; 17 | 18 | final class ResolverResolutionCompletedRequest 19 | { 20 | private Uuid $requestId; 21 | /** @var iterable */ 22 | private iterable $addresses; 23 | 24 | /** 25 | * @param iterable $addresses 26 | */ 27 | public function __construct(Uuid $requestId, iterable $addresses) 28 | { 29 | $this->requestId = $requestId; 30 | $this->addresses = $addresses; 31 | } 32 | 33 | public function getRequestId(): Uuid 34 | { 35 | return $this->requestId; 36 | } 37 | 38 | /** 39 | * @return iterable 40 | */ 41 | public function getAddresses(): iterable 42 | { 43 | return $this->addresses; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /testkit-backend/src/Requests/ResultConsumeRequest.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\TestkitBackend\Requests; 15 | 16 | use Symfony\Component\Uid\Uuid; 17 | 18 | final class ResultConsumeRequest 19 | { 20 | private Uuid $resultId; 21 | 22 | public function __construct(Uuid $resultId) 23 | { 24 | $this->resultId = $resultId; 25 | } 26 | 27 | public function getResultId(): Uuid 28 | { 29 | return $this->resultId; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /testkit-backend/src/Requests/ResultNextRequest.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\TestkitBackend\Requests; 15 | 16 | use Symfony\Component\Uid\Uuid; 17 | 18 | final class ResultNextRequest 19 | { 20 | private Uuid $resultId; 21 | 22 | public function __construct(Uuid $resultId) 23 | { 24 | $this->resultId = $resultId; 25 | } 26 | 27 | public function getResultId(): Uuid 28 | { 29 | return $this->resultId; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /testkit-backend/src/Requests/ResultSingleRequest.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\TestkitBackend\Requests; 15 | 16 | use Symfony\Component\Uid\Uuid; 17 | 18 | final class ResultSingleRequest 19 | { 20 | private Uuid $resultId; 21 | 22 | public function __construct(Uuid $resultId) 23 | { 24 | $this->resultId = $resultId; 25 | } 26 | 27 | public function getResultId(): Uuid 28 | { 29 | return $this->resultId; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /testkit-backend/src/Requests/RetryableNegativeRequest.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\TestkitBackend\Requests; 15 | 16 | use Symfony\Component\Uid\Uuid; 17 | 18 | final class RetryableNegativeRequest 19 | { 20 | private Uuid $sessionId; 21 | /** @var Uuid|string */ 22 | private $errorId; 23 | 24 | /** 25 | * @param Uuid|string $errorId 26 | */ 27 | public function __construct(Uuid $sessionId, $errorId) 28 | { 29 | $this->sessionId = $sessionId; 30 | $this->errorId = $errorId; 31 | } 32 | 33 | public function getSessionId(): Uuid 34 | { 35 | return $this->sessionId; 36 | } 37 | 38 | /** 39 | * @return Uuid|string 40 | */ 41 | public function getErrorId() 42 | { 43 | return $this->errorId; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /testkit-backend/src/Requests/RetryablePositiveRequest.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\TestkitBackend\Requests; 15 | 16 | use Symfony\Component\Uid\Uuid; 17 | 18 | final class RetryablePositiveRequest 19 | { 20 | private Uuid $sessionId; 21 | 22 | public function __construct(Uuid $sessionId) 23 | { 24 | $this->sessionId = $sessionId; 25 | } 26 | 27 | public function getSessionId(): Uuid 28 | { 29 | return $this->sessionId; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /testkit-backend/src/Requests/SessionBeginTransactionRequest.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\TestkitBackend\Requests; 15 | 16 | use Laudis\Neo4j\Databags\TransactionConfiguration; 17 | use Symfony\Component\Uid\Uuid; 18 | 19 | final class SessionBeginTransactionRequest 20 | { 21 | private Uuid $sessionId; 22 | /** @var iterable|null */ 23 | private ?iterable $txMeta; 24 | private ?int $timeout; 25 | 26 | /** 27 | * @param iterable|null $txMeta 28 | */ 29 | public function __construct( 30 | Uuid $sessionId, 31 | ?iterable $txMeta = null, 32 | ?int $timeout = null, 33 | ) { 34 | $this->sessionId = $sessionId; 35 | $this->txMeta = $txMeta; 36 | $this->timeout = $timeout; 37 | } 38 | 39 | public function getSessionId(): Uuid 40 | { 41 | return $this->sessionId; 42 | } 43 | 44 | /** 45 | * @return iterable 46 | */ 47 | public function getTxMeta(): iterable 48 | { 49 | return $this->txMeta ?? []; 50 | } 51 | 52 | public function getTimeout(): int 53 | { 54 | return (int) ($this->timeout ?? TransactionConfiguration::DEFAULT_TIMEOUT); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /testkit-backend/src/Requests/SessionCloseRequest.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\TestkitBackend\Requests; 15 | 16 | use Symfony\Component\Uid\Uuid; 17 | 18 | final class SessionCloseRequest 19 | { 20 | private Uuid $sessionId; 21 | 22 | public function __construct(Uuid $sessionId) 23 | { 24 | $this->sessionId = $sessionId; 25 | } 26 | 27 | public function getSessionId(): Uuid 28 | { 29 | return $this->sessionId; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /testkit-backend/src/Requests/SessionLastBookmarksRequest.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\TestkitBackend\Requests; 15 | 16 | use Symfony\Component\Uid\Uuid; 17 | 18 | final class SessionLastBookmarksRequest 19 | { 20 | private Uuid $sessionId; 21 | 22 | public function __construct(Uuid $sessionId) 23 | { 24 | $this->sessionId = $sessionId; 25 | } 26 | 27 | public function getSessionId(): Uuid 28 | { 29 | return $this->sessionId; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /testkit-backend/src/Requests/SessionReadTransactionRequest.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\TestkitBackend\Requests; 15 | 16 | use Symfony\Component\Uid\Uuid; 17 | 18 | final class SessionReadTransactionRequest 19 | { 20 | private Uuid $sessionId; 21 | /** @var iterable */ 22 | private iterable $txMeta; 23 | private ?int $timeout; 24 | 25 | /** 26 | * @param iterable|null $txMeta 27 | */ 28 | public function __construct( 29 | Uuid $sessionId, 30 | ?iterable $txMeta = null, 31 | ?int $timeout = null, 32 | ) { 33 | $this->sessionId = $sessionId; 34 | $this->txMeta = $txMeta ?? []; 35 | $this->timeout = $timeout; 36 | } 37 | 38 | public function getSessionId(): Uuid 39 | { 40 | return $this->sessionId; 41 | } 42 | 43 | /** 44 | * @return iterable 45 | */ 46 | public function getTxMeta(): iterable 47 | { 48 | return $this->txMeta; 49 | } 50 | 51 | public function getTimeout(): ?int 52 | { 53 | return $this->timeout; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /testkit-backend/src/Requests/SessionRunRequest.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\TestkitBackend\Requests; 15 | 16 | use Symfony\Component\Uid\Uuid; 17 | 18 | final class SessionRunRequest 19 | { 20 | /** 21 | * @param iterable|null $params 22 | * @param iterable|null $txMeta 23 | */ 24 | public function __construct( 25 | private Uuid $sessionId, 26 | private string $cypher, 27 | private ?iterable $params = null, 28 | private ?iterable $txMeta = null, 29 | private ?float $timeout = null, 30 | ) { 31 | } 32 | 33 | public function getSessionId(): Uuid 34 | { 35 | return $this->sessionId; 36 | } 37 | 38 | public function getCypher(): string 39 | { 40 | return $this->cypher; 41 | } 42 | 43 | /** 44 | * @return iterable 45 | */ 46 | public function getParams(): iterable 47 | { 48 | return $this->params ?? []; 49 | } 50 | 51 | /** 52 | * @return iterable|null 53 | */ 54 | public function getTxMeta(): ?iterable 55 | { 56 | return $this->txMeta; 57 | } 58 | 59 | public function getTimeout(): ?float 60 | { 61 | return $this->timeout; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /testkit-backend/src/Requests/SessionWriteTransactionRequest.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\TestkitBackend\Requests; 15 | 16 | use Symfony\Component\Uid\Uuid; 17 | 18 | final class SessionWriteTransactionRequest 19 | { 20 | private Uuid $sessionId; 21 | /** @var iterable */ 22 | private iterable $txMeta; 23 | private ?int $timeout; 24 | 25 | /** 26 | * @param iterable|null $txMeta 27 | */ 28 | public function __construct( 29 | Uuid $sessionId, 30 | ?iterable $txMeta = null, 31 | ?int $timeout = null, 32 | ) { 33 | $this->sessionId = $sessionId; 34 | $this->txMeta = $txMeta ?? []; 35 | $this->timeout = $timeout; 36 | } 37 | 38 | public function getSessionId(): Uuid 39 | { 40 | return $this->sessionId; 41 | } 42 | 43 | /** 44 | * @return iterable 45 | */ 46 | public function getTxMeta(): iterable 47 | { 48 | return $this->txMeta; 49 | } 50 | 51 | public function getTimeout(): ?int 52 | { 53 | return $this->timeout; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /testkit-backend/src/Requests/StartTestRequest.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\TestkitBackend\Requests; 15 | 16 | final class StartTestRequest 17 | { 18 | private string $testName; 19 | 20 | public function __construct(string $testName) 21 | { 22 | $this->testName = $testName; 23 | } 24 | 25 | public function getTestName(): string 26 | { 27 | return $this->testName; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /testkit-backend/src/Requests/TransactionCommitRequest.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\TestkitBackend\Requests; 15 | 16 | use Symfony\Component\Uid\Uuid; 17 | 18 | final class TransactionCommitRequest 19 | { 20 | private Uuid $txId; 21 | 22 | public function __construct(Uuid $txId) 23 | { 24 | $this->txId = $txId; 25 | } 26 | 27 | public function getTxId(): Uuid 28 | { 29 | return $this->txId; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /testkit-backend/src/Requests/TransactionRollbackRequest.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\TestkitBackend\Requests; 15 | 16 | use Symfony\Component\Uid\Uuid; 17 | 18 | final class TransactionRollbackRequest 19 | { 20 | private Uuid $txId; 21 | 22 | public function __construct(Uuid $txId) 23 | { 24 | $this->txId = $txId; 25 | } 26 | 27 | public function getTxId(): Uuid 28 | { 29 | return $this->txId; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /testkit-backend/src/Requests/TransactionRunRequest.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\TestkitBackend\Requests; 15 | 16 | use Symfony\Component\Uid\Uuid; 17 | 18 | final class TransactionRunRequest 19 | { 20 | /** 21 | * @param iterable|null $params 22 | */ 23 | public function __construct( 24 | private Uuid $txId, 25 | private string $cypher, 26 | private ?iterable $params = null, 27 | private ?float $timeout = null, 28 | ) { 29 | } 30 | 31 | public function getTxId(): Uuid 32 | { 33 | return $this->txId; 34 | } 35 | 36 | public function getCypher(): string 37 | { 38 | return $this->cypher; 39 | } 40 | 41 | /** 42 | * @return iterable 43 | */ 44 | public function getParams(): iterable 45 | { 46 | return $this->params ?? []; 47 | } 48 | 49 | public function getTimeout(): ?float 50 | { 51 | return $this->timeout; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /testkit-backend/src/Requests/VerifyConnectivityRequest.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\TestkitBackend\Requests; 15 | 16 | use Symfony\Component\Uid\Uuid; 17 | 18 | final class VerifyConnectivityRequest 19 | { 20 | private Uuid $driverId; 21 | 22 | public function __construct(Uuid $driverId) 23 | { 24 | $this->driverId = $driverId; 25 | } 26 | 27 | public function getDriverId(): Uuid 28 | { 29 | return $this->driverId; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /testkit-backend/src/Responses/BackendErrorResponse.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\TestkitBackend\Responses; 15 | 16 | use Laudis\Neo4j\TestkitBackend\Contracts\TestkitResponseInterface; 17 | 18 | /** 19 | * Indicates an internal error has occurred. 20 | */ 21 | final class BackendErrorResponse implements TestkitResponseInterface 22 | { 23 | private string $message; 24 | 25 | public function __construct(string $message) 26 | { 27 | $this->message = $message; 28 | } 29 | 30 | public function jsonSerialize(): array 31 | { 32 | return [ 33 | 'name' => 'BackendError', 34 | 'data' => [ 35 | 'msg' => $this->message, 36 | ], 37 | ]; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /testkit-backend/src/Responses/BookmarksResponse.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\TestkitBackend\Responses; 15 | 16 | use Laudis\Neo4j\TestkitBackend\Contracts\TestkitResponseInterface; 17 | 18 | /** 19 | * Represents an array of bookmarks. 20 | */ 21 | final class BookmarksResponse implements TestkitResponseInterface 22 | { 23 | /** 24 | * @var iterable 25 | */ 26 | private iterable $bookmarks; 27 | 28 | /** 29 | * @param iterable $bookmarks 30 | */ 31 | public function __construct(iterable $bookmarks) 32 | { 33 | $this->bookmarks = $bookmarks; 34 | } 35 | 36 | public function jsonSerialize(): array 37 | { 38 | return [ 39 | 'name' => 'Bookmarks', 40 | 'data' => [ 41 | 'bookmarks' => $this->bookmarks, 42 | ], 43 | ]; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /testkit-backend/src/Responses/DomainNameResolutionRequiredResponse.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\TestkitBackend\Responses; 15 | 16 | use Laudis\Neo4j\TestkitBackend\Contracts\TestkitResponseInterface; 17 | use Symfony\Component\Uid\Uuid; 18 | 19 | /** 20 | * Represents a need for new address resolution. 21 | * 22 | * This means that the backend expects the frontend to call the resolver function and submit a new request 23 | * with the results of it. 24 | */ 25 | final class DomainNameResolutionRequiredResponse implements TestkitResponseInterface 26 | { 27 | private Uuid $id; 28 | private string $name; 29 | 30 | public function __construct(Uuid $id, string $name) 31 | { 32 | $this->id = $id; 33 | $this->name = $name; 34 | } 35 | 36 | public function jsonSerialize(): array 37 | { 38 | return [ 39 | 'name' => 'DomainNameResolutionRequired', 40 | 'data' => [ 41 | 'id' => $this->id->toRfc4122(), 42 | 'name' => $this->name, 43 | ], 44 | ]; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /testkit-backend/src/Responses/DriverErrorResponse.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\TestkitBackend\Responses; 15 | 16 | use Laudis\Neo4j\Exception\Neo4jException; 17 | use Laudis\Neo4j\TestkitBackend\Contracts\TestkitResponseInterface; 18 | use Symfony\Component\Uid\Uuid; 19 | 20 | /** 21 | * Base class for all kind of driver errors that is NOT a backend specific error. 22 | */ 23 | final class DriverErrorResponse implements TestkitResponseInterface 24 | { 25 | private Uuid $id; 26 | private Neo4jException $exception; 27 | 28 | public function __construct(Uuid $id, Neo4jException $exception) 29 | { 30 | $this->id = $id; 31 | $this->exception = $exception; 32 | } 33 | 34 | public function jsonSerialize(): array 35 | { 36 | return [ 37 | 'name' => 'DriverError', 38 | 'data' => [ 39 | 'id' => $this->id->toRfc4122(), 40 | 'code' => $this->exception->getNeo4jCode(), 41 | 'msg' => $this->exception->getNeo4jMessage(), 42 | ], 43 | ]; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /testkit-backend/src/Responses/DriverResponse.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\TestkitBackend\Responses; 15 | 16 | use Laudis\Neo4j\TestkitBackend\Contracts\TestkitResponseInterface; 17 | use Symfony\Component\Uid\Uuid; 18 | 19 | /** 20 | * Represents the driver instance in the backend. 21 | */ 22 | final class DriverResponse implements TestkitResponseInterface 23 | { 24 | private Uuid $id; 25 | 26 | public function __construct(Uuid $id) 27 | { 28 | $this->id = $id; 29 | } 30 | 31 | public function jsonSerialize(): array 32 | { 33 | return [ 34 | 'name' => 'Driver', 35 | 'data' => [ 36 | 'id' => $this->id->toRfc4122(), 37 | ], 38 | ]; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /testkit-backend/src/Responses/FeatureListResponse.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\TestkitBackend\Responses; 15 | 16 | use Laudis\Neo4j\TestkitBackend\Contracts\TestkitResponseInterface; 17 | 18 | /** 19 | * Indicates the features the driver supports. 20 | */ 21 | final class FeatureListResponse implements TestkitResponseInterface 22 | { 23 | private array $features; 24 | 25 | public function __construct(array $features) 26 | { 27 | $this->features = $features; 28 | } 29 | 30 | public function jsonSerialize(): array 31 | { 32 | return [ 33 | 'name' => 'FeatureList', 34 | 'data' => [ 35 | 'features' => $this->features, 36 | ], 37 | ]; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /testkit-backend/src/Responses/FrontendErrorResponse.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\TestkitBackend\Responses; 15 | 16 | use Laudis\Neo4j\TestkitBackend\Contracts\TestkitResponseInterface; 17 | 18 | /** 19 | * Represents an error originating from client code. 20 | */ 21 | final class FrontendErrorResponse implements TestkitResponseInterface 22 | { 23 | private string $message; 24 | 25 | public function __construct(string $message) 26 | { 27 | $this->message = $message; 28 | } 29 | 30 | public function jsonSerialize(): array 31 | { 32 | return [ 33 | 'name' => 'FrontendError', 34 | 'data' => [ 35 | 'msg' => $this->message, 36 | ], 37 | ]; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /testkit-backend/src/Responses/MultiDBSupportResponse.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\TestkitBackend\Responses; 15 | 16 | use Laudis\Neo4j\TestkitBackend\Contracts\TestkitResponseInterface; 17 | use Symfony\Component\Uid\Uuid; 18 | 19 | /** 20 | * Specifies whether the server or cluster the driver connects to supports multi-databases. 21 | */ 22 | final class MultiDBSupportResponse implements TestkitResponseInterface 23 | { 24 | private Uuid $id; 25 | private bool $available; 26 | 27 | public function __construct(Uuid $id, bool $available) 28 | { 29 | $this->id = $id; 30 | $this->available = $available; 31 | } 32 | 33 | public function jsonSerialize(): array 34 | { 35 | return [ 36 | 'name' => 'MultiDBSupport', 37 | 'data' => [ 38 | 'id' => $this->id, 39 | 'available' => $this->available, 40 | ], 41 | ]; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /testkit-backend/src/Responses/NullRecordResponse.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\TestkitBackend\Responses; 15 | 16 | use Laudis\Neo4j\TestkitBackend\Contracts\TestkitResponseInterface; 17 | 18 | /** 19 | * Represents the record end when iterating through it. 20 | */ 21 | final class NullRecordResponse implements TestkitResponseInterface 22 | { 23 | public function jsonSerialize(): array 24 | { 25 | return [ 26 | 'name' => 'NullRecord', 27 | ]; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /testkit-backend/src/Responses/RecordResponse.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\TestkitBackend\Responses; 15 | 16 | use Laudis\Neo4j\TestkitBackend\Contracts\TestkitResponseInterface; 17 | 18 | /** 19 | * Represents a record from a result. 20 | */ 21 | final class RecordResponse implements TestkitResponseInterface 22 | { 23 | /** 24 | * @var iterable 25 | */ 26 | private iterable $values; 27 | 28 | /** 29 | * @param iterable $values 30 | */ 31 | public function __construct(iterable $values) 32 | { 33 | $this->values = $values; 34 | } 35 | 36 | public function jsonSerialize(): array 37 | { 38 | return [ 39 | 'name' => 'Record', 40 | 'data' => [ 41 | 'values' => $this->values, 42 | ], 43 | ]; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /testkit-backend/src/Responses/ResolverResolutionRequiredResponse.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\TestkitBackend\Responses; 15 | 16 | use Laudis\Neo4j\TestkitBackend\Contracts\TestkitResponseInterface; 17 | use Symfony\Component\Uid\Uuid; 18 | 19 | /** 20 | * Represents a need for new address resolution. 21 | * 22 | * This means that the backend is expecting the frontend to call the resolver function and submit a new request 23 | * with the results of it. 24 | */ 25 | final class ResolverResolutionRequiredResponse implements TestkitResponseInterface 26 | { 27 | private Uuid $id; 28 | private string $address; 29 | 30 | public function __construct(Uuid $id, string $address) 31 | { 32 | $this->id = $id; 33 | $this->address = $address; 34 | } 35 | 36 | public function jsonSerialize(): array 37 | { 38 | return [ 39 | 'name' => 'ResolverResolutionRequired', 40 | 'data' => [ 41 | 'id' => $this->id->toRfc4122(), 42 | 'address' => $this->address, 43 | ], 44 | ]; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /testkit-backend/src/Responses/ResultResponse.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\TestkitBackend\Responses; 15 | 16 | use Laudis\Neo4j\TestkitBackend\Contracts\TestkitResponseInterface; 17 | use Symfony\Component\Uid\Uuid; 18 | 19 | /** 20 | * Represents a result instance on the backend. 21 | */ 22 | final class ResultResponse implements TestkitResponseInterface 23 | { 24 | private Uuid $id; 25 | private iterable $keys; 26 | 27 | /** 28 | * @param iterable $keys 29 | */ 30 | public function __construct(Uuid $id, iterable $keys) 31 | { 32 | $this->id = $id; 33 | $this->keys = $keys; 34 | } 35 | 36 | public function jsonSerialize(): array 37 | { 38 | return [ 39 | 'name' => 'Result', 40 | 'data' => [ 41 | 'id' => $this->id->toRfc4122(), 42 | 'keys' => $this->keys, 43 | ], 44 | ]; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /testkit-backend/src/Responses/RetryableDoneResponse.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\TestkitBackend\Responses; 15 | 16 | use Laudis\Neo4j\TestkitBackend\Contracts\TestkitResponseInterface; 17 | 18 | /** 19 | * Indicates a retryable transaction is successfully committed. 20 | */ 21 | final class RetryableDoneResponse implements TestkitResponseInterface 22 | { 23 | public function jsonSerialize(): array 24 | { 25 | return [ 26 | 'name' => 'RetryableDone', 27 | 'data' => [], 28 | ]; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /testkit-backend/src/Responses/RetryableTryResponse.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\TestkitBackend\Responses; 15 | 16 | use Laudis\Neo4j\TestkitBackend\Contracts\TestkitResponseInterface; 17 | use Symfony\Component\Uid\Uuid; 18 | 19 | /** 20 | * Represents a retryable transaction. The backend created a transaction and will use a retryable function. 21 | * All further requests will be applied through that retryable function. 22 | */ 23 | final class RetryableTryResponse implements TestkitResponseInterface 24 | { 25 | private Uuid $id; 26 | 27 | public function __construct(Uuid $id) 28 | { 29 | $this->id = $id; 30 | } 31 | 32 | public function jsonSerialize(): array 33 | { 34 | return [ 35 | 'name' => 'RetryableTry', 36 | 'data' => [ 37 | 'id' => $this->id->toRfc4122(), 38 | ], 39 | ]; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /testkit-backend/src/Responses/RoutingTableResponse.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\TestkitBackend\Responses; 15 | 16 | use Laudis\Neo4j\TestkitBackend\Contracts\TestkitResponseInterface; 17 | 18 | /** 19 | * Represents the full routing table. 20 | */ 21 | final class RoutingTableResponse implements TestkitResponseInterface 22 | { 23 | private ?string $database; 24 | private int $ttl; 25 | /** @var iterable */ 26 | private iterable $routers; 27 | /** @var iterable */ 28 | private iterable $readers; 29 | /** @var iterable */ 30 | private iterable $writers; 31 | 32 | /** 33 | * @param iterable $routers 34 | * @param iterable $readers 35 | * @param iterable $writers 36 | */ 37 | public function __construct(?string $database, int $ttl, iterable $routers, iterable $readers, iterable $writers) 38 | { 39 | $this->database = $database; 40 | $this->ttl = $ttl; 41 | $this->routers = $routers; 42 | $this->readers = $readers; 43 | $this->writers = $writers; 44 | } 45 | 46 | public function jsonSerialize(): array 47 | { 48 | return [ 49 | 'name' => 'RoutingTable', 50 | 'data' => [ 51 | 'database' => $this->database, 52 | 'ttl' => $this->ttl, 53 | 'routers' => $this->routers, 54 | 'readers' => $this->readers, 55 | 'writers' => $this->writers, 56 | ], 57 | ]; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /testkit-backend/src/Responses/RunTestResponse.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\TestkitBackend\Responses; 15 | 16 | use Laudis\Neo4j\TestkitBackend\Contracts\TestkitResponseInterface; 17 | 18 | /** 19 | * Indicates the test can start. 20 | */ 21 | final class RunTestResponse implements TestkitResponseInterface 22 | { 23 | public function jsonSerialize(): array 24 | { 25 | return [ 26 | 'name' => 'RunTest', 27 | ]; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /testkit-backend/src/Responses/SessionResponse.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\TestkitBackend\Responses; 15 | 16 | use Laudis\Neo4j\TestkitBackend\Contracts\TestkitResponseInterface; 17 | use Symfony\Component\Uid\Uuid; 18 | 19 | /** 20 | * Represents a session instance on the backend. 21 | */ 22 | final class SessionResponse implements TestkitResponseInterface 23 | { 24 | private Uuid $id; 25 | 26 | public function __construct(Uuid $id) 27 | { 28 | $this->id = $id; 29 | } 30 | 31 | public function jsonSerialize(): array 32 | { 33 | return [ 34 | 'name' => 'Session', 35 | 'data' => [ 36 | 'id' => $this->id->toRfc4122(), 37 | ], 38 | ]; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /testkit-backend/src/Responses/SkipTestResponse.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\TestkitBackend\Responses; 15 | 16 | use Laudis\Neo4j\TestkitBackend\Contracts\TestkitResponseInterface; 17 | 18 | /** 19 | * Indicates the test should be skipped. 20 | */ 21 | final class SkipTestResponse implements TestkitResponseInterface 22 | { 23 | private string $reason; 24 | 25 | public function __construct(string $reason) 26 | { 27 | $this->reason = $reason; 28 | } 29 | 30 | public function jsonSerialize(): array 31 | { 32 | return [ 33 | 'name' => 'SkipTest', 34 | 'data' => [ 35 | 'reason' => $this->reason, 36 | ], 37 | ]; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /testkit-backend/src/Responses/TransactionResponse.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\TestkitBackend\Responses; 15 | 16 | use Laudis\Neo4j\TestkitBackend\Contracts\TestkitResponseInterface; 17 | use Symfony\Component\Uid\Uuid; 18 | 19 | /** 20 | * Represents a transaction instance on the backend. 21 | */ 22 | final class TransactionResponse implements TestkitResponseInterface 23 | { 24 | private Uuid $id; 25 | 26 | public function __construct(Uuid $id) 27 | { 28 | $this->id = $id; 29 | } 30 | 31 | public function jsonSerialize(): array 32 | { 33 | return [ 34 | 'name' => 'Transaction', 35 | 'data' => [ 36 | 'id' => $this->id->toRfc4122(), 37 | ], 38 | ]; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /testkit-backend/src/Responses/Types/CypherNode.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\TestkitBackend\Responses\Types; 15 | 16 | use Laudis\Neo4j\TestkitBackend\Contracts\TestkitResponseInterface; 17 | 18 | final class CypherNode implements TestkitResponseInterface 19 | { 20 | private CypherObject $id; 21 | private CypherObject $labels; 22 | private CypherObject $props; 23 | private CypherObject $elementId; 24 | 25 | public function __construct(CypherObject $id, CypherObject $labels, CypherObject $props, CypherObject $elementId) 26 | { 27 | $this->id = $id; 28 | $this->labels = $labels; 29 | $this->props = $props; 30 | $this->elementId = $elementId; 31 | } 32 | 33 | public function jsonSerialize(): array 34 | { 35 | return [ 36 | 'name' => 'CypherNode', 37 | 'data' => [ 38 | 'id' => $this->id, 39 | 'labels' => $this->labels, 40 | 'props' => $this->props, 41 | 'elementId' => $this->elementId, 42 | ], 43 | ]; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /testkit-backend/src/Responses/Types/CypherPath.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\TestkitBackend\Responses\Types; 15 | 16 | use Laudis\Neo4j\TestkitBackend\Contracts\TestkitResponseInterface; 17 | 18 | final class CypherPath implements TestkitResponseInterface 19 | { 20 | private CypherObject $nodes; 21 | private CypherObject $relationships; 22 | 23 | public function __construct(CypherObject $nodes, CypherObject $relationships) 24 | { 25 | $this->nodes = $nodes; 26 | $this->relationships = $relationships; 27 | } 28 | 29 | public function jsonSerialize(): array 30 | { 31 | return [ 32 | 'name' => 'CypherPath', 33 | 'data' => [ 34 | 'nodes' => $this->nodes, 35 | 'relationships' => $this->relationships, 36 | ], 37 | ]; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /testkit-backend/src/Responses/Types/CypherRelationship.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Laudis\Neo4j\TestkitBackend\Responses\Types; 15 | 16 | use Laudis\Neo4j\TestkitBackend\Contracts\TestkitResponseInterface; 17 | 18 | final class CypherRelationship implements TestkitResponseInterface 19 | { 20 | private CypherObject $id; 21 | private CypherObject $startNodeId; 22 | private CypherObject $endNodeId; 23 | private CypherObject $type; 24 | private CypherObject $props; 25 | private CypherObject $elementId; 26 | 27 | public function __construct(CypherObject $id, CypherObject $startNodeId, CypherObject $endNodeId, CypherObject $type, CypherObject $props, CypherObject $elementId) 28 | { 29 | $this->id = $id; 30 | $this->startNodeId = $startNodeId; 31 | $this->endNodeId = $endNodeId; 32 | $this->type = $type; 33 | $this->props = $props; 34 | $this->elementId = $elementId; 35 | } 36 | 37 | public function jsonSerialize(): array 38 | { 39 | return [ 40 | 'name' => 'CypherRelationship', 41 | 'data' => [ 42 | 'id' => $this->id, 43 | 'startNodeId' => $this->startNodeId, 44 | 'endNodeId' => $this->endNodeId, 45 | 'type' => $this->type, 46 | 'props' => $this->props, 47 | 'elementId' => $this->elementId, 48 | ], 49 | ]; 50 | } 51 | } 52 | --------------------------------------------------------------------------------