From 4c1a518f63d2c26099ededce34e5e4741f7ad992 Mon Sep 17 00:00:00 2001 From: exaby73 Date: Wed, 28 Aug 2024 10:43:17 +0530 Subject: [PATCH 01/25] refactor: Remove profiler integration --- config/services.php | 3 - src/Collector/Neo4jDataCollector.php | 83 ------------------- src/Collector/Twig/Neo4jResultExtension.php | 56 ------------- src/DependencyInjection/Neo4jExtension.php | 15 ---- src/Event/FailureEvent.php | 45 ---------- src/Event/PostRunEvent.php | 29 ------- src/Event/PreRunEvent.php | 27 ------ src/EventListener/Neo4jProfileListener.php | 78 ----------------- .../Twig/Neo4jResultExtensionTest.php | 65 --------------- .../Neo4jExtensionTest.php | 57 ------------- 10 files changed, 458 deletions(-) delete mode 100644 src/Collector/Neo4jDataCollector.php delete mode 100644 src/Collector/Twig/Neo4jResultExtension.php delete mode 100644 src/Event/FailureEvent.php delete mode 100644 src/Event/PostRunEvent.php delete mode 100644 src/Event/PreRunEvent.php delete mode 100644 src/EventListener/Neo4jProfileListener.php delete mode 100644 tests/Unit/Collector/Twig/Neo4jResultExtensionTest.php delete mode 100644 tests/Unit/DependencyInjection/Neo4jExtensionTest.php diff --git a/config/services.php b/config/services.php index 8ec9920..2e78d3a 100644 --- a/config/services.php +++ b/config/services.php @@ -48,7 +48,4 @@ $services->alias(DriverInterface::class, 'neo4j.driver'); $services->alias(SessionInterface::class, 'neo4j.session'); $services->alias(TransactionInterface::class, 'neo4j.transaction'); - - $services->set('neo4j.subscriber', Neo4jProfileListener::class) - ->tag('kernel.event_subscriber'); }; diff --git a/src/Collector/Neo4jDataCollector.php b/src/Collector/Neo4jDataCollector.php deleted file mode 100644 index 2e3f739..0000000 --- a/src/Collector/Neo4jDataCollector.php +++ /dev/null @@ -1,83 +0,0 @@ ->, - * failed_statements: list - * } $data - */ -final class Neo4jDataCollector extends AbstractDataCollector -{ - public function __construct( - private Neo4jProfileListener $subscriber - ) { - } - - public function collect(Request $request, Response $response, ?\Throwable $exception = null): void - { - $this->data['successful_statements'] = array_map( - static fn (ResultSummary $summary) => $summary->toArray(), - $this->subscriber->getProfiledSummaries() - ); - - $this->data['failed_statements'] = array_map( - static fn (array $x) => [ - 'statement' => $x['statement']->toArray(), - 'exception' => [ - 'code' => $x['exception']->getErrors()[0]->getCode(), - 'message' => $x['exception']->getErrors()[0]->getMessage(), - 'classification' => $x['exception']->getErrors()[0]->getClassification(), - 'category' => $x['exception']->getErrors()[0]->getCategory(), - 'title' => $x['exception']->getErrors()[0]->getTitle(), - ], - 'alias' => $x['alias'], - ], - $this->subscriber->getProfiledFailures() - ); - } - - public function reset(): void - { - parent::reset(); - $this->subscriber->reset(); - } - - public function getName(): string - { - return 'neo4j'; - } - - public function getFailedStatements(): array - { - return $this->data['failed_statements']; - } - - public function getSuccessfulStatements(): array - { - return $this->data['successful_statements']; - } - - public function getQueryCount(): int - { - return count($this->data['successful_statements']) + count($this->data['failed_statements']); - } - - public static function getTemplate(): ?string - { - return 'web_profiler.html.twig'; - } -} diff --git a/src/Collector/Twig/Neo4jResultExtension.php b/src/Collector/Twig/Neo4jResultExtension.php deleted file mode 100644 index 20277f1..0000000 --- a/src/Collector/Twig/Neo4jResultExtension.php +++ /dev/null @@ -1,56 +0,0 @@ - - */ -class Neo4jResultExtension extends AbstractExtension -{ - /** - * @return array - */ - public function getFilters(): array - { - return [ - new TwigFilter('neo4jResult', [$this, 'getType']), - ]; - } - - public function getType(mixed $object): string - { - return $this->doGetType($object, true); - } - - public function getName(): string - { - return 'neo4j.result'; - } - - private function doGetType(mixed $object, bool $recursive): string - { - if ($object instanceof Node) { - return sprintf('%s: %s', $object->getId(), $object->getLabels()->join(', ')); - } - - if (is_array($object) && $recursive) { - if (empty($object)) { - return 'Empty array'; - } - $ret = []; - foreach ($object as $o) { - $ret[] = $this->doGetType($o, false); - } - - return sprintf('[%s]', implode(', ', $ret)); - } - - return get_debug_type($object); - } -} diff --git a/src/DependencyInjection/Neo4jExtension.php b/src/DependencyInjection/Neo4jExtension.php index f8617ae..5283723 100644 --- a/src/DependencyInjection/Neo4jExtension.php +++ b/src/DependencyInjection/Neo4jExtension.php @@ -12,7 +12,6 @@ use Symfony\Component\Config\FileLocator; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\ContainerInterface; -use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Extension\Extension; use Symfony\Component\DependencyInjection\Loader\PhpFileLoader; use Symfony\Component\DependencyInjection\Reference; @@ -52,20 +51,6 @@ public function load(array $configs, ContainerBuilder $container): ContainerBuil } } - if (0 !== count($enabledProfiles)) { - $container->setDefinition('neo4j.data_collector', (new Definition(Neo4jDataCollector::class)) - ->setAutowired(true) - ->addTag('data_collector') - ); - - $container->setAlias(Neo4jProfileListener::class, 'neo4j.subscriber'); - - $container->setDefinition('neo4j.subscriber', (new Definition(Neo4jProfileListener::class)) - ->setArgument(0, $enabledProfiles) - ->addTag('kernel.event_subscriber') - ); - } - return $container; } diff --git a/src/Event/FailureEvent.php b/src/Event/FailureEvent.php deleted file mode 100644 index 2981193..0000000 --- a/src/Event/FailureEvent.php +++ /dev/null @@ -1,45 +0,0 @@ -exception; - } - - public function disableException(): void - { - $this->shouldThrowException = false; - } - - public function shouldThrowException(): bool - { - return $this->shouldThrowException; - } - - public function getAlias(): ?string - { - return $this->alias; - } - - public function getStatement(): Statement - { - return $this->statement; - } -} diff --git a/src/Event/PostRunEvent.php b/src/Event/PostRunEvent.php deleted file mode 100644 index b0bf84b..0000000 --- a/src/Event/PostRunEvent.php +++ /dev/null @@ -1,29 +0,0 @@ -result; - } - - public function getAlias(): ?string - { - return $this->alias; - } -} diff --git a/src/Event/PreRunEvent.php b/src/Event/PreRunEvent.php deleted file mode 100644 index 6ab3f4d..0000000 --- a/src/Event/PreRunEvent.php +++ /dev/null @@ -1,27 +0,0 @@ -statement; - } - - public function getAlias(): ?string - { - return $this->alias; - } -} diff --git a/src/EventListener/Neo4jProfileListener.php b/src/EventListener/Neo4jProfileListener.php deleted file mode 100644 index b046b99..0000000 --- a/src/EventListener/Neo4jProfileListener.php +++ /dev/null @@ -1,78 +0,0 @@ - - */ - private array $profiledSummaries = []; - - /** - * @var list - */ - private array $profiledFailures = []; - - /** - * @param list $enabledProfiles - */ - public function __construct(private array $enabledProfiles = []) - { - } - - public static function getSubscribedEvents(): array - { - return [ - PostRunEvent::EVENT_ID => 'onPostRun', - FailureEvent::EVENT_ID => 'onFailure', - ]; - } - - public function onPostRun(PostRunEvent $event): void - { - if (in_array($event->getAlias(), $this->enabledProfiles)) { - $this->profiledSummaries[] = $event->getResult(); - } - } - - public function onFailure(FailureEvent $event): void - { - if (in_array($event->getAlias(), $this->enabledProfiles)) { - $this->profiledFailures[] = [ - 'exception' => $event->getException(), - 'statement' => $event->getStatement(), - 'alias' => $event->getAlias(), - ]; - } - } - - public function getProfiledSummaries(): array - { - return $this->profiledSummaries; - } - - /** - * @return list - */ - public function getProfiledFailures(): array - { - return $this->profiledFailures; - } - - public function reset(): void - { - $this->profiledFailures = []; - $this->profiledSummaries = []; - } -} diff --git a/tests/Unit/Collector/Twig/Neo4jResultExtensionTest.php b/tests/Unit/Collector/Twig/Neo4jResultExtensionTest.php deleted file mode 100644 index 07bc8f6..0000000 --- a/tests/Unit/Collector/Twig/Neo4jResultExtensionTest.php +++ /dev/null @@ -1,65 +0,0 @@ - - */ -class Neo4jResultExtensionTest extends TestCase -{ - public function testEmptyArray(): void - { - $o = new Neo4jResultExtension(); - $result = $o->getType([]); - - $this->assertEquals('Empty array', $result); - } - - public function testObject(): void - { - $o = new Neo4jResultExtension(); - $result = $o->getType($o); - - $this->assertEquals(Neo4jResultExtension::class, $result); - } - - public function testScalar(): void - { - $o = new Neo4jResultExtension(); - $result = $o->getType(3); - - $this->assertEquals('int', $result); - } - - public function testScalarArray(): void - { - $o = new Neo4jResultExtension(); - $result = $o->getType([3, 6.3]); - - $this->assertEquals('[int, float]', $result); - } - - public function testArrayArray(): void - { - $o = new Neo4jResultExtension(); - $result = $o->getType([[]]); - - $this->assertEquals('[array]', $result); - } - - public function testNode(): void - { - $o = new Neo4jResultExtension(); - $result = $o->getType(new Node(1, new CypherList(['Label']), new CypherMap(), null)); - - $this->assertEquals('1: Label', $result); - } -} diff --git a/tests/Unit/DependencyInjection/Neo4jExtensionTest.php b/tests/Unit/DependencyInjection/Neo4jExtensionTest.php deleted file mode 100644 index 572535d..0000000 --- a/tests/Unit/DependencyInjection/Neo4jExtensionTest.php +++ /dev/null @@ -1,57 +0,0 @@ - - */ -class Neo4jExtensionTest extends AbstractExtensionTestCase -{ - protected function getMinimalConfiguration(): array - { - $this->setParameter('kernel.cache_dir', 'foo'); - - return ['drivers' => ['default' => ['dsn' => 'bolt://localhost']]]; - } - - public function testDataCollectorLoaded(): void - { - $this->setParameter('kernel.debug', true); - $this->load(); - - $this->assertContainerBuilderHasService('neo4j.data_collector', Neo4jDataCollector::class); - } - - public function testDataCollectorNotLoadedInNonDebug(): void - { - $this->setParameter('kernel.debug', false); - $this->load(); - - $this->assertContainerBuilderNotHasService('neo4j.data_collector'); - } - - public function testDataCollectorNotLoadedWhenDisabled(): void - { - $this->setParameter('kernel.debug', true); - $this->load(['drivers' => [ - 'default' => [ - 'profiling' => false, - ], - ]]); - - $this->assertContainerBuilderNotHasService('neo4j.neo4j_data_collector'); - } - - protected function getContainerExtensions(): array - { - return [ - new Neo4jExtension(), - ]; - } -} From 5e6fdc5d1ba75ddabd4b5da9249751665b768d6f Mon Sep 17 00:00:00 2001 From: exaby73 Date: Wed, 28 Aug 2024 10:50:57 +0530 Subject: [PATCH 02/25] refactor: Bring back EventHandler and fix CS --- config/services.php | 1 - src/DependencyInjection/Neo4jExtension.php | 2 - src/Event/FailureEvent.php | 45 ++++++++++++++++++++++ src/Event/PostRunEvent.php | 29 ++++++++++++++ src/Event/PreRunEvent.php | 27 +++++++++++++ 5 files changed, 101 insertions(+), 3 deletions(-) create mode 100644 src/Event/FailureEvent.php create mode 100644 src/Event/PostRunEvent.php create mode 100644 src/Event/PreRunEvent.php diff --git a/config/services.php b/config/services.php index 2e78d3a..957a43f 100644 --- a/config/services.php +++ b/config/services.php @@ -8,7 +8,6 @@ use Laudis\Neo4j\Contracts\TransactionInterface; use Neo4j\Neo4jBundle\ClientFactory; use Neo4j\Neo4jBundle\EventHandler; -use Neo4j\Neo4jBundle\EventListener\Neo4jProfileListener; use Neo4j\Neo4jBundle\SymfonyClient; use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; diff --git a/src/DependencyInjection/Neo4jExtension.php b/src/DependencyInjection/Neo4jExtension.php index 5283723..bf491ec 100644 --- a/src/DependencyInjection/Neo4jExtension.php +++ b/src/DependencyInjection/Neo4jExtension.php @@ -4,8 +4,6 @@ namespace Neo4j\Neo4jBundle\DependencyInjection; -use Neo4j\Neo4jBundle\Collector\Neo4jDataCollector; -use Neo4j\Neo4jBundle\EventListener\Neo4jProfileListener; use Psr\Http\Client\ClientInterface; use Psr\Http\Message\RequestFactoryInterface; use Psr\Http\Message\StreamFactoryInterface; diff --git a/src/Event/FailureEvent.php b/src/Event/FailureEvent.php new file mode 100644 index 0000000..2981193 --- /dev/null +++ b/src/Event/FailureEvent.php @@ -0,0 +1,45 @@ +exception; + } + + public function disableException(): void + { + $this->shouldThrowException = false; + } + + public function shouldThrowException(): bool + { + return $this->shouldThrowException; + } + + public function getAlias(): ?string + { + return $this->alias; + } + + public function getStatement(): Statement + { + return $this->statement; + } +} diff --git a/src/Event/PostRunEvent.php b/src/Event/PostRunEvent.php new file mode 100644 index 0000000..b0bf84b --- /dev/null +++ b/src/Event/PostRunEvent.php @@ -0,0 +1,29 @@ +result; + } + + public function getAlias(): ?string + { + return $this->alias; + } +} diff --git a/src/Event/PreRunEvent.php b/src/Event/PreRunEvent.php new file mode 100644 index 0000000..6ab3f4d --- /dev/null +++ b/src/Event/PreRunEvent.php @@ -0,0 +1,27 @@ +statement; + } + + public function getAlias(): ?string + { + return $this->alias; + } +} From f84e9ddd4cec5229c6f1de18fada059e85e91858 Mon Sep 17 00:00:00 2001 From: exaby73 Date: Wed, 28 Aug 2024 10:53:08 +0530 Subject: [PATCH 03/25] remove: Unit test config --- phpunit.xml.dist | 3 --- 1 file changed, 3 deletions(-) diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 9fd609d..73e173e 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,9 +1,6 @@ - - ./tests/Unit - ./tests/Functional From c704332a810db97616a9c53dbd7a6d754a97fbb1 Mon Sep 17 00:00:00 2001 From: exaby73 Date: Wed, 28 Aug 2024 10:59:01 +0530 Subject: [PATCH 04/25] revert: Remove profiling --- config/services.php | 4 + phpunit.xml.dist | 3 + src/Collector/Neo4jDataCollector.php | 83 +++++++++++++++++++ src/Collector/Twig/Neo4jResultExtension.php | 56 +++++++++++++ src/DependencyInjection/Neo4jExtension.php | 17 ++++ src/EventListener/Neo4jProfileListener.php | 78 +++++++++++++++++ .../Twig/Neo4jResultExtensionTest.php | 65 +++++++++++++++ .../Neo4jExtensionTest.php | 57 +++++++++++++ 8 files changed, 363 insertions(+) create mode 100644 src/Collector/Neo4jDataCollector.php create mode 100644 src/Collector/Twig/Neo4jResultExtension.php create mode 100644 src/EventListener/Neo4jProfileListener.php create mode 100644 tests/Unit/Collector/Twig/Neo4jResultExtensionTest.php create mode 100644 tests/Unit/DependencyInjection/Neo4jExtensionTest.php diff --git a/config/services.php b/config/services.php index 957a43f..8ec9920 100644 --- a/config/services.php +++ b/config/services.php @@ -8,6 +8,7 @@ use Laudis\Neo4j\Contracts\TransactionInterface; use Neo4j\Neo4jBundle\ClientFactory; use Neo4j\Neo4jBundle\EventHandler; +use Neo4j\Neo4jBundle\EventListener\Neo4jProfileListener; use Neo4j\Neo4jBundle\SymfonyClient; use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; @@ -47,4 +48,7 @@ $services->alias(DriverInterface::class, 'neo4j.driver'); $services->alias(SessionInterface::class, 'neo4j.session'); $services->alias(TransactionInterface::class, 'neo4j.transaction'); + + $services->set('neo4j.subscriber', Neo4jProfileListener::class) + ->tag('kernel.event_subscriber'); }; diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 73e173e..9fd609d 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,6 +1,9 @@ + + ./tests/Unit + ./tests/Functional diff --git a/src/Collector/Neo4jDataCollector.php b/src/Collector/Neo4jDataCollector.php new file mode 100644 index 0000000..2e3f739 --- /dev/null +++ b/src/Collector/Neo4jDataCollector.php @@ -0,0 +1,83 @@ +>, + * failed_statements: list + * } $data + */ +final class Neo4jDataCollector extends AbstractDataCollector +{ + public function __construct( + private Neo4jProfileListener $subscriber + ) { + } + + public function collect(Request $request, Response $response, ?\Throwable $exception = null): void + { + $this->data['successful_statements'] = array_map( + static fn (ResultSummary $summary) => $summary->toArray(), + $this->subscriber->getProfiledSummaries() + ); + + $this->data['failed_statements'] = array_map( + static fn (array $x) => [ + 'statement' => $x['statement']->toArray(), + 'exception' => [ + 'code' => $x['exception']->getErrors()[0]->getCode(), + 'message' => $x['exception']->getErrors()[0]->getMessage(), + 'classification' => $x['exception']->getErrors()[0]->getClassification(), + 'category' => $x['exception']->getErrors()[0]->getCategory(), + 'title' => $x['exception']->getErrors()[0]->getTitle(), + ], + 'alias' => $x['alias'], + ], + $this->subscriber->getProfiledFailures() + ); + } + + public function reset(): void + { + parent::reset(); + $this->subscriber->reset(); + } + + public function getName(): string + { + return 'neo4j'; + } + + public function getFailedStatements(): array + { + return $this->data['failed_statements']; + } + + public function getSuccessfulStatements(): array + { + return $this->data['successful_statements']; + } + + public function getQueryCount(): int + { + return count($this->data['successful_statements']) + count($this->data['failed_statements']); + } + + public static function getTemplate(): ?string + { + return 'web_profiler.html.twig'; + } +} diff --git a/src/Collector/Twig/Neo4jResultExtension.php b/src/Collector/Twig/Neo4jResultExtension.php new file mode 100644 index 0000000..20277f1 --- /dev/null +++ b/src/Collector/Twig/Neo4jResultExtension.php @@ -0,0 +1,56 @@ + + */ +class Neo4jResultExtension extends AbstractExtension +{ + /** + * @return array + */ + public function getFilters(): array + { + return [ + new TwigFilter('neo4jResult', [$this, 'getType']), + ]; + } + + public function getType(mixed $object): string + { + return $this->doGetType($object, true); + } + + public function getName(): string + { + return 'neo4j.result'; + } + + private function doGetType(mixed $object, bool $recursive): string + { + if ($object instanceof Node) { + return sprintf('%s: %s', $object->getId(), $object->getLabels()->join(', ')); + } + + if (is_array($object) && $recursive) { + if (empty($object)) { + return 'Empty array'; + } + $ret = []; + foreach ($object as $o) { + $ret[] = $this->doGetType($o, false); + } + + return sprintf('[%s]', implode(', ', $ret)); + } + + return get_debug_type($object); + } +} diff --git a/src/DependencyInjection/Neo4jExtension.php b/src/DependencyInjection/Neo4jExtension.php index bf491ec..f8617ae 100644 --- a/src/DependencyInjection/Neo4jExtension.php +++ b/src/DependencyInjection/Neo4jExtension.php @@ -4,12 +4,15 @@ namespace Neo4j\Neo4jBundle\DependencyInjection; +use Neo4j\Neo4jBundle\Collector\Neo4jDataCollector; +use Neo4j\Neo4jBundle\EventListener\Neo4jProfileListener; use Psr\Http\Client\ClientInterface; use Psr\Http\Message\RequestFactoryInterface; use Psr\Http\Message\StreamFactoryInterface; use Symfony\Component\Config\FileLocator; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Extension\Extension; use Symfony\Component\DependencyInjection\Loader\PhpFileLoader; use Symfony\Component\DependencyInjection\Reference; @@ -49,6 +52,20 @@ public function load(array $configs, ContainerBuilder $container): ContainerBuil } } + if (0 !== count($enabledProfiles)) { + $container->setDefinition('neo4j.data_collector', (new Definition(Neo4jDataCollector::class)) + ->setAutowired(true) + ->addTag('data_collector') + ); + + $container->setAlias(Neo4jProfileListener::class, 'neo4j.subscriber'); + + $container->setDefinition('neo4j.subscriber', (new Definition(Neo4jProfileListener::class)) + ->setArgument(0, $enabledProfiles) + ->addTag('kernel.event_subscriber') + ); + } + return $container; } diff --git a/src/EventListener/Neo4jProfileListener.php b/src/EventListener/Neo4jProfileListener.php new file mode 100644 index 0000000..b046b99 --- /dev/null +++ b/src/EventListener/Neo4jProfileListener.php @@ -0,0 +1,78 @@ + + */ + private array $profiledSummaries = []; + + /** + * @var list + */ + private array $profiledFailures = []; + + /** + * @param list $enabledProfiles + */ + public function __construct(private array $enabledProfiles = []) + { + } + + public static function getSubscribedEvents(): array + { + return [ + PostRunEvent::EVENT_ID => 'onPostRun', + FailureEvent::EVENT_ID => 'onFailure', + ]; + } + + public function onPostRun(PostRunEvent $event): void + { + if (in_array($event->getAlias(), $this->enabledProfiles)) { + $this->profiledSummaries[] = $event->getResult(); + } + } + + public function onFailure(FailureEvent $event): void + { + if (in_array($event->getAlias(), $this->enabledProfiles)) { + $this->profiledFailures[] = [ + 'exception' => $event->getException(), + 'statement' => $event->getStatement(), + 'alias' => $event->getAlias(), + ]; + } + } + + public function getProfiledSummaries(): array + { + return $this->profiledSummaries; + } + + /** + * @return list + */ + public function getProfiledFailures(): array + { + return $this->profiledFailures; + } + + public function reset(): void + { + $this->profiledFailures = []; + $this->profiledSummaries = []; + } +} diff --git a/tests/Unit/Collector/Twig/Neo4jResultExtensionTest.php b/tests/Unit/Collector/Twig/Neo4jResultExtensionTest.php new file mode 100644 index 0000000..07bc8f6 --- /dev/null +++ b/tests/Unit/Collector/Twig/Neo4jResultExtensionTest.php @@ -0,0 +1,65 @@ + + */ +class Neo4jResultExtensionTest extends TestCase +{ + public function testEmptyArray(): void + { + $o = new Neo4jResultExtension(); + $result = $o->getType([]); + + $this->assertEquals('Empty array', $result); + } + + public function testObject(): void + { + $o = new Neo4jResultExtension(); + $result = $o->getType($o); + + $this->assertEquals(Neo4jResultExtension::class, $result); + } + + public function testScalar(): void + { + $o = new Neo4jResultExtension(); + $result = $o->getType(3); + + $this->assertEquals('int', $result); + } + + public function testScalarArray(): void + { + $o = new Neo4jResultExtension(); + $result = $o->getType([3, 6.3]); + + $this->assertEquals('[int, float]', $result); + } + + public function testArrayArray(): void + { + $o = new Neo4jResultExtension(); + $result = $o->getType([[]]); + + $this->assertEquals('[array]', $result); + } + + public function testNode(): void + { + $o = new Neo4jResultExtension(); + $result = $o->getType(new Node(1, new CypherList(['Label']), new CypherMap(), null)); + + $this->assertEquals('1: Label', $result); + } +} diff --git a/tests/Unit/DependencyInjection/Neo4jExtensionTest.php b/tests/Unit/DependencyInjection/Neo4jExtensionTest.php new file mode 100644 index 0000000..572535d --- /dev/null +++ b/tests/Unit/DependencyInjection/Neo4jExtensionTest.php @@ -0,0 +1,57 @@ + + */ +class Neo4jExtensionTest extends AbstractExtensionTestCase +{ + protected function getMinimalConfiguration(): array + { + $this->setParameter('kernel.cache_dir', 'foo'); + + return ['drivers' => ['default' => ['dsn' => 'bolt://localhost']]]; + } + + public function testDataCollectorLoaded(): void + { + $this->setParameter('kernel.debug', true); + $this->load(); + + $this->assertContainerBuilderHasService('neo4j.data_collector', Neo4jDataCollector::class); + } + + public function testDataCollectorNotLoadedInNonDebug(): void + { + $this->setParameter('kernel.debug', false); + $this->load(); + + $this->assertContainerBuilderNotHasService('neo4j.data_collector'); + } + + public function testDataCollectorNotLoadedWhenDisabled(): void + { + $this->setParameter('kernel.debug', true); + $this->load(['drivers' => [ + 'default' => [ + 'profiling' => false, + ], + ]]); + + $this->assertContainerBuilderNotHasService('neo4j.neo4j_data_collector'); + } + + protected function getContainerExtensions(): array + { + return [ + new Neo4jExtension(), + ]; + } +} From ca80e25cfe5de84356d7869f6f357836d33ec02a Mon Sep 17 00:00:00 2001 From: exaby73 Date: Wed, 28 Aug 2024 11:43:47 +0530 Subject: [PATCH 05/25] test: Add failing test for profiler --- composer.json | 10 +++++--- tests/App/Controller/TestController.php | 26 ++++++++++++++++++++ tests/App/Controller/Twig/index.twig.html | 1 + tests/App/TestKernel.php | 2 ++ tests/App/config/default.yml | 16 ++++++++++++ tests/App/config/routes.yaml | 3 +++ tests/Functional/ProfilerTest.php | 30 +++++++++++++++++++++++ 7 files changed, 84 insertions(+), 4 deletions(-) create mode 100644 tests/App/Controller/TestController.php create mode 100644 tests/App/Controller/Twig/index.twig.html create mode 100644 tests/App/config/routes.yaml create mode 100644 tests/Functional/ProfilerTest.php diff --git a/composer.json b/composer.json index 63870d2..304c616 100644 --- a/composer.json +++ b/composer.json @@ -19,18 +19,20 @@ "symfony/config": "^5.4 || ^6.0 || ^7.0" }, "require-dev": { + "friendsofphp/php-cs-fixer": "^3.30", + "kubawerlos/php-cs-fixer-custom-fixers": "^3.0", "matthiasnoback/symfony-dependency-injection-test": "^4.3 || ^5.0", "phpunit/phpunit": "^9.5", + "psalm/plugin-phpunit": "^0.18", "psalm/plugin-symfony": "^5.0", "symfony/console": "^5.4 || ^6.0 || ^7.0", "symfony/framework-bundle": "^5.4 || ^6.0 || ^7.0", "symfony/http-kernel": "^5.4 || ^6.0 || ^7.0", + "symfony/routing": "^6.4", "symfony/test-pack": "^1.1", + "symfony/twig-bundle": "^6.4", "symfony/yaml": "^5.4 || ^6.0 || ^7.0", - "vimeo/psalm": "^5.15.0", - "kubawerlos/php-cs-fixer-custom-fixers": "^3.0", - "friendsofphp/php-cs-fixer": "^3.30", - "psalm/plugin-phpunit": "^0.18" + "vimeo/psalm": "^5.15.0" }, "autoload": { "psr-4": { diff --git a/tests/App/Controller/TestController.php b/tests/App/Controller/TestController.php new file mode 100644 index 0000000..271cb53 --- /dev/null +++ b/tests/App/Controller/TestController.php @@ -0,0 +1,26 @@ +client->run('MATCH (n) RETURN n'); + $this->client->run('MATCH (n) RETURN n'); + return $this->render('index.twig.html'); + } +} \ No newline at end of file diff --git a/tests/App/Controller/Twig/index.twig.html b/tests/App/Controller/Twig/index.twig.html new file mode 100644 index 0000000..ab4e38d --- /dev/null +++ b/tests/App/Controller/Twig/index.twig.html @@ -0,0 +1 @@ +

