From 531ae8c5670080cc67303705b3df5de29a0c76eb Mon Sep 17 00:00:00 2001 From: Roman Bylbas Date: Fri, 3 Dec 2021 17:34:49 +0200 Subject: [PATCH 1/6] Implemented event steam Signed-off-by: Roman Bylbas --- docs/Example/EventStream.md | 42 ++++++++++++++++++ src/EventStream/Event.php | 50 ++++++++++++++++++++++ src/EventStream/EventSource.php | 76 +++++++++++++++++++++++++++++++++ 3 files changed, 168 insertions(+) create mode 100644 docs/Example/EventStream.md create mode 100644 src/EventStream/Event.php create mode 100644 src/EventStream/EventSource.php diff --git a/docs/Example/EventStream.md b/docs/Example/EventStream.md new file mode 100644 index 0000000..105c707 --- /dev/null +++ b/docs/Example/EventStream.md @@ -0,0 +1,42 @@ +# Event stream + +Create `EventSource` instance by passing node url to constructor + +```php +$nodeUrl = 'http://3.208.91.63:9999'; +$es = new Casper\EventStream\EventSource($nodeUrl); +``` + +Use `connect()` method for connecting to the node. The method has two parameters `$eventsType` and `$startFrom`. The first one is mandatory and second is optional. + +Available events type: +- `EventSource::EVENT_TYPE_MAIN` - listen system events +- `EventSource::EVENT_TYPE_DEPLOYS` - listen deploy events +- `EventSource::EVENT_TYPE_SIGS` - listen finality signatures events + +```php +$es->connect(\Casper\EventStream\EventSource::EVENT_TYPE_MAIN); +``` + +Set event handler callback function to `EventSource` instance with `onMessage()` method. +For event stream aborting use `abort()` method. + +## Example + +```php +$nodeUrl = 'http://localhost:9999'; +$es = new Casper\EventStream\EventSource($nodeUrl); + +$es->onMessage( + function (\Casper\EventStream\Event $event) use ($es) { + if ($event->getId() === null) { + $es->abort(); + return; + } + + echo json_encode($event->getData()) . "\n"; + } +); + +$es->connect(\Casper\EventStream\EventSource::EVENT_TYPE_MAIN); +``` diff --git a/src/EventStream/Event.php b/src/EventStream/Event.php new file mode 100644 index 0000000..26ace29 --- /dev/null +++ b/src/EventStream/Event.php @@ -0,0 +1,50 @@ +.*)/", $data, $match)) { + foreach ($match['data'] as $dataLine) { + $this->data = json_decode($dataLine, true); + } + } + + if (preg_match_all("/(?id|name)\:(?.*)/", $data, $match)) { + foreach ($match['key'] as $i => $key) { + $this->{$key} = trim($match['value'][$i]); + } + } + + if (!$this->data) { + throw new \Exception('Invalid event'); + } + } + + public function getId(): ?string + { + return $this->id; + } + + public function getName(): ?string + { + return $this->name; + } + + public function getData(): ?array + { + return $this->data; + } +} diff --git a/src/EventStream/EventSource.php b/src/EventStream/EventSource.php new file mode 100644 index 0000000..9d125a3 --- /dev/null +++ b/src/EventStream/EventSource.php @@ -0,0 +1,76 @@ +url = $url; + } + + public function onMessage(callable $callback): void + { + $this->onMessage = $callback; + } + + public function abort() + { + $this->aborted = true; + } + + /** + * @throws \Exception + */ + public function connect(string $eventsType, int $startFrom = 0): void + { + $this->assertEventTypeIsValid($eventsType); + + $curl = curl_init($this->url . '/events/' . $eventsType); + curl_setopt_array($curl, array( + CURLOPT_WRITEFUNCTION => function ($_, $data) { + try { + $event = new Event(trim($data)); + + if (is_callable($this->onMessage)) { + ($this->onMessage)($event); + } + } catch (\Exception $_) { + } + return strlen($data); + }, + CURLOPT_NOPROGRESS => false, + CURLOPT_PROGRESSFUNCTION => function () { + return $this->aborted; + } + )); + curl_exec($curl); + $error = curl_error($curl); + + if (!$this->aborted && $error) { + throw new \Exception($error); + } + + curl_close($curl); + } + + /** + * @throws \Exception + */ + private function assertEventTypeIsValid(string $eventType): void + { + if (!in_array($eventType, [self::EVENT_TYPE_MAIN, self::EVENT_TYPE_DEPLOYS, self::EVENT_TYPE_SIGS])) { + throw new \Exception('Invalid event type'); + } + } +} From 9f26dbb721d5a594bcecc0820d90dd1fc8a5eac1 Mon Sep 17 00:00:00 2001 From: Roman Bylbas Date: Fri, 3 Dec 2021 20:13:17 +0200 Subject: [PATCH 2/6] Added Event Stream documentation to README.md Signed-off-by: Roman Bylbas --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 267d1c6..30f1b22 100644 --- a/README.md +++ b/README.md @@ -51,6 +51,7 @@ $latestBlockHash = $latestBlock->getHash(); ## More examples - [Key management](docs/Example/KeyManagement.md) - [Sending a Transfer](docs/Example/SendingTransfer.md) +- [Event Stream](docs/Example/EventStream.md) ## API ### RpcClient From f00f8693b69fac079a84d435474a5ecc49d1c176 Mon Sep 17 00:00:00 2001 From: Roman Bylbas Date: Sun, 5 Dec 2021 12:32:33 +0200 Subject: [PATCH 3/6] Small fix Signed-off-by: Roman Bylbas --- src/EventStream/Event.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/EventStream/Event.php b/src/EventStream/Event.php index 26ace29..e2a20a6 100644 --- a/src/EventStream/Event.php +++ b/src/EventStream/Event.php @@ -17,9 +17,7 @@ class Event public function __construct(string $data) { if (preg_match_all("/data:(?.*)/", $data, $match)) { - foreach ($match['data'] as $dataLine) { - $this->data = json_decode($dataLine, true); - } + $this->data = json_decode($match['data'][0], true); } if (preg_match_all("/(?id|name)\:(?.*)/", $data, $match)) { From 75ce45956c9d8d39007d0d0d8da4a12650223dca Mon Sep 17 00:00:00 2001 From: Roman Bylbas Date: Thu, 30 Jun 2022 14:02:31 +0300 Subject: [PATCH 4/6] Refactored event stram Signed-off-by: Roman Bylbas --- docs/Example/EventStream.md | 33 ++++++------- src/EventStream/Event.php | 19 ++------ src/EventStream/EventSource.php | 76 ------------------------------ src/EventStream/EventStream.php | 83 +++++++++++++++++++++++++++++++++ 4 files changed, 99 insertions(+), 112 deletions(-) delete mode 100644 src/EventStream/EventSource.php create mode 100644 src/EventStream/EventStream.php diff --git a/docs/Example/EventStream.md b/docs/Example/EventStream.md index 105c707..17080ee 100644 --- a/docs/Example/EventStream.md +++ b/docs/Example/EventStream.md @@ -1,33 +1,26 @@ # Event stream -Create `EventSource` instance by passing node url to constructor +Create `EventStream` instance by passing node url and stream path to the constructor. The third argument `$startFromEvent` is not required but can be used to filter out all events with id below `$startFromEvent` value. -```php -$nodeUrl = 'http://3.208.91.63:9999'; -$es = new Casper\EventStream\EventSource($nodeUrl); -``` - -Use `connect()` method for connecting to the node. The method has two parameters `$eventsType` and `$startFrom`. The first one is mandatory and second is optional. - -Available events type: -- `EventSource::EVENT_TYPE_MAIN` - listen system events -- `EventSource::EVENT_TYPE_DEPLOYS` - listen deploy events -- `EventSource::EVENT_TYPE_SIGS` - listen finality signatures events +Available stream paths: +- `EventStream::STREAM_PATH_MAIN` - listen system events +- `EventStream::STREAM_PATH_DEPLOYS` - listen deploy events +- `EventStream::STREAM_PATH_SIGS` - listen finality signatures events -```php -$es->connect(\Casper\EventStream\EventSource::EVENT_TYPE_MAIN); -``` - -Set event handler callback function to `EventSource` instance with `onMessage()` method. +Set event handler callback function to `EventStream` instance with `onEvent()` method. For event stream aborting use `abort()` method. +Use `listen()` method for connecting to the node. + ## Example ```php $nodeUrl = 'http://localhost:9999'; -$es = new Casper\EventStream\EventSource($nodeUrl); +$streamPath = \Casper\EventStream\EventStream::STREAM_PATH_MAIN; +$startFromEvent = 12345; -$es->onMessage( +$es = new Casper\EventStream\EventStream($nodeUrl, $streamPath, $startFromEvent); +$es->onEvent( function (\Casper\EventStream\Event $event) use ($es) { if ($event->getId() === null) { $es->abort(); @@ -38,5 +31,5 @@ $es->onMessage( } ); -$es->connect(\Casper\EventStream\EventSource::EVENT_TYPE_MAIN); +$es->listen(); ``` diff --git a/src/EventStream/Event.php b/src/EventStream/Event.php index e2a20a6..0fad767 100644 --- a/src/EventStream/Event.php +++ b/src/EventStream/Event.php @@ -4,9 +4,7 @@ class Event { - private ?string $id = null; - - private ?string $name = null; + private ?int $id = null; private ?array $data = null; @@ -20,14 +18,8 @@ public function __construct(string $data) $this->data = json_decode($match['data'][0], true); } - if (preg_match_all("/(?id|name)\:(?.*)/", $data, $match)) { - foreach ($match['key'] as $i => $key) { - $this->{$key} = trim($match['value'][$i]); - } - } - - if (!$this->data) { - throw new \Exception('Invalid event'); + if (preg_match_all("/id:(?.*)/", $data, $match)) { + $this->id = $match['id'][0]; } } @@ -36,11 +28,6 @@ public function getId(): ?string return $this->id; } - public function getName(): ?string - { - return $this->name; - } - public function getData(): ?array { return $this->data; diff --git a/src/EventStream/EventSource.php b/src/EventStream/EventSource.php deleted file mode 100644 index 9d125a3..0000000 --- a/src/EventStream/EventSource.php +++ /dev/null @@ -1,76 +0,0 @@ -url = $url; - } - - public function onMessage(callable $callback): void - { - $this->onMessage = $callback; - } - - public function abort() - { - $this->aborted = true; - } - - /** - * @throws \Exception - */ - public function connect(string $eventsType, int $startFrom = 0): void - { - $this->assertEventTypeIsValid($eventsType); - - $curl = curl_init($this->url . '/events/' . $eventsType); - curl_setopt_array($curl, array( - CURLOPT_WRITEFUNCTION => function ($_, $data) { - try { - $event = new Event(trim($data)); - - if (is_callable($this->onMessage)) { - ($this->onMessage)($event); - } - } catch (\Exception $_) { - } - return strlen($data); - }, - CURLOPT_NOPROGRESS => false, - CURLOPT_PROGRESSFUNCTION => function () { - return $this->aborted; - } - )); - curl_exec($curl); - $error = curl_error($curl); - - if (!$this->aborted && $error) { - throw new \Exception($error); - } - - curl_close($curl); - } - - /** - * @throws \Exception - */ - private function assertEventTypeIsValid(string $eventType): void - { - if (!in_array($eventType, [self::EVENT_TYPE_MAIN, self::EVENT_TYPE_DEPLOYS, self::EVENT_TYPE_SIGS])) { - throw new \Exception('Invalid event type'); - } - } -} diff --git a/src/EventStream/EventStream.php b/src/EventStream/EventStream.php new file mode 100644 index 0000000..132eca4 --- /dev/null +++ b/src/EventStream/EventStream.php @@ -0,0 +1,83 @@ +assertSteamPathIsValid($streamPath); + + $this->nodeUrl = $nodeUrl; + $this->streamPath = $streamPath; + $this->startFromEvent = $startFromEvent; + } + + public function onEvent(callable $onEvent): void + { + $this->onEvent = $onEvent; + } + + public function abort() + { + $this->aborted = true; + } + + /** + * @throws \Exception + */ + public function listen(): void + { + $curl = curl_init($this->nodeUrl . $this->streamPath); + curl_setopt_array($curl, array( + CURLOPT_WRITEFUNCTION => function ($_, $data) { + $event = new Event(trim($data)); + + if (is_callable($this->onEvent) && $event->getId() >= $this->startFromEvent && $event->getData()) { + ($this->onEvent)($event); + } + + return strlen($data); + }, + CURLOPT_NOPROGRESS => false, + CURLOPT_PROGRESSFUNCTION => function () { + return $this->aborted; + } + )); + curl_exec($curl); + $error = curl_error($curl); + + if (!$this->aborted && $error) { + throw new \Exception($error); + } + + curl_close($curl); + } + + /** + * @throws \Exception + */ + private function assertSteamPathIsValid(string $streamPath): void + { + if (!in_array($streamPath, [self::STREAM_PATH_MAIN, self::STREAM_PATH_DEPLOYS, self::STREAM_PATH_SIGS])) { + throw new \Exception('Invalid stream path'); + } + } +} From b10157afcd53f583f755a98d50b10838eed509dc Mon Sep 17 00:00:00 2001 From: Roman Bylbas Date: Tue, 25 Jul 2023 10:23:31 +0300 Subject: [PATCH 5/6] Changed CASPER_PHP_SDK_TEST_NODE_URL in ci.yml Signed-off-by: Roman Bylbas --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8e75f41..0d3ddc6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -47,4 +47,4 @@ jobs: - name: Run test suite run: composer run-script test env: - CASPER_PHP_SDK_TEST_NODE_URL: "136.243.187.84:7777" + CASPER_PHP_SDK_TEST_NODE_URL: "65.21.237.160:7777" From 78f9ff84d4945b8ab56cddec5dce1b86b637bbfe Mon Sep 17 00:00:00 2001 From: Roman Bylbas Date: Tue, 25 Jul 2023 11:38:22 +0300 Subject: [PATCH 6/6] Fixed getStateRootHash rpc method params Signed-off-by: Roman Bylbas --- src/Rpc/RpcClient.php | 2 +- tests/Functional/Rpc/RpcClientTest.php | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Rpc/RpcClient.php b/src/Rpc/RpcClient.php index 6bfdcdb..30865de 100644 --- a/src/Rpc/RpcClient.php +++ b/src/Rpc/RpcClient.php @@ -198,7 +198,7 @@ public function getStateRootHash(string $blockHash = ''): string { $response = $this->rpcCallMethod( self::RPC_METHOD_GET_STATE_ROOT_HASH, - array('block_hash' => $blockHash) + $blockHash ? [array('Hash' => $blockHash)] : [] ); return $response['state_root_hash']; diff --git a/tests/Functional/Rpc/RpcClientTest.php b/tests/Functional/Rpc/RpcClientTest.php index 5c0649a..f4e19b7 100644 --- a/tests/Functional/Rpc/RpcClientTest.php +++ b/tests/Functional/Rpc/RpcClientTest.php @@ -59,7 +59,7 @@ public function testGetDeploy(): void public function testGetDeployByFakeHash(): void { $fakeDeployHash = '1234567891234567891234567891234567891234567891234567891234567891'; - $errorMessage = 'deploy not know'; + $errorMessage = 'No such deploy'; $this->expectException(RpcError::class); $this->expectExceptionMessage($errorMessage); @@ -89,7 +89,7 @@ public function testGetBlockByHash(Block $latestBlock): void public function testGetBlockByFakeHash(): void { $fakeBlockHash = '1234567891234567891234567891234567891234567891234567891234567891'; - $errorMessage = 'block not know'; + $errorMessage = 'No such block'; $this->expectException(RpcError::class); $this->expectExceptionMessage($errorMessage);