Hello Neo4j

\ No newline at end of file diff --git a/tests/App/TestKernel.php b/tests/App/TestKernel.php index d8192cd..1ad3f90 100644 --- a/tests/App/TestKernel.php +++ b/tests/App/TestKernel.php @@ -6,6 +6,7 @@ use Neo4j\Neo4jBundle\Neo4jBundle; use Symfony\Bundle\FrameworkBundle\FrameworkBundle; +use Symfony\Bundle\TwigBundle\TwigBundle; use Symfony\Component\Config\Loader\LoaderInterface; use Symfony\Component\HttpKernel\Kernel; @@ -15,6 +16,7 @@ public function registerBundles(): array { return [ new FrameworkBundle(), + new TwigBundle(), new Neo4jBundle(), ]; } diff --git a/tests/App/config/default.yml b/tests/App/config/default.yml index 323ae51..cef35ed 100644 --- a/tests/App/config/default.yml +++ b/tests/App/config/default.yml @@ -1,6 +1,22 @@ + +services: + Neo4j\Neo4jBundle\Tests\App\Controller\TestController: + public: true + autoconfigure: true + autowire: true + tags: ['controller.service_arguments'] + framework: secret: test test: true + profiler: { enabled: true, collect: false } + router: + resource: '%kernel.project_dir%/tests/App/config/routes.yaml' + type: 'yaml' + +twig: + paths: + - '%kernel.project_dir%/tests/App/Controller/Twig' parameters: neo4j.dsn.badname: bolt://localhost diff --git a/tests/App/config/routes.yaml b/tests/App/config/routes.yaml new file mode 100644 index 0000000..39a14d9 --- /dev/null +++ b/tests/App/config/routes.yaml @@ -0,0 +1,3 @@ +controllers: + resource: '../Controller/' + type: annotation \ No newline at end of file diff --git a/tests/Functional/ProfilerTest.php b/tests/Functional/ProfilerTest.php new file mode 100644 index 0000000..f6218b5 --- /dev/null +++ b/tests/Functional/ProfilerTest.php @@ -0,0 +1,30 @@ +enableProfiler(); + + $client->request('GET', '/'); + + $this->assertResponseIsSuccessful(); + if ($profile = $client->getProfile()) { + $this->assertEquals( + 2, + $profile->getCollector('neo4j')->getQueryCount() + ); + } + } +} \ No newline at end of file From f9e22173a30b5bd511400641d41dec2bc265dc66 Mon Sep 17 00:00:00 2001 From: exaby73 Date: Wed, 28 Aug 2024 11:48:02 +0530 Subject: [PATCH 06/25] ci: Run CI on all pull requests --- .github/workflows/static-analysis.yml | 2 -- .github/workflows/tests.yml | 2 -- 2 files changed, 4 deletions(-) diff --git a/.github/workflows/static-analysis.yml b/.github/workflows/static-analysis.yml index ca1cfde..d0c7cc7 100644 --- a/.github/workflows/static-analysis.yml +++ b/.github/workflows/static-analysis.yml @@ -4,8 +4,6 @@ on: branches: - master pull_request: - branches: - - master jobs: php-cs-fixer: diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 1aa931e..3058801 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -4,8 +4,6 @@ on: branches: - master pull_request: - branches: - - master jobs: build: From e8eaedbf94eb3fff6669e0281a9b76235391aad8 Mon Sep 17 00:00:00 2001 From: exaby73 Date: Wed, 28 Aug 2024 11:50:14 +0530 Subject: [PATCH 07/25] chore: Psalm and CS fixes --- tests/App/Controller/TestController.php | 7 +++---- tests/Functional/ProfilerTest.php | 7 +++++-- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/tests/App/Controller/TestController.php b/tests/App/Controller/TestController.php index 271cb53..8c3b0d0 100644 --- a/tests/App/Controller/TestController.php +++ b/tests/App/Controller/TestController.php @@ -3,8 +3,6 @@ namespace Neo4j\Neo4jBundle\Tests\App\Controller; use Laudis\Neo4j\Contracts\ClientInterface; -use Laudis\Neo4j\Neo4j\Neo4jDriver; -use Neo4j\Neo4jBundle\SymfonyClient; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Attribute\Route; @@ -16,11 +14,12 @@ public function __construct( ) { } - #[Route("/", methods: ["GET"])] + #[Route('/', methods: ['GET'])] public function index(): Response { $this->client->run('MATCH (n) RETURN n'); $this->client->run('MATCH (n) RETURN n'); + return $this->render('index.twig.html'); } -} \ No newline at end of file +} diff --git a/tests/Functional/ProfilerTest.php b/tests/Functional/ProfilerTest.php index f6218b5..139435b 100644 --- a/tests/Functional/ProfilerTest.php +++ b/tests/Functional/ProfilerTest.php @@ -2,6 +2,7 @@ namespace Functional; +use Neo4j\Neo4jBundle\Collector\Neo4jDataCollector; use Neo4j\Neo4jBundle\Tests\App\TestKernel; use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; @@ -21,10 +22,12 @@ public function testProfiler() $this->assertResponseIsSuccessful(); if ($profile = $client->getProfile()) { + /** @var Neo4jDataCollector $collector */ + $collector = $profile->getCollector('neo4j'); $this->assertEquals( 2, - $profile->getCollector('neo4j')->getQueryCount() + $collector->getQueryCount() ); } } -} \ No newline at end of file +} From c6e85ce64bd2a4130dbca35626105079e96be148 Mon Sep 17 00:00:00 2001 From: exaby73 Date: Wed, 28 Aug 2024 11:55:28 +0530 Subject: [PATCH 08/25] fix: Symfony routing and twig versions --- composer.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/composer.json b/composer.json index 304c616..da47ffc 100644 --- a/composer.json +++ b/composer.json @@ -28,9 +28,9 @@ "symfony/console": "^5.4 || ^6.0 || ^7.0", "symfony/framework-bundle": "^5.4 || ^6.0 || ^7.0", "symfony/http-kernel": "^5.4 || ^6.0 || ^7.0", - "symfony/routing": "^6.4", + "symfony/routing": "^5.4 || ^6.0 || ^7.0", "symfony/test-pack": "^1.1", - "symfony/twig-bundle": "^6.4", + "symfony/twig-bundle": "^5.4 || ^6.0 || ^7.0", "symfony/yaml": "^5.4 || ^6.0 || ^7.0", "vimeo/psalm": "^5.15.0" }, From 8dd1d667213c94e28ac60bbdb14ac19e59aad19a Mon Sep 17 00:00:00 2001 From: exaby73 Date: Wed, 28 Aug 2024 12:03:01 +0530 Subject: [PATCH 09/25] fix: Add invoke to TestController --- tests/App/Controller/TestController.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/App/Controller/TestController.php b/tests/App/Controller/TestController.php index 8c3b0d0..a59ca55 100644 --- a/tests/App/Controller/TestController.php +++ b/tests/App/Controller/TestController.php @@ -14,6 +14,11 @@ public function __construct( ) { } + public function __invoke(): Response + { + return $this->index(); + } + #[Route('/', methods: ['GET'])] public function index(): Response { From 36334aaa843c672465e41e74d919681df6ceb184 Mon Sep 17 00:00:00 2001 From: exaby73 Date: Wed, 28 Aug 2024 12:07:16 +0530 Subject: [PATCH 10/25] fix: Change route type to attribute --- tests/App/config/routes.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/App/config/routes.yaml b/tests/App/config/routes.yaml index 39a14d9..dab96bc 100644 --- a/tests/App/config/routes.yaml +++ b/tests/App/config/routes.yaml @@ -1,3 +1,3 @@ controllers: resource: '../Controller/' - type: annotation \ No newline at end of file + type: attribute \ No newline at end of file From 9186fd409f129e9f0265a31755544fdbae640408 Mon Sep 17 00:00:00 2001 From: exaby73 Date: Wed, 28 Aug 2024 12:16:15 +0530 Subject: [PATCH 11/25] refactor: Move to invoke routing --- tests/App/Controller/TestController.php | 7 ------- tests/App/config/routes.yaml | 6 +++--- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/tests/App/Controller/TestController.php b/tests/App/Controller/TestController.php index a59ca55..1cd54c9 100644 --- a/tests/App/Controller/TestController.php +++ b/tests/App/Controller/TestController.php @@ -5,7 +5,6 @@ use Laudis\Neo4j\Contracts\ClientInterface; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Response; -use Symfony\Component\Routing\Attribute\Route; class TestController extends AbstractController { @@ -15,12 +14,6 @@ public function __construct( } public function __invoke(): Response - { - return $this->index(); - } - - #[Route('/', methods: ['GET'])] - public function index(): Response { $this->client->run('MATCH (n) RETURN n'); $this->client->run('MATCH (n) RETURN n'); diff --git a/tests/App/config/routes.yaml b/tests/App/config/routes.yaml index dab96bc..6e6640d 100644 --- a/tests/App/config/routes.yaml +++ b/tests/App/config/routes.yaml @@ -1,3 +1,3 @@ -controllers: - resource: '../Controller/' - type: attribute \ No newline at end of file +test: + path: / + controller: Neo4j\Neo4jBundle\Tests\App\Controller\TestController \ No newline at end of file From 5dda8b9352a3bd27652a20c108ade84a10ad7d7d Mon Sep 17 00:00:00 2001 From: exaby73 Date: Wed, 28 Aug 2024 18:10:20 +0530 Subject: [PATCH 12/25] wip: Profiler --- Dockerfile | 6 ++- bin/console.php | 2 +- composer.json | 2 + config/services.php | 4 -- docker-compose.yml | 1 + src/Collector/Neo4jDataCollector.php | 2 +- src/DependencyInjection/Neo4jExtension.php | 48 ++++++++++++++++------ src/EventHandler.php | 6 +-- tests/App/Controller/TestController.php | 14 +++++-- tests/App/Controller/Twig/base.html.twig | 27 ++++++++++++ tests/App/Controller/Twig/index.html.twig | 7 ++++ tests/App/Controller/Twig/index.twig.html | 1 - tests/App/TestKernel.php | 2 + tests/App/config/default.yml | 10 ++++- tests/App/config/routes.yaml | 9 +++- tests/App/index.php | 28 +++++++++++++ tests/Functional/ProfilerTest.php | 8 +++- 17 files changed, 145 insertions(+), 32 deletions(-) create mode 100644 tests/App/Controller/Twig/base.html.twig create mode 100644 tests/App/Controller/Twig/index.html.twig delete mode 100644 tests/App/Controller/Twig/index.twig.html create mode 100644 tests/App/index.php diff --git a/Dockerfile b/Dockerfile index 9596b04..d3f46b1 100644 --- a/Dockerfile +++ b/Dockerfile @@ -10,7 +10,9 @@ RUN apt-get update \ && docker-php-ext-enable xdebug \ && curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer -WORKDIR /opt/project - +RUN echo "xdebug.client_host=host.docker.internal" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini +RUN echo "xdebug.mode=debug,develop" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini +WORKDIR /opt/project +CMD ["php", "-S", "0.0.0.0:80", "-t", "/opt/project/tests/App"] diff --git a/bin/console.php b/bin/console.php index 645f886..6abacd8 100644 --- a/bin/console.php +++ b/bin/console.php @@ -5,6 +5,6 @@ require __DIR__ . '/../vendor/autoload.php'; -$console = new Application(new TestKernel('test', true)); +$console = new Application(new TestKernel($_ENV['APP_ENV'] ?? 'dev', true)); $console->run(); \ No newline at end of file diff --git a/composer.json b/composer.json index da47ffc..970d17c 100644 --- a/composer.json +++ b/composer.json @@ -29,8 +29,10 @@ "symfony/framework-bundle": "^5.4 || ^6.0 || ^7.0", "symfony/http-kernel": "^5.4 || ^6.0 || ^7.0", "symfony/routing": "^5.4 || ^6.0 || ^7.0", + "symfony/stopwatch": "^6.4", "symfony/test-pack": "^1.1", "symfony/twig-bundle": "^5.4 || ^6.0 || ^7.0", + "symfony/web-profiler-bundle": "^5.4 || ^6.0 || ^7.0", "symfony/yaml": "^5.4 || ^6.0 || ^7.0", "vimeo/psalm": "^5.15.0" }, diff --git a/config/services.php b/config/services.php index 8ec9920..20b3e6a 100644 --- a/config/services.php +++ b/config/services.php @@ -17,10 +17,6 @@ return static function (ContainerConfigurator $configurator) { $services = $configurator->services(); - $services->set('neo4j.event_handler', EventHandler::class) - ->autowire() - ->autoconfigure(); - $services->set('neo4j.client_factory', ClientFactory::class) ->args([ service('neo4j.event_handler'), diff --git a/docker-compose.yml b/docker-compose.yml index 8e5b808..a4bff42 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -16,6 +16,7 @@ services: - NEO4J_PORT=7687 - NEO4J_USER=neo4j - NEO4J_PASSWORD=testtest + - XDEBUG_CONFIG="client_host=host.docker.internal log=/tmp/xdebug.log" working_dir: /opt/project networks: - neo4j-symfony diff --git a/src/Collector/Neo4jDataCollector.php b/src/Collector/Neo4jDataCollector.php index 2e3f739..215aced 100644 --- a/src/Collector/Neo4jDataCollector.php +++ b/src/Collector/Neo4jDataCollector.php @@ -78,6 +78,6 @@ public function getQueryCount(): int public static function getTemplate(): ?string { - return 'web_profiler.html.twig'; + return 'data_collector/web_profiler.html.twig'; } } diff --git a/src/DependencyInjection/Neo4jExtension.php b/src/DependencyInjection/Neo4jExtension.php index f8617ae..27a610c 100644 --- a/src/DependencyInjection/Neo4jExtension.php +++ b/src/DependencyInjection/Neo4jExtension.php @@ -5,6 +5,7 @@ namespace Neo4j\Neo4jBundle\DependencyInjection; use Neo4j\Neo4jBundle\Collector\Neo4jDataCollector; +use Neo4j\Neo4jBundle\EventHandler; use Neo4j\Neo4jBundle\EventListener\Neo4jProfileListener; use Psr\Http\Client\ClientInterface; use Psr\Http\Message\RequestFactoryInterface; @@ -27,9 +28,16 @@ public function load(array $configs, ContainerBuilder $container): ContainerBuil $configuration = new Configuration(); $mergedConfig = $this->processConfiguration($configuration, $configs); - $loader = new PhpFileLoader($container, new FileLocator(__DIR__.'/../../config')); + $loader = new PhpFileLoader($container, new FileLocator(__DIR__ . '/../../config')); $loader->load('services.php'); + $defaultAlias = $mergedConfig['default_driver'] ?? $mergedConfig['drivers'][0]['alias'] ?? 'default'; + + $container->setDefinition('neo4j.event_handler', new Definition(EventHandler::class)) + ->setAutowired(true) + ->addTag('neo4j.event_handler') + ->setArgument(1, $defaultAlias); + $container->getDefinition('neo4j.client_factory') ->setArgument(1, $mergedConfig['default_driver_config'] ?? null) ->setArgument(2, $mergedConfig['default_session_config'] ?? null) @@ -37,32 +45,46 @@ public function load(array $configs, ContainerBuilder $container): ContainerBuil ->setArgument(4, $mergedConfig['drivers'] ?? []) ->setArgument(5, $mergedConfig['default_driver'] ?? null) ->setArgument(6, new Reference(ClientInterface::class, ContainerInterface::NULL_ON_INVALID_REFERENCE)) - ->setArgument(7, new Reference(StreamFactoryInterface::class, ContainerInterface::NULL_ON_INVALID_REFERENCE)) - ->setArgument(8, new Reference(RequestFactoryInterface::class, ContainerInterface::NULL_ON_INVALID_REFERENCE)) - ->setAbstract(false) - ; + ->setArgument( + 7, + new Reference(StreamFactoryInterface::class, ContainerInterface::NULL_ON_INVALID_REFERENCE) + ) + ->setArgument( + 8, + new Reference(RequestFactoryInterface::class, ContainerInterface::NULL_ON_INVALID_REFERENCE) + ) + ->setAbstract(false); $container->getDefinition('neo4j.driver') - ->setArgument(0, $mergedConfig['drivers']['alias'] ?? 'default'); + ->setArgument(0, $defaultAlias); $enabledProfiles = []; foreach ($mergedConfig['drivers'] as $driver) { - if (true === $driver['profiling'] || (null === $driver['profiling'] && $container->getParameter('kernel.debug'))) { + if (true === $driver['profiling'] || (null === $driver['profiling'] && $container->getParameter( + 'kernel.debug' + ))) { $enabledProfiles[] = $driver['alias']; } } if (0 !== count($enabledProfiles)) { - $container->setDefinition('neo4j.data_collector', (new Definition(Neo4jDataCollector::class)) - ->setAutowired(true) - ->addTag('data_collector') + $container->setDefinition( + 'neo4j.data_collector', + (new Definition(Neo4jDataCollector::class)) + ->setAutowired(true) + ->addTag('data_collector', [ + 'id' => Neo4jDataCollector::class, + 'priority' => 500, + ]) ); $container->setAlias(Neo4jProfileListener::class, 'neo4j.subscriber'); - $container->setDefinition('neo4j.subscriber', (new Definition(Neo4jProfileListener::class)) - ->setArgument(0, $enabledProfiles) - ->addTag('kernel.event_subscriber') + $container->setDefinition( + 'neo4j.subscriber', + (new Definition(Neo4jProfileListener::class)) + ->setArgument(0, $enabledProfiles) + ->addTag('kernel.event_subscriber') ); } diff --git a/src/EventHandler.php b/src/EventHandler.php index ce2a83c..3185890 100644 --- a/src/EventHandler.php +++ b/src/EventHandler.php @@ -24,7 +24,7 @@ class EventHandler { private ?EventDispatcherInterface $dispatcher; - public function __construct(?EventDispatcherInterface $dispatcher) + public function __construct(?EventDispatcherInterface $dispatcher, private readonly string $alias) { $this->dispatcher = $dispatcher; } @@ -46,9 +46,9 @@ public function handle(callable $runHandler, Statement $statement, ?string $alia try { $tbr = $runHandler($statement); - $this->dispatcher->dispatch(new PostRunEvent($alias, $tbr->getSummary()), PostRunEvent::EVENT_ID); + $this->dispatcher->dispatch(new PostRunEvent($alias ?? $this->alias, $tbr->getSummary()), PostRunEvent::EVENT_ID); } catch (Neo4jException $e) { - $event = new FailureEvent($alias, $statement, $e); + $event = new FailureEvent($alias ?? $this->alias, $statement, $e); $event = $this->dispatcher->dispatch($event, FailureEvent::EVENT_ID); if ($event->shouldThrowException()) { diff --git a/tests/App/Controller/TestController.php b/tests/App/Controller/TestController.php index 1cd54c9..131bbb4 100644 --- a/tests/App/Controller/TestController.php +++ b/tests/App/Controller/TestController.php @@ -5,6 +5,7 @@ use Laudis\Neo4j\Contracts\ClientInterface; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\Profiler\Profiler; class TestController extends AbstractController { @@ -13,11 +14,18 @@ public function __construct( ) { } - public function __invoke(): Response + public function __invoke(Profiler $profiler): Response { +// dd($profiler->loadProfile('0a1909')); + // Successful statement $this->client->run('MATCH (n) RETURN n'); - $this->client->run('MATCH (n) RETURN n'); + try { + // Failing statement + $this->client->run('MATCH (n) {x: $x}', ['x' => 1]); + } catch (\Exception $e) { + // ignore + } - return $this->render('index.twig.html'); + return $this->render('index.html.twig'); } } diff --git a/tests/App/Controller/Twig/base.html.twig b/tests/App/Controller/Twig/base.html.twig new file mode 100644 index 0000000..7d88c9e --- /dev/null +++ b/tests/App/Controller/Twig/base.html.twig @@ -0,0 +1,27 @@ + + + + + {% block title %}My Application{% endblock %} + {% block stylesheets %} + {# Include your stylesheets here #} + {% endblock %} + + +
+ {# Include your navigation/header here #} +
+ +
+ {% block body %}{% endblock %} +
+ +
+ {# Include your footer here #} +
+ +{% block javascripts %} +{# Include your JavaScripts here #} +{% endblock %} + + \ No newline at end of file diff --git a/tests/App/Controller/Twig/index.html.twig b/tests/App/Controller/Twig/index.html.twig new file mode 100644 index 0000000..fa75d44 --- /dev/null +++ b/tests/App/Controller/Twig/index.html.twig @@ -0,0 +1,7 @@ +{% extends 'base.html.twig' %} + +{% block title %}Hello Neo4j{% endblock %} + +{% block body %} +

Hello Neo4j

+{% endblock %} \ No newline at end of file diff --git a/tests/App/Controller/Twig/index.twig.html b/tests/App/Controller/Twig/index.twig.html deleted file mode 100644 index ab4e38d..0000000 --- a/tests/App/Controller/Twig/index.twig.html +++ /dev/null @@ -1 +0,0 @@ -

Hello Neo4j

\ No newline at end of file diff --git a/tests/App/TestKernel.php b/tests/App/TestKernel.php index 1ad3f90..7512766 100644 --- a/tests/App/TestKernel.php +++ b/tests/App/TestKernel.php @@ -7,6 +7,7 @@ use Neo4j\Neo4jBundle\Neo4jBundle; use Symfony\Bundle\FrameworkBundle\FrameworkBundle; use Symfony\Bundle\TwigBundle\TwigBundle; +use Symfony\Bundle\WebProfilerBundle\WebProfilerBundle; use Symfony\Component\Config\Loader\LoaderInterface; use Symfony\Component\HttpKernel\Kernel; @@ -17,6 +18,7 @@ public function registerBundles(): array return [ new FrameworkBundle(), new TwigBundle(), + new WebProfilerBundle(), new Neo4jBundle(), ]; } diff --git a/tests/App/config/default.yml b/tests/App/config/default.yml index cef35ed..2218dac 100644 --- a/tests/App/config/default.yml +++ b/tests/App/config/default.yml @@ -5,18 +5,26 @@ services: autoconfigure: true autowire: true tags: ['controller.service_arguments'] + Symfony\Component\HttpKernel\Profiler\Profiler: '@profiler' + Symfony\Component\HttpKernel\EventListener\ProfilerListener: '@profiler_listener' framework: secret: test test: true - profiler: { enabled: true, collect: false } + profiler: { enabled: true, collect: true } router: resource: '%kernel.project_dir%/tests/App/config/routes.yaml' type: 'yaml' twig: + debug: "%kernel.debug%" paths: - '%kernel.project_dir%/tests/App/Controller/Twig' + - '%kernel.project_dir%/templates' + +web_profiler: + toolbar: true + intercept_redirects: false parameters: neo4j.dsn.badname: bolt://localhost diff --git a/tests/App/config/routes.yaml b/tests/App/config/routes.yaml index 6e6640d..e5fd9fa 100644 --- a/tests/App/config/routes.yaml +++ b/tests/App/config/routes.yaml @@ -1,3 +1,10 @@ test: path: / - controller: Neo4j\Neo4jBundle\Tests\App\Controller\TestController \ No newline at end of file + controller: Neo4j\Neo4jBundle\Tests\App\Controller\TestController + +web_profiler_wdt: + resource: '@WebProfilerBundle/Resources/config/routing/wdt.xml' + prefix: /_wdt +web_profiler_profiler: + resource: '@WebProfilerBundle/Resources/config/routing/profiler.xml' + prefix: /_profiler \ No newline at end of file diff --git a/tests/App/index.php b/tests/App/index.php new file mode 100644 index 0000000..3f89bde --- /dev/null +++ b/tests/App/index.php @@ -0,0 +1,28 @@ +boot(); +$request = Request::createFromGlobals(); +$response = $kernel->handle($request); +if ($kernel->getContainer()->has('profiler')) { + /** @var Symfony\Component\HttpKernel\Profiler\Profiler $profiler */ + $profiler = $kernel->getContainer()->get('profiler'); + $profile = $profiler->collect($request, $response); + if (null === $profile) { + error_log('Profiler token was not generated!!!'); + } else { + error_log('Profiler token: '.$profile->getToken()); + } +} else { + error_log('Profiler service not found in container'); +} +$response->send(); +$kernel->terminate($request, $response); diff --git a/tests/Functional/ProfilerTest.php b/tests/Functional/ProfilerTest.php index 139435b..ebe0125 100644 --- a/tests/Functional/ProfilerTest.php +++ b/tests/Functional/ProfilerTest.php @@ -1,6 +1,6 @@ enableProfiler(); + // Calls Neo4j\Neo4jBundle\Tests\App\Controller\TestController::__invoke $client->request('GET', '/'); - $this->assertResponseIsSuccessful(); if ($profile = $client->getProfile()) { /** @var Neo4jDataCollector $collector */ $collector = $profile->getCollector('neo4j'); @@ -28,6 +28,10 @@ public function testProfiler() 2, $collector->getQueryCount() ); + $successfulStatements = $collector->getSuccessfulStatements(); + $failedStatements = $collector->getFailedStatements(); + $this->assertCount(1, $successfulStatements); + $this->assertCount(1, $failedStatements); } } } From 5e5f531f09e9520d6b399a6ca9327f9d8131d316 Mon Sep 17 00:00:00 2001 From: Ghlen Nagels Date: Wed, 28 Aug 2024 19:10:36 +0530 Subject: [PATCH 13/25] working proof of concept --- docker-compose.yml | 4 +- src/Collector/Neo4jDataCollector.php | 2 +- src/DependencyInjection/Neo4jExtension.php | 2 +- .../Resources/views}/web_profiler.html.twig | 42 +++++++++---------- tests/App/config/default.yml | 1 - tests/App/config/routes.yaml | 2 +- 6 files changed, 27 insertions(+), 26 deletions(-) rename {templates => src/Resources/views}/web_profiler.html.twig (75%) diff --git a/docker-compose.yml b/docker-compose.yml index a4bff42..b558b1c 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -3,7 +3,7 @@ networks: services: app: - user: root:${UID-1000}:${GID-1000} + user: ${UID-1000}:${GID-1000} build: context: . ports: @@ -18,6 +18,8 @@ services: - NEO4J_PASSWORD=testtest - XDEBUG_CONFIG="client_host=host.docker.internal log=/tmp/xdebug.log" working_dir: /opt/project + extra_hosts: + - "host.docker.internal:host-gateway" networks: - neo4j-symfony diff --git a/src/Collector/Neo4jDataCollector.php b/src/Collector/Neo4jDataCollector.php index 215aced..a62e09e 100644 --- a/src/Collector/Neo4jDataCollector.php +++ b/src/Collector/Neo4jDataCollector.php @@ -78,6 +78,6 @@ public function getQueryCount(): int public static function getTemplate(): ?string { - return 'data_collector/web_profiler.html.twig'; + return '@Neo4j/web_profiler.html.twig'; } } diff --git a/src/DependencyInjection/Neo4jExtension.php b/src/DependencyInjection/Neo4jExtension.php index 27a610c..e1dee30 100644 --- a/src/DependencyInjection/Neo4jExtension.php +++ b/src/DependencyInjection/Neo4jExtension.php @@ -73,7 +73,7 @@ public function load(array $configs, ContainerBuilder $container): ContainerBuil (new Definition(Neo4jDataCollector::class)) ->setAutowired(true) ->addTag('data_collector', [ - 'id' => Neo4jDataCollector::class, + 'id' => 'neo4j', 'priority' => 500, ]) ); diff --git a/templates/web_profiler.html.twig b/src/Resources/views/web_profiler.html.twig similarity index 75% rename from templates/web_profiler.html.twig rename to src/Resources/views/web_profiler.html.twig index 8393225..81bfeb6 100644 --- a/templates/web_profiler.html.twig +++ b/src/Resources/views/web_profiler.html.twig @@ -4,7 +4,7 @@ {% block toolbar %} {% if collector.queryCount > 0 %} {% set icon %} - {{ include('@Neo4j/neo4j.svg') }} +{# {{ include('@Neo4j/public/images/neo4j.svg') }}#} {{ collector.queryCount }} stmt. {% endset %} @@ -22,7 +22,7 @@ {% endif %}
Total time - {{ '%0.2f'|format(collector.time) }}ms +{# {{ '%0.2f'|format(collector.time) }}ms#}
{% endset %} @@ -30,17 +30,17 @@ {% endif %} {% endblock %} -{% block head %} - - - {{ parent() }} -{% endblock %} +{#{% block head %}#} +{# #} +{# #} +{# {{ parent() }}#} +{#{% endblock %}#} {% block menu %} {# This left-hand menu appears when using the full-screen profiler. #} - {{ include('@Neo4j/Icon/neo4j.svg') }} +{# {{ include('@Neo4j/Icon/neo4j.svg') }}#} Neo4j {% if collector.failedStatements|length %} @@ -68,7 +68,7 @@ {% set start_time = statement.start_time|default(null) %} {% set end_time = statement.end_time|default(null) %} - {{ idx + 1 }} + {{ idx + 1 }} {% if start_time is not null and end_time is not null %}{{ '%0.2f'|format(end_time - start_time) }}ms{% endif %}
@@ -85,24 +85,24 @@
Tag: {{ statement.tag|default('N/A')|yaml_encode }}
- {% if statement.success %} -
- Number of results: {{ statement.nb_results }} -
-
- Scheme: {{ statement.scheme }} -
+{# {% if statement.success %}#}
- Statistics: {{ statement.statistics|yaml_encode }} + Number of results: {{ statement.nb_results|default('N/A') }}
- {% else %}
- Type: {{ statement.exceptionCode }} + Scheme: {{ statement.scheme|default('N/A') }}
- message: {{ statement.exceptionMessage }} + Statistics: {{ statement|yaml_encode }}
- {% endif %} +{# {% else %}#} +{#
#} +{# Type: {{ statement.exceptionCode }}#} +{#
#} +{#
#} +{# message: {{ statement.exceptionMessage }}#} +{#
.suc#} +{# {% endif %}#}
diff --git a/tests/App/config/default.yml b/tests/App/config/default.yml index 2218dac..4b7102d 100644 --- a/tests/App/config/default.yml +++ b/tests/App/config/default.yml @@ -20,7 +20,6 @@ twig: debug: "%kernel.debug%" paths: - '%kernel.project_dir%/tests/App/Controller/Twig' - - '%kernel.project_dir%/templates' web_profiler: toolbar: true diff --git a/tests/App/config/routes.yaml b/tests/App/config/routes.yaml index e5fd9fa..62a3882 100644 --- a/tests/App/config/routes.yaml +++ b/tests/App/config/routes.yaml @@ -7,4 +7,4 @@ web_profiler_wdt: prefix: /_wdt web_profiler_profiler: resource: '@WebProfilerBundle/Resources/config/routing/profiler.xml' - prefix: /_profiler \ No newline at end of file + prefix: /_profiler4 \ No newline at end of file From e24a8b7f5f8a75d45115ef3a2e313fa21b025ce9 Mon Sep 17 00:00:00 2001 From: exaby73 Date: Thu, 12 Sep 2024 06:00:32 +0530 Subject: [PATCH 14/25] feat: Add profiler integration --- config/services.php | 1 - src/Collector/Neo4jDataCollector.php | 86 +++++++++++++++++----- src/DependencyInjection/Neo4jExtension.php | 6 +- src/Event/FailureEvent.php | 7 +- src/Event/PostRunEvent.php | 8 +- src/Event/PreRunEvent.php | 7 +- src/EventHandler.php | 13 +++- src/EventListener/Neo4jProfileListener.php | 23 +++++- src/Resources/views/web_profiler.html.twig | 40 +++++++--- tests/App/Controller/TestController.php | 2 +- 10 files changed, 152 insertions(+), 41 deletions(-) diff --git a/config/services.php b/config/services.php index 20b3e6a..59dd13f 100644 --- a/config/services.php +++ b/config/services.php @@ -7,7 +7,6 @@ use Laudis\Neo4j\Contracts\SessionInterface; use Laudis\Neo4j\Contracts\TransactionInterface; use Neo4j\Neo4jBundle\ClientFactory; -use Neo4j\Neo4jBundle\EventHandler; use Neo4j\Neo4jBundle\EventListener\Neo4jProfileListener; use Neo4j\Neo4jBundle\SymfonyClient; use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; diff --git a/src/Collector/Neo4jDataCollector.php b/src/Collector/Neo4jDataCollector.php index a62e09e..5457264 100644 --- a/src/Collector/Neo4jDataCollector.php +++ b/src/Collector/Neo4jDataCollector.php @@ -4,7 +4,6 @@ namespace Neo4j\Neo4jBundle\Collector; -use Laudis\Neo4j\Databags\ResultSummary; use Neo4j\Neo4jBundle\EventListener\Neo4jProfileListener; use Symfony\Bundle\FrameworkBundle\DataCollector\AbstractDataCollector; use Symfony\Component\HttpFoundation\Request; @@ -12,31 +11,54 @@ /** * @var array{ - * successful_statements: array>, - * failed_statements: list + * successful_statements_count: int, + * failed_statements_count: int, + * statements: array> | list, * } $data */ final class Neo4jDataCollector extends AbstractDataCollector { public function __construct( - private Neo4jProfileListener $subscriber + private readonly Neo4jProfileListener $subscriber ) { } public function collect(Request $request, Response $response, ?\Throwable $exception = null): void { - $this->data['successful_statements'] = array_map( - static fn (ResultSummary $summary) => $summary->toArray(), - $this->subscriber->getProfiledSummaries() + $profiledSummaries = $this->subscriber->getProfiledSummaries(); + $successfulStatements = array_map( + static function (string $key, mixed $value) { + if ('result' !== $key) { + return [...$value, 'status' => 'success']; + } + + return array_map( + static function (string $key, mixed $obj) { + if (is_object($obj) && method_exists($obj, 'toArray')) { + return $obj->toArray(); + } + + return $obj; + }, + $value['result']->toArray() + ); + }, + array_keys($profiledSummaries), + array_values($profiledSummaries) ); - $this->data['failed_statements'] = array_map( + $failedStatements = array_map( static fn (array $x) => [ - 'statement' => $x['statement']->toArray(), + 'status' => 'failure', + 'time' => $x['time'], + 'timestamp' => $x['timestamp'], + 'result' => [ + 'statement' => $x['statement']->toArray(), + ], 'exception' => [ 'code' => $x['exception']->getErrors()[0]->getCode(), 'message' => $x['exception']->getErrors()[0]->getMessage(), @@ -48,6 +70,15 @@ public function collect(Request $request, Response $response, ?\Throwable $excep ], $this->subscriber->getProfiledFailures() ); + + $this->data['successful_statements_count'] = count($successfulStatements); + $this->data['failed_statements_count'] = count($failedStatements); + $mergedArray = array_merge($successfulStatements, $failedStatements); + uasort( + $mergedArray, + static fn (array $a, array $b) => $a['start_time'] <=> $b['timestamp'] + ); + $this->data['statements'] = $mergedArray; } public function reset(): void @@ -61,19 +92,40 @@ public function getName(): string return 'neo4j'; } - public function getFailedStatements(): array + public function getStatements(): array { - return $this->data['failed_statements']; + return $this->data['statements']; } public function getSuccessfulStatements(): array { - return $this->data['successful_statements']; + return array_filter( + $this->data['statements'], + static fn (array $x) => 'success' === $x['status'] + ); + } + + public function getFailedStatements(): array + { + return array_filter( + $this->data['statements'], + static fn (array $x) => 'failure' === $x['status'] + ); + } + + public function getFailedStatementsCount(): array + { + return $this->data['failed_statements_count']; + } + + public function getSuccessfulStatementsCount(): array + { + return $this->data['successful_statements_count']; } public function getQueryCount(): int { - return count($this->data['successful_statements']) + count($this->data['failed_statements']); + return count($this->data['statements']); } public static function getTemplate(): ?string diff --git a/src/DependencyInjection/Neo4jExtension.php b/src/DependencyInjection/Neo4jExtension.php index e1dee30..d366309 100644 --- a/src/DependencyInjection/Neo4jExtension.php +++ b/src/DependencyInjection/Neo4jExtension.php @@ -28,7 +28,7 @@ public function load(array $configs, ContainerBuilder $container): ContainerBuil $configuration = new Configuration(); $mergedConfig = $this->processConfiguration($configuration, $configs); - $loader = new PhpFileLoader($container, new FileLocator(__DIR__ . '/../../config')); + $loader = new PhpFileLoader($container, new FileLocator(__DIR__.'/../../config')); $loader->load('services.php'); $defaultAlias = $mergedConfig['default_driver'] ?? $mergedConfig['drivers'][0]['alias'] ?? 'default'; @@ -61,8 +61,8 @@ public function load(array $configs, ContainerBuilder $container): ContainerBuil $enabledProfiles = []; foreach ($mergedConfig['drivers'] as $driver) { if (true === $driver['profiling'] || (null === $driver['profiling'] && $container->getParameter( - 'kernel.debug' - ))) { + 'kernel.debug' + ))) { $enabledProfiles[] = $driver['alias']; } } diff --git a/src/Event/FailureEvent.php b/src/Event/FailureEvent.php index 2981193..fb5c5bd 100644 --- a/src/Event/FailureEvent.php +++ b/src/Event/FailureEvent.php @@ -14,7 +14,7 @@ class FailureEvent extends Event protected bool $shouldThrowException = true; - public function __construct(private ?string $alias, private Statement $statement, private Neo4jException $exception) + public function __construct(private ?string $alias, private Statement $statement, private Neo4jException $exception, private \DateTimeInterface $time) { } @@ -33,6 +33,11 @@ public function shouldThrowException(): bool return $this->shouldThrowException; } + public function getTime(): \DateTimeInterface + { + return $this->time; + } + public function getAlias(): ?string { return $this->alias; diff --git a/src/Event/PostRunEvent.php b/src/Event/PostRunEvent.php index b0bf84b..05a0afb 100644 --- a/src/Event/PostRunEvent.php +++ b/src/Event/PostRunEvent.php @@ -13,7 +13,8 @@ class PostRunEvent extends Event public function __construct( private ?string $alias, - private ResultSummary $result + private ResultSummary $result, + private \DateTimeInterface $time ) { } @@ -22,6 +23,11 @@ public function getResult(): ResultSummary return $this->result; } + public function getTime(): \DateTimeInterface + { + return $this->time; + } + public function getAlias(): ?string { return $this->alias; diff --git a/src/Event/PreRunEvent.php b/src/Event/PreRunEvent.php index 6ab3f4d..3bf873f 100644 --- a/src/Event/PreRunEvent.php +++ b/src/Event/PreRunEvent.php @@ -11,7 +11,7 @@ class PreRunEvent extends Event { public const EVENT_ID = 'neo4j.pre_run'; - public function __construct(private ?string $alias, private Statement $statement) + public function __construct(private ?string $alias, private Statement $statement, private \DateTimeInterface $time) { } @@ -20,6 +20,11 @@ public function getStatement(): Statement return $this->statement; } + public function getTime(): \DateTimeInterface + { + return $this->time; + } + public function getAlias(): ?string { return $this->alias; diff --git a/src/EventHandler.php b/src/EventHandler.php index 3185890..01ec3f3 100644 --- a/src/EventHandler.php +++ b/src/EventHandler.php @@ -42,13 +42,20 @@ public function handle(callable $runHandler, Statement $statement, ?string $alia return $runHandler($statement); } - $this->dispatcher->dispatch(new PreRunEvent($alias, $statement), PreRunEvent::EVENT_ID); + /** @noinspection PhpUnhandledExceptionInspection */ + $time = new \DateTimeImmutable('now', new \DateTimeZone(date_default_timezone_get())); + $this->dispatcher->dispatch(new PreRunEvent($alias, $statement, $time), PreRunEvent::EVENT_ID); try { $tbr = $runHandler($statement); - $this->dispatcher->dispatch(new PostRunEvent($alias ?? $this->alias, $tbr->getSummary()), PostRunEvent::EVENT_ID); + $this->dispatcher->dispatch( + new PostRunEvent($alias ?? $this->alias, $tbr->getSummary(), $time), + PostRunEvent::EVENT_ID + ); } catch (Neo4jException $e) { - $event = new FailureEvent($alias ?? $this->alias, $statement, $e); + /** @noinspection PhpUnhandledExceptionInspection */ + $time = new \DateTimeImmutable('now', new \DateTimeZone(date_default_timezone_get())); + $event = new FailureEvent($alias ?? $this->alias, $statement, $e, $time); $event = $this->dispatcher->dispatch($event, FailureEvent::EVENT_ID); if ($event->shouldThrowException()) { diff --git a/src/EventListener/Neo4jProfileListener.php b/src/EventListener/Neo4jProfileListener.php index b046b99..ddeb023 100644 --- a/src/EventListener/Neo4jProfileListener.php +++ b/src/EventListener/Neo4jProfileListener.php @@ -4,6 +4,7 @@ namespace Neo4j\Neo4jBundle\EventListener; +use DateTimeInterface; use Laudis\Neo4j\Databags\ResultSummary; use Laudis\Neo4j\Databags\Statement; use Laudis\Neo4j\Exception\Neo4jException; @@ -15,7 +16,11 @@ final class Neo4jProfileListener implements EventSubscriberInterface, ResetInterface { /** - * @var list + * @var list */ private array $profiledSummaries = []; @@ -27,7 +32,7 @@ final class Neo4jProfileListener implements EventSubscriberInterface, ResetInter /** * @param list $enabledProfiles */ - public function __construct(private array $enabledProfiles = []) + public function __construct(private readonly array $enabledProfiles = []) { } @@ -42,17 +47,29 @@ public static function getSubscribedEvents(): array public function onPostRun(PostRunEvent $event): void { if (in_array($event->getAlias(), $this->enabledProfiles)) { - $this->profiledSummaries[] = $event->getResult(); + $time = $event->getTime(); + $result = $event->getResult(); + $end_time = $time->getTimestamp() + $result->getResultAvailableAfter() + $result->getResultConsumedAfter(); + $this->profiledSummaries[] = [ + 'result' => $event->getResult(), + 'alias' => $event->getAlias(), + 'time' => $time->format('Y-m-d H:i:s'), + 'start_time' => $time->getTimestamp(), + 'end_time' => $end_time, + ]; } } public function onFailure(FailureEvent $event): void { if (in_array($event->getAlias(), $this->enabledProfiles)) { + $time = $event->getTime(); $this->profiledFailures[] = [ 'exception' => $event->getException(), 'statement' => $event->getStatement(), 'alias' => $event->getAlias(), + 'time' => $time->format('Y-m-d H:i:s'), + 'timestamp' => $time->getTimestamp(), ]; } } diff --git a/src/Resources/views/web_profiler.html.twig b/src/Resources/views/web_profiler.html.twig index 81bfeb6..3fea0f5 100644 --- a/src/Resources/views/web_profiler.html.twig +++ b/src/Resources/views/web_profiler.html.twig @@ -60,19 +60,25 @@ # Status Query + Query Time + Query Type + Database + Executed At + Exception Title - {% for idx, statement in collector.successfulStatements %} + {% for idx, statement in collector.statements %} + {% set status = statement.status|default('unknown') %} {% set start_time = statement.start_time|default(null) %} {% set end_time = statement.end_time|default(null) %} {{ idx + 1 }} - {% if start_time is not null and end_time is not null %}{{ '%0.2f'|format(end_time - start_time) }}ms{% endif %} + {{ statement.status }}
- {{ statement.query|default('') }} + {{ statement.result.statement.text|default('') }}
View details @@ -80,31 +86,45 @@
- Parameters: {{ statement.parameters|default([])|yaml_encode }} + Parameters: {{ statement.result.parameters|default([])|yaml_encode }}
- Tag: {{ statement.tag|default('N/A')|yaml_encode }} + Tag: {{ statement.result.tag|default('N/A')|yaml_encode }}
-{# {% if statement.success %}#} +{# {% if statement.result.success %}#}
- Number of results: {{ statement.nb_results|default('N/A') }} + Number of results: {{ statement.result.nb_results|default('N/A') }}
- Scheme: {{ statement.scheme|default('N/A') }} + Scheme: {{ statement.result.scheme|default('N/A') }}
Statistics: {{ statement|yaml_encode }}
{# {% else %}#} {#
#} -{# Type: {{ statement.exceptionCode }}#} +{# Type: {{ statement.result.exceptionCode }}#} {#
#} {#
#} -{# message: {{ statement.exceptionMessage }}#} +{# message: {{ statement.result.exceptionMessage }}#} {#
.suc#} {# {% endif %}#}
+ {% if status is same as('success') %}{% if start_time is not null and end_time is not null %}{{ '%0.2f'|format(end_time - start_time) }}ms{% endif %}{% else %}N/A{% endif %} + {% if status is same as('success') %} + {{ statement.result.queryType }} + {{ statement.result.databaseInfo.name }} + {% else %} + N/A + N/A + {% endif %} + {{ statement.time }} + {% if status is same as('success') %} + N/A + {% else %} + {{ statement.exception.title }} + {% endif %} {% endfor %} diff --git a/tests/App/Controller/TestController.php b/tests/App/Controller/TestController.php index 131bbb4..34734ae 100644 --- a/tests/App/Controller/TestController.php +++ b/tests/App/Controller/TestController.php @@ -16,7 +16,7 @@ public function __construct( public function __invoke(Profiler $profiler): Response { -// dd($profiler->loadProfile('0a1909')); + // dd($profiler->loadProfile('0a1909')); // Successful statement $this->client->run('MATCH (n) RETURN n'); try { From 85cca34c8fbd6b642bca039d6a02e8ecd5f153ed Mon Sep 17 00:00:00 2001 From: exaby73 Date: Thu, 12 Sep 2024 06:15:16 +0530 Subject: [PATCH 15/25] chore: CS and Psalm fixes --- src/Collector/Neo4jDataCollector.php | 9 ++++++--- src/Event/PostRunEvent.php | 6 +++--- src/EventListener/Neo4jProfileListener.php | 21 +++++++++++++++++---- src/SymfonyClient.php | 4 ++-- tests/App/Controller/TestController.php | 3 +-- 5 files changed, 29 insertions(+), 14 deletions(-) diff --git a/src/Collector/Neo4jDataCollector.php b/src/Collector/Neo4jDataCollector.php index 5457264..9edebff 100644 --- a/src/Collector/Neo4jDataCollector.php +++ b/src/Collector/Neo4jDataCollector.php @@ -32,12 +32,15 @@ public function collect(Request $request, Response $response, ?\Throwable $excep $profiledSummaries = $this->subscriber->getProfiledSummaries(); $successfulStatements = array_map( static function (string $key, mixed $value) { - if ('result' !== $key) { - return [...$value, 'status' => 'success']; + if ('result' !== $key && /* Is always array */ is_array($value)) { + return [ + ...$value, + 'status' => 'success', + ]; } return array_map( - static function (string $key, mixed $obj) { + static function (mixed $obj) { if (is_object($obj) && method_exists($obj, 'toArray')) { return $obj->toArray(); } diff --git a/src/Event/PostRunEvent.php b/src/Event/PostRunEvent.php index 05a0afb..0c7a43c 100644 --- a/src/Event/PostRunEvent.php +++ b/src/Event/PostRunEvent.php @@ -12,9 +12,9 @@ class PostRunEvent extends Event public const EVENT_ID = 'neo4j.post_run'; public function __construct( - private ?string $alias, - private ResultSummary $result, - private \DateTimeInterface $time + private readonly ?string $alias, + private readonly ResultSummary $result, + private readonly \DateTimeInterface $time ) { } diff --git a/src/EventListener/Neo4jProfileListener.php b/src/EventListener/Neo4jProfileListener.php index ddeb023..e5c91d7 100644 --- a/src/EventListener/Neo4jProfileListener.php +++ b/src/EventListener/Neo4jProfileListener.php @@ -4,7 +4,6 @@ namespace Neo4j\Neo4jBundle\EventListener; -use DateTimeInterface; use Laudis\Neo4j\Databags\ResultSummary; use Laudis\Neo4j\Databags\Statement; use Laudis\Neo4j\Exception\Neo4jException; @@ -19,13 +18,21 @@ final class Neo4jProfileListener implements EventSubscriberInterface, ResetInter * @var list */ private array $profiledSummaries = []; /** - * @var list + * @var list */ private array $profiledFailures = []; @@ -80,7 +87,13 @@ public function getProfiledSummaries(): array } /** - * @return list + * @return list */ public function getProfiledFailures(): array { diff --git a/src/SymfonyClient.php b/src/SymfonyClient.php index 615f748..2290e36 100644 --- a/src/SymfonyClient.php +++ b/src/SymfonyClient.php @@ -25,8 +25,8 @@ class SymfonyClient implements ClientInterface * @param ClientInterface> $client */ public function __construct( - private ClientInterface $client, - private EventHandler $handler + private readonly ClientInterface $client, + private readonly EventHandler $handler ) { } diff --git a/tests/App/Controller/TestController.php b/tests/App/Controller/TestController.php index 34734ae..c9f7152 100644 --- a/tests/App/Controller/TestController.php +++ b/tests/App/Controller/TestController.php @@ -16,13 +16,12 @@ public function __construct( public function __invoke(Profiler $profiler): Response { - // dd($profiler->loadProfile('0a1909')); // Successful statement $this->client->run('MATCH (n) RETURN n'); try { // Failing statement $this->client->run('MATCH (n) {x: $x}', ['x' => 1]); - } catch (\Exception $e) { + } catch (\Exception) { // ignore } From ecf6c7697c79ae3788d184ec76ce6c768d441c9c Mon Sep 17 00:00:00 2001 From: exaby73 Date: Thu, 12 Sep 2024 06:18:44 +0530 Subject: [PATCH 16/25] feat: Upgrade static analysis PHP version to 8.3 --- .github/workflows/static-analysis.yml | 2 +- Dockerfile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/static-analysis.yml b/.github/workflows/static-analysis.yml index d0c7cc7..5b810e4 100644 --- a/.github/workflows/static-analysis.yml +++ b/.github/workflows/static-analysis.yml @@ -14,7 +14,7 @@ jobs: - uses: php-actions/composer@v6 with: progress: yes - php_version: 8.1 + php_version: 8.3 version: 2 - name: "PHP-CS-Fixer" run: composer check-cs diff --git a/Dockerfile b/Dockerfile index d3f46b1..e291592 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM php:8.1-cli +FROM php:8.3-cli RUN apt-get update \ && apt-get install -y \ libzip-dev \ From ae22ba995831edfc53b2905469d6f0064c94aa87 Mon Sep 17 00:00:00 2001 From: exaby73 Date: Sun, 15 Sep 2024 05:35:23 +0530 Subject: [PATCH 17/25] fix: Add recursive toArray, and add stopwatch events --- src/Collector/Neo4jDataCollector.php | 52 +++++++++++++--------- src/EventHandler.php | 12 ++++- src/Resources/views/web_profiler.html.twig | 4 +- tests/App/Controller/TestController.php | 5 ++- 4 files changed, 46 insertions(+), 27 deletions(-) diff --git a/src/Collector/Neo4jDataCollector.php b/src/Collector/Neo4jDataCollector.php index 9edebff..2676146 100644 --- a/src/Collector/Neo4jDataCollector.php +++ b/src/Collector/Neo4jDataCollector.php @@ -29,30 +29,21 @@ public function __construct( public function collect(Request $request, Response $response, ?\Throwable $exception = null): void { + $t = $this; $profiledSummaries = $this->subscriber->getProfiledSummaries(); - $successfulStatements = array_map( - static function (string $key, mixed $value) { - if ('result' !== $key && /* Is always array */ is_array($value)) { - return [ - ...$value, - 'status' => 'success', - ]; + $successfulStatements = []; + foreach ($profiledSummaries as $summary) { + $statement = ['status' => 'success']; + foreach ($summary as $key => $value) { + if (!is_array($value) && !is_object($value)) { + $statement[$key] = $value; + continue; } - return array_map( - static function (mixed $obj) { - if (is_object($obj) && method_exists($obj, 'toArray')) { - return $obj->toArray(); - } - - return $obj; - }, - $value['result']->toArray() - ); - }, - array_keys($profiledSummaries), - array_values($profiledSummaries) - ); + $statement[$key] = $t->recursiveToArray($value); + } + $successfulStatements[] = $statement; + } $failedStatements = array_map( static fn (array $x) => [ @@ -95,6 +86,7 @@ public function getName(): string return 'neo4j'; } + /** @api */ public function getStatements(): array { return $this->data['statements']; @@ -116,11 +108,13 @@ public function getFailedStatements(): array ); } + /** @api */ public function getFailedStatementsCount(): array { return $this->data['failed_statements_count']; } + /** @api */ public function getSuccessfulStatementsCount(): array { return $this->data['successful_statements_count']; @@ -135,4 +129,20 @@ public static function getTemplate(): ?string { return '@Neo4j/web_profiler.html.twig'; } + + private function recursiveToArray(mixed $obj): mixed + { + if (is_array($obj)) { + return array_map( + fn (mixed $x) => $this->recursiveToArray($x), + $obj + ); + } + + if (is_object($obj) && method_exists($obj, 'toArray')) { + return $obj->toArray(); + } + + return $obj; + } } diff --git a/src/EventHandler.php b/src/EventHandler.php index 01ec3f3..4e08a01 100644 --- a/src/EventHandler.php +++ b/src/EventHandler.php @@ -18,14 +18,18 @@ use Neo4j\Neo4jBundle\Event\FailureEvent; use Neo4j\Neo4jBundle\Event\PostRunEvent; use Neo4j\Neo4jBundle\Event\PreRunEvent; +use Symfony\Component\Stopwatch\Stopwatch; use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; class EventHandler { private ?EventDispatcherInterface $dispatcher; - public function __construct(?EventDispatcherInterface $dispatcher, private readonly string $alias) - { + public function __construct( + ?EventDispatcherInterface $dispatcher, + private readonly string $alias, + private readonly Stopwatch $stopwatch + ) { $this->dispatcher = $dispatcher; } @@ -46,13 +50,17 @@ public function handle(callable $runHandler, Statement $statement, ?string $alia $time = new \DateTimeImmutable('now', new \DateTimeZone(date_default_timezone_get())); $this->dispatcher->dispatch(new PreRunEvent($alias, $statement, $time), PreRunEvent::EVENT_ID); + $stopWatchName = sprintf('neo4j.%s.query', $alias ?? $this->alias); try { + $this->stopwatch->start($stopWatchName); $tbr = $runHandler($statement); + $this->stopwatch->stop($stopWatchName); $this->dispatcher->dispatch( new PostRunEvent($alias ?? $this->alias, $tbr->getSummary(), $time), PostRunEvent::EVENT_ID ); } catch (Neo4jException $e) { + $this->stopwatch->stop($stopWatchName); /** @noinspection PhpUnhandledExceptionInspection */ $time = new \DateTimeImmutable('now', new \DateTimeZone(date_default_timezone_get())); $event = new FailureEvent($alias ?? $this->alias, $statement, $e, $time); diff --git a/src/Resources/views/web_profiler.html.twig b/src/Resources/views/web_profiler.html.twig index 3fea0f5..5c288a7 100644 --- a/src/Resources/views/web_profiler.html.twig +++ b/src/Resources/views/web_profiler.html.twig @@ -86,7 +86,7 @@
- Parameters: {{ statement.result.parameters|default([])|yaml_encode }} + Parameters: {{ statement.result.statement.parameters|default([])|json_encode }}
Tag: {{ statement.result.tag|default('N/A')|yaml_encode }} @@ -99,7 +99,7 @@ Scheme: {{ statement.result.scheme|default('N/A') }}
- Statistics: {{ statement|yaml_encode }} + Statistics: {{ statement|json_encode }}
{# {% else %}#} {#
#} diff --git a/tests/App/Controller/TestController.php b/tests/App/Controller/TestController.php index c9f7152..41b0fbd 100644 --- a/tests/App/Controller/TestController.php +++ b/tests/App/Controller/TestController.php @@ -6,6 +6,7 @@ use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Profiler\Profiler; +use Symfony\Component\Stopwatch\Stopwatch; class TestController extends AbstractController { @@ -14,10 +15,10 @@ public function __construct( ) { } - public function __invoke(Profiler $profiler): Response + public function __invoke(Profiler $profiler, Stopwatch $stopwatch): Response { // Successful statement - $this->client->run('MATCH (n) RETURN n'); + $this->client->run('MATCH (n {foo: $bar}) RETURN n', ['bar' => 'baz']); try { // Failing statement $this->client->run('MATCH (n) {x: $x}', ['x' => 1]); From c7b2f505e4b48b4f190500d067942da54d9ca4f5 Mon Sep 17 00:00:00 2001 From: exaby73 Date: Sun, 15 Sep 2024 12:18:31 +0530 Subject: [PATCH 18/25] fix: nits --- .github/workflows/static-analysis.yml | 2 +- src/Collector/Neo4jDataCollector.php | 2 +- src/EventHandler.php | 8 ++++---- src/Resources/views/web_profiler.html.twig | 17 +---------------- 4 files changed, 7 insertions(+), 22 deletions(-) diff --git a/.github/workflows/static-analysis.yml b/.github/workflows/static-analysis.yml index 5b810e4..d0c7cc7 100644 --- a/.github/workflows/static-analysis.yml +++ b/.github/workflows/static-analysis.yml @@ -14,7 +14,7 @@ jobs: - uses: php-actions/composer@v6 with: progress: yes - php_version: 8.3 + php_version: 8.1 version: 2 - name: "PHP-CS-Fixer" run: composer check-cs diff --git a/src/Collector/Neo4jDataCollector.php b/src/Collector/Neo4jDataCollector.php index 2676146..db04cbd 100644 --- a/src/Collector/Neo4jDataCollector.php +++ b/src/Collector/Neo4jDataCollector.php @@ -134,7 +134,7 @@ private function recursiveToArray(mixed $obj): mixed { if (is_array($obj)) { return array_map( - fn (mixed $x) => $this->recursiveToArray($x), + fn(mixed $x) => $this->recursiveToArray($x), $obj ); } diff --git a/src/EventHandler.php b/src/EventHandler.php index 4e08a01..71ae12b 100644 --- a/src/EventHandler.php +++ b/src/EventHandler.php @@ -28,7 +28,7 @@ class EventHandler public function __construct( ?EventDispatcherInterface $dispatcher, private readonly string $alias, - private readonly Stopwatch $stopwatch + private readonly ?Stopwatch $stopwatch ) { $this->dispatcher = $dispatcher; } @@ -52,15 +52,15 @@ public function handle(callable $runHandler, Statement $statement, ?string $alia $stopWatchName = sprintf('neo4j.%s.query', $alias ?? $this->alias); try { - $this->stopwatch->start($stopWatchName); + $this->stopwatch?->start($stopWatchName); $tbr = $runHandler($statement); - $this->stopwatch->stop($stopWatchName); + $this->stopwatch?->stop($stopWatchName); $this->dispatcher->dispatch( new PostRunEvent($alias ?? $this->alias, $tbr->getSummary(), $time), PostRunEvent::EVENT_ID ); } catch (Neo4jException $e) { - $this->stopwatch->stop($stopWatchName); + $this->stopwatch?->stop($stopWatchName); /** @noinspection PhpUnhandledExceptionInspection */ $time = new \DateTimeImmutable('now', new \DateTimeZone(date_default_timezone_get())); $event = new FailureEvent($alias ?? $this->alias, $statement, $e, $time); diff --git a/src/Resources/views/web_profiler.html.twig b/src/Resources/views/web_profiler.html.twig index 5c288a7..c434925 100644 --- a/src/Resources/views/web_profiler.html.twig +++ b/src/Resources/views/web_profiler.html.twig @@ -89,26 +89,11 @@ Parameters: {{ statement.result.statement.parameters|default([])|json_encode }}
- Tag: {{ statement.result.tag|default('N/A')|yaml_encode }} -
-{# {% if statement.result.success %}#} -
- Number of results: {{ statement.result.nb_results|default('N/A') }} -
-
- Scheme: {{ statement.result.scheme|default('N/A') }} + Alias: {{ statement.alias|default('N/A') }}
Statistics: {{ statement|json_encode }}
-{# {% else %}#} -{#
#} -{# Type: {{ statement.result.exceptionCode }}#} -{#
#} -{#
#} -{# message: {{ statement.result.exceptionMessage }}#} -{#
.suc#} -{# {% endif %}#}
{% if status is same as('success') %}{% if start_time is not null and end_time is not null %}{{ '%0.2f'|format(end_time - start_time) }}ms{% endif %}{% else %}N/A{% endif %} From deeed45b3d779e50922641043506117528c6fc52 Mon Sep 17 00:00:00 2001 From: exaby73 Date: Mon, 16 Sep 2024 11:36:15 +0530 Subject: [PATCH 19/25] feat: Add scheme for future scope --- Dockerfile | 2 +- src/Collector/Neo4jDataCollector.php | 2 +- src/Event/FailureEvent.php | 15 +++++++-- src/Event/PostRunEvent.php | 8 ++++- src/Event/PreRunEvent.php | 14 +++++++-- src/EventHandler.php | 16 ++++++---- src/Resources/views/web_profiler.html.twig | 4 +++ src/SymfonyClient.php | 36 +++++++++++++++------- src/SymfonyTransaction.php | 13 ++++++-- 9 files changed, 83 insertions(+), 27 deletions(-) diff --git a/Dockerfile b/Dockerfile index e291592..d3f46b1 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM php:8.3-cli +FROM php:8.1-cli RUN apt-get update \ && apt-get install -y \ libzip-dev \ diff --git a/src/Collector/Neo4jDataCollector.php b/src/Collector/Neo4jDataCollector.php index db04cbd..2676146 100644 --- a/src/Collector/Neo4jDataCollector.php +++ b/src/Collector/Neo4jDataCollector.php @@ -134,7 +134,7 @@ private function recursiveToArray(mixed $obj): mixed { if (is_array($obj)) { return array_map( - fn(mixed $x) => $this->recursiveToArray($x), + fn (mixed $x) => $this->recursiveToArray($x), $obj ); } diff --git a/src/Event/FailureEvent.php b/src/Event/FailureEvent.php index fb5c5bd..e5892d9 100644 --- a/src/Event/FailureEvent.php +++ b/src/Event/FailureEvent.php @@ -14,8 +14,13 @@ class FailureEvent extends Event protected bool $shouldThrowException = true; - public function __construct(private ?string $alias, private Statement $statement, private Neo4jException $exception, private \DateTimeInterface $time) - { + public function __construct( + private readonly ?string $alias, + private readonly Statement $statement, + private readonly Neo4jException $exception, + private readonly \DateTimeInterface $time, + private readonly ?string $scheme + ) { } public function getException(): Neo4jException @@ -23,6 +28,7 @@ public function getException(): Neo4jException return $this->exception; } + /** @api */ public function disableException(): void { $this->shouldThrowException = false; @@ -47,4 +53,9 @@ public function getStatement(): Statement { return $this->statement; } + + public function getScheme(): ?string + { + return $this->scheme; + } } diff --git a/src/Event/PostRunEvent.php b/src/Event/PostRunEvent.php index 0c7a43c..e9d2d47 100644 --- a/src/Event/PostRunEvent.php +++ b/src/Event/PostRunEvent.php @@ -14,7 +14,8 @@ class PostRunEvent extends Event public function __construct( private readonly ?string $alias, private readonly ResultSummary $result, - private readonly \DateTimeInterface $time + private readonly \DateTimeInterface $time, + private readonly ?string $scheme ) { } @@ -32,4 +33,9 @@ public function getAlias(): ?string { return $this->alias; } + + public function getScheme(): ?string + { + return $this->scheme; + } } diff --git a/src/Event/PreRunEvent.php b/src/Event/PreRunEvent.php index 3bf873f..a653fe5 100644 --- a/src/Event/PreRunEvent.php +++ b/src/Event/PreRunEvent.php @@ -11,10 +11,15 @@ class PreRunEvent extends Event { public const EVENT_ID = 'neo4j.pre_run'; - public function __construct(private ?string $alias, private Statement $statement, private \DateTimeInterface $time) - { + public function __construct( + private readonly ?string $alias, + private readonly Statement $statement, + private readonly \DateTimeInterface $time, + private readonly ?string $scheme + ) { } + /** @api */ public function getStatement(): Statement { return $this->statement; @@ -29,4 +34,9 @@ public function getAlias(): ?string { return $this->alias; } + + public function getScheme(): ?string + { + return $this->scheme; + } } diff --git a/src/EventHandler.php b/src/EventHandler.php index 71ae12b..638cb3b 100644 --- a/src/EventHandler.php +++ b/src/EventHandler.php @@ -40,30 +40,34 @@ public function __construct( * * @return SummarizedResult */ - public function handle(callable $runHandler, Statement $statement, ?string $alias): SummarizedResult + public function handle(callable $runHandler, Statement $statement, ?string $alias, ?string $scheme): SummarizedResult { + $stopWatchName = sprintf('neo4j.%s.query', $alias ?? $this->alias); if (null === $this->dispatcher) { - return $runHandler($statement); + $this->stopwatch?->start($stopWatchName); + $result = $runHandler($statement); + $this->stopwatch?->stop($stopWatchName); + + return $result; } /** @noinspection PhpUnhandledExceptionInspection */ $time = new \DateTimeImmutable('now', new \DateTimeZone(date_default_timezone_get())); - $this->dispatcher->dispatch(new PreRunEvent($alias, $statement, $time), PreRunEvent::EVENT_ID); + $this->dispatcher->dispatch(new PreRunEvent($alias, $statement, $time, $scheme), PreRunEvent::EVENT_ID); - $stopWatchName = sprintf('neo4j.%s.query', $alias ?? $this->alias); try { $this->stopwatch?->start($stopWatchName); $tbr = $runHandler($statement); $this->stopwatch?->stop($stopWatchName); $this->dispatcher->dispatch( - new PostRunEvent($alias ?? $this->alias, $tbr->getSummary(), $time), + new PostRunEvent($alias ?? $this->alias, $tbr->getSummary(), $time, $scheme), PostRunEvent::EVENT_ID ); } catch (Neo4jException $e) { $this->stopwatch?->stop($stopWatchName); /** @noinspection PhpUnhandledExceptionInspection */ $time = new \DateTimeImmutable('now', new \DateTimeZone(date_default_timezone_get())); - $event = new FailureEvent($alias ?? $this->alias, $statement, $e, $time); + $event = new FailureEvent($alias ?? $this->alias, $statement, $e, $time, $scheme); $event = $this->dispatcher->dispatch($event, FailureEvent::EVENT_ID); if ($event->shouldThrowException()) { diff --git a/src/Resources/views/web_profiler.html.twig b/src/Resources/views/web_profiler.html.twig index c434925..35cec19 100644 --- a/src/Resources/views/web_profiler.html.twig +++ b/src/Resources/views/web_profiler.html.twig @@ -91,6 +91,10 @@
Alias: {{ statement.alias|default('N/A') }}
+ {# TODO: Add scheme. Scheme is currently private in the underlying driver #} +{#
#} +{# Scheme: {{ statement.scheme|default('N/A') }}#} +{#
#}
Statistics: {{ statement|json_encode }}
diff --git a/src/SymfonyClient.php b/src/SymfonyClient.php index 2290e36..fd4eff2 100644 --- a/src/SymfonyClient.php +++ b/src/SymfonyClient.php @@ -37,7 +37,12 @@ public function run(string $statement, iterable $parameters = [], ?string $alias public function runStatement(Statement $statement, ?string $alias = null): ?SummarizedResult { - return $this->handler->handle(fn (Statement $statement) => $this->client->runStatement($statement, $alias), $statement, $alias); + return $this->handler->handle( + fn(Statement $statement) => $this->client->runStatement($statement, $alias), + $statement, + $alias, + null + ); } public function runStatements(iterable $statements, ?string $alias = null): CypherList @@ -50,14 +55,17 @@ public function runStatements(iterable $statements, ?string $alias = null): Cyph return CypherList::fromIterable($tbr); } - public function beginTransaction(?iterable $statements = null, ?string $alias = null, ?TransactionConfiguration $config = null): UnmanagedTransactionInterface - { + public function beginTransaction( + ?iterable $statements = null, + ?string $alias = null, + ?TransactionConfiguration $config = null + ): UnmanagedTransactionInterface { $tsx = new SymfonyTransaction($this->client->beginTransaction(null, $alias, $config), $this->handler, $alias); - $runHandler = fn (Statement $statement): CypherList => $tsx->runStatement($statement); + $runHandler = fn(Statement $statement): CypherList => $tsx->runStatement($statement); foreach (($statements ?? []) as $statement) { - $this->handler->handle($runHandler, $statement, $alias); + $this->handler->handle($runHandler, $statement, $alias, null); } return $tsx; @@ -68,24 +76,30 @@ public function getDriver(?string $alias): DriverInterface return $this->client->getDriver($alias); } - public function writeTransaction(callable $tsxHandler, ?string $alias = null, ?TransactionConfiguration $config = null) - { + public function writeTransaction( + callable $tsxHandler, + ?string $alias = null, + ?TransactionConfiguration $config = null + ) { $sessionConfig = SessionConfiguration::default()->withAccessMode(AccessMode::READ()); $session = $this->client->getDriver($alias)->createSession($sessionConfig); return TransactionHelper::retry( - fn () => new SymfonyTransaction($session->beginTransaction([], $config), $this->handler, $alias), + fn() => new SymfonyTransaction($session->beginTransaction([], $config), $this->handler, $alias), $tsxHandler ); } - public function readTransaction(callable $tsxHandler, ?string $alias = null, ?TransactionConfiguration $config = null) - { + public function readTransaction( + callable $tsxHandler, + ?string $alias = null, + ?TransactionConfiguration $config = null + ) { $sessionConfig = SessionConfiguration::default()->withAccessMode(AccessMode::WRITE()); $session = $this->client->getDriver($alias)->createSession($sessionConfig); return TransactionHelper::retry( - fn () => new SymfonyTransaction($session->beginTransaction([], $config), $this->handler, $alias), + fn() => new SymfonyTransaction($session->beginTransaction([], $config), $this->handler, $alias), $tsxHandler ); } diff --git a/src/SymfonyTransaction.php b/src/SymfonyTransaction.php index bc8a46a..8400fc7 100644 --- a/src/SymfonyTransaction.php +++ b/src/SymfonyTransaction.php @@ -18,8 +18,11 @@ class SymfonyTransaction implements UnmanagedTransactionInterface /** * @param UnmanagedTransactionInterface> $tsx */ - public function __construct(private UnmanagedTransactionInterface $tsx, private EventHandler $handler, private ?string $alias) - { + public function __construct( + private readonly UnmanagedTransactionInterface $tsx, + private readonly EventHandler $handler, + private readonly ?string $alias + ) { } public function run(string $statement, iterable $parameters = []): SummarizedResult @@ -29,7 +32,11 @@ public function run(string $statement, iterable $parameters = []): SummarizedRes public function runStatement(Statement $statement): SummarizedResult { - return $this->handler->handle(fn ($statement) => $this->tsx->runStatement($statement), $statement, $this->alias); + return $this->handler->handle(fn ($statement) => $this->tsx->runStatement($statement), + $statement, + $this->alias, + null + ); } /** From 157828443bdfe47fb20ded157f8cd9934a9776e3 Mon Sep 17 00:00:00 2001 From: exaby73 Date: Mon, 16 Sep 2024 11:54:43 +0530 Subject: [PATCH 20/25] fix: Remove ProfileListener from services.php --- config/services.php | 3 --- 1 file changed, 3 deletions(-) diff --git a/config/services.php b/config/services.php index 59dd13f..a071aad 100644 --- a/config/services.php +++ b/config/services.php @@ -43,7 +43,4 @@ $services->alias(DriverInterface::class, 'neo4j.driver'); $services->alias(SessionInterface::class, 'neo4j.session'); $services->alias(TransactionInterface::class, 'neo4j.transaction'); - - $services->set('neo4j.subscriber', Neo4jProfileListener::class) - ->tag('kernel.event_subscriber'); }; From 06da73c7e3019c1e384022e8fba4560d236255cd Mon Sep 17 00:00:00 2001 From: exaby73 Date: Mon, 16 Sep 2024 11:57:41 +0530 Subject: [PATCH 21/25] fix: Remove EventHandler from services.php --- config/services.php | 4 ---- 1 file changed, 4 deletions(-) diff --git a/config/services.php b/config/services.php index 779c7c3..59dd13f 100644 --- a/config/services.php +++ b/config/services.php @@ -16,10 +16,6 @@ return static function (ContainerConfigurator $configurator) { $services = $configurator->services(); - $services->set('neo4j.event_handler', EventHandler::class) - ->autowire() - ->autoconfigure(); - $services->set('neo4j.client_factory', ClientFactory::class) ->args([ service('neo4j.event_handler'), From 5298dced04cffa2112c4d476e579d10841a25339 Mon Sep 17 00:00:00 2001 From: exaby73 Date: Mon, 16 Sep 2024 12:01:58 +0530 Subject: [PATCH 22/25] chore: Run fix-cs --- src/SymfonyClient.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/SymfonyClient.php b/src/SymfonyClient.php index fd4eff2..8842f7e 100644 --- a/src/SymfonyClient.php +++ b/src/SymfonyClient.php @@ -38,7 +38,7 @@ public function run(string $statement, iterable $parameters = [], ?string $alias public function runStatement(Statement $statement, ?string $alias = null): ?SummarizedResult { return $this->handler->handle( - fn(Statement $statement) => $this->client->runStatement($statement, $alias), + fn (Statement $statement) => $this->client->runStatement($statement, $alias), $statement, $alias, null @@ -62,7 +62,7 @@ public function beginTransaction( ): UnmanagedTransactionInterface { $tsx = new SymfonyTransaction($this->client->beginTransaction(null, $alias, $config), $this->handler, $alias); - $runHandler = fn(Statement $statement): CypherList => $tsx->runStatement($statement); + $runHandler = fn (Statement $statement): CypherList => $tsx->runStatement($statement); foreach (($statements ?? []) as $statement) { $this->handler->handle($runHandler, $statement, $alias, null); @@ -85,7 +85,7 @@ public function writeTransaction( $session = $this->client->getDriver($alias)->createSession($sessionConfig); return TransactionHelper::retry( - fn() => new SymfonyTransaction($session->beginTransaction([], $config), $this->handler, $alias), + fn () => new SymfonyTransaction($session->beginTransaction([], $config), $this->handler, $alias), $tsxHandler ); } @@ -99,7 +99,7 @@ public function readTransaction( $session = $this->client->getDriver($alias)->createSession($sessionConfig); return TransactionHelper::retry( - fn() => new SymfonyTransaction($session->beginTransaction([], $config), $this->handler, $alias), + fn () => new SymfonyTransaction($session->beginTransaction([], $config), $this->handler, $alias), $tsxHandler ); } From d2ce89fe730d37769db3433278870e88a838c547 Mon Sep 17 00:00:00 2001 From: exaby73 Date: Mon, 16 Sep 2024 12:07:00 +0530 Subject: [PATCH 23/25] chore: Run fix-cs --- src/Collector/Neo4jDataCollector.php | 2 +- src/Event/FailureEvent.php | 2 +- src/Event/PostRunEvent.php | 2 +- src/Event/PreRunEvent.php | 2 +- src/EventHandler.php | 2 +- src/SymfonyClient.php | 8 ++++---- src/SymfonyTransaction.php | 2 +- tests/App/Controller/TestController.php | 2 +- 8 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/Collector/Neo4jDataCollector.php b/src/Collector/Neo4jDataCollector.php index 2676146..ec7d2ba 100644 --- a/src/Collector/Neo4jDataCollector.php +++ b/src/Collector/Neo4jDataCollector.php @@ -23,7 +23,7 @@ final class Neo4jDataCollector extends AbstractDataCollector { public function __construct( - private readonly Neo4jProfileListener $subscriber + private readonly Neo4jProfileListener $subscriber, ) { } diff --git a/src/Event/FailureEvent.php b/src/Event/FailureEvent.php index e5892d9..50ab49f 100644 --- a/src/Event/FailureEvent.php +++ b/src/Event/FailureEvent.php @@ -19,7 +19,7 @@ public function __construct( private readonly Statement $statement, private readonly Neo4jException $exception, private readonly \DateTimeInterface $time, - private readonly ?string $scheme + private readonly ?string $scheme, ) { } diff --git a/src/Event/PostRunEvent.php b/src/Event/PostRunEvent.php index e9d2d47..38c38da 100644 --- a/src/Event/PostRunEvent.php +++ b/src/Event/PostRunEvent.php @@ -15,7 +15,7 @@ public function __construct( private readonly ?string $alias, private readonly ResultSummary $result, private readonly \DateTimeInterface $time, - private readonly ?string $scheme + private readonly ?string $scheme, ) { } diff --git a/src/Event/PreRunEvent.php b/src/Event/PreRunEvent.php index a653fe5..8772a15 100644 --- a/src/Event/PreRunEvent.php +++ b/src/Event/PreRunEvent.php @@ -15,7 +15,7 @@ public function __construct( private readonly ?string $alias, private readonly Statement $statement, private readonly \DateTimeInterface $time, - private readonly ?string $scheme + private readonly ?string $scheme, ) { } diff --git a/src/EventHandler.php b/src/EventHandler.php index 638cb3b..0f44d1f 100644 --- a/src/EventHandler.php +++ b/src/EventHandler.php @@ -28,7 +28,7 @@ class EventHandler public function __construct( ?EventDispatcherInterface $dispatcher, private readonly string $alias, - private readonly ?Stopwatch $stopwatch + private readonly ?Stopwatch $stopwatch, ) { $this->dispatcher = $dispatcher; } diff --git a/src/SymfonyClient.php b/src/SymfonyClient.php index 8842f7e..f1696fe 100644 --- a/src/SymfonyClient.php +++ b/src/SymfonyClient.php @@ -26,7 +26,7 @@ class SymfonyClient implements ClientInterface */ public function __construct( private readonly ClientInterface $client, - private readonly EventHandler $handler + private readonly EventHandler $handler, ) { } @@ -58,7 +58,7 @@ public function runStatements(iterable $statements, ?string $alias = null): Cyph public function beginTransaction( ?iterable $statements = null, ?string $alias = null, - ?TransactionConfiguration $config = null + ?TransactionConfiguration $config = null, ): UnmanagedTransactionInterface { $tsx = new SymfonyTransaction($this->client->beginTransaction(null, $alias, $config), $this->handler, $alias); @@ -79,7 +79,7 @@ public function getDriver(?string $alias): DriverInterface public function writeTransaction( callable $tsxHandler, ?string $alias = null, - ?TransactionConfiguration $config = null + ?TransactionConfiguration $config = null, ) { $sessionConfig = SessionConfiguration::default()->withAccessMode(AccessMode::READ()); $session = $this->client->getDriver($alias)->createSession($sessionConfig); @@ -93,7 +93,7 @@ public function writeTransaction( public function readTransaction( callable $tsxHandler, ?string $alias = null, - ?TransactionConfiguration $config = null + ?TransactionConfiguration $config = null, ) { $sessionConfig = SessionConfiguration::default()->withAccessMode(AccessMode::WRITE()); $session = $this->client->getDriver($alias)->createSession($sessionConfig); diff --git a/src/SymfonyTransaction.php b/src/SymfonyTransaction.php index 8400fc7..603b732 100644 --- a/src/SymfonyTransaction.php +++ b/src/SymfonyTransaction.php @@ -21,7 +21,7 @@ class SymfonyTransaction implements UnmanagedTransactionInterface public function __construct( private readonly UnmanagedTransactionInterface $tsx, private readonly EventHandler $handler, - private readonly ?string $alias + private readonly ?string $alias, ) { } diff --git a/tests/App/Controller/TestController.php b/tests/App/Controller/TestController.php index 41b0fbd..98969b0 100644 --- a/tests/App/Controller/TestController.php +++ b/tests/App/Controller/TestController.php @@ -11,7 +11,7 @@ class TestController extends AbstractController { public function __construct( - private readonly ClientInterface $client + private readonly ClientInterface $client, ) { } From 03d771bd9ac119efe5218212bc5ef50eb94ca9d6 Mon Sep 17 00:00:00 2001 From: exaby73 Date: Mon, 16 Sep 2024 12:37:16 +0530 Subject: [PATCH 24/25] chore: Fix Psalm warnings --- docker-compose.yml | 2 +- src/ClientFactory.php | 8 ++++---- src/Collector/Neo4jDataCollector.php | 2 +- tests/Functional/ProfilerTest.php | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index b558b1c..c28d0d8 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -3,7 +3,7 @@ networks: services: app: - user: ${UID-1000}:${GID-1000} + user: root:${UID-1000}:${GID-1000} build: context: . ports: diff --git a/src/ClientFactory.php b/src/ClientFactory.php index 972f7e1..7d8d419 100644 --- a/src/ClientFactory.php +++ b/src/ClientFactory.php @@ -56,15 +56,15 @@ public function create(): SymfonyClient /** @var ClientBuilder> $builder */ $builder = ClientBuilder::create(); - if ($this->driverConfig) { + if (null !== $this->driverConfig) { $builder = $builder->withDefaultDriverConfiguration($this->makeDriverConfig()); } - if ($this->sessionConfiguration) { + if (null !== $this->sessionConfiguration) { $builder = $builder->withDefaultSessionConfiguration($this->makeSessionConfig()); } - if ($this->transactionConfiguration) { + if (null !== $this->transactionConfiguration) { $builder = $builder->withDefaultTransactionConfiguration($this->makeTransactionConfig()); } @@ -77,7 +77,7 @@ public function create(): SymfonyClient ); } - if ($this->defaultDriver) { + if (null !== $this->defaultDriver) { $builder = $builder->withDefaultDriver($this->defaultDriver); } diff --git a/src/Collector/Neo4jDataCollector.php b/src/Collector/Neo4jDataCollector.php index ec7d2ba..4099a5c 100644 --- a/src/Collector/Neo4jDataCollector.php +++ b/src/Collector/Neo4jDataCollector.php @@ -134,7 +134,7 @@ private function recursiveToArray(mixed $obj): mixed { if (is_array($obj)) { return array_map( - fn (mixed $x) => $this->recursiveToArray($x), + fn (mixed $x): mixed => $this->recursiveToArray($x), $obj ); } diff --git a/tests/Functional/ProfilerTest.php b/tests/Functional/ProfilerTest.php index ebe0125..623b079 100644 --- a/tests/Functional/ProfilerTest.php +++ b/tests/Functional/ProfilerTest.php @@ -13,7 +13,7 @@ protected static function getKernelClass(): string return TestKernel::class; } - public function testProfiler() + public function testProfiler(): void { $client = static::createClient(); $client->enableProfiler(); From cf7864823b8b4b53ac68bff27941a89c4e07d343 Mon Sep 17 00:00:00 2001 From: Ghlen Nagels Date: Mon, 16 Sep 2024 13:48:01 +0530 Subject: [PATCH 25/25] force correct environment to prepare the cache before running psalm --- composer.json | 2 +- psalm.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/composer.json b/composer.json index 970d17c..1bf4ae2 100644 --- a/composer.json +++ b/composer.json @@ -53,7 +53,7 @@ } }, "scripts": { - "psalm": "php bin/console.php cache:warmup && vendor/bin/psalm --show-info=true", + "psalm": "APP_ENV=dev php bin/console.php cache:warmup && vendor/bin/psalm --show-info=true", "fix-cs": "vendor/bin/php-cs-fixer fix", "check-cs": "vendor/bin/php-cs-fixer fix --dry-run", "ci-symfony-install-version": "./.github/scripts/setup-symfony-env.bash" diff --git a/psalm.xml b/psalm.xml index ce09bf1..a81eac8 100644 --- a/psalm.xml +++ b/psalm.xml @@ -18,7 +18,7 @@ - var/cache/test/Neo4j_Neo4jBundle_Tests_App_TestKernelTestDebugContainer.xml + var/cache/dev/Neo4j_Neo4jBundle_Tests_App_TestKernelDevDebugContainer.xml