From 3f8dce1cfbf72b01878aad47f0be7093811a246a Mon Sep 17 00:00:00 2001 From: t1gor Date: Mon, 3 Sep 2018 10:50:23 +0300 Subject: [PATCH 1/7] WIP: initial concept of returning report objects --- .github/CONTRIBUTING.md | 23 +++++-- .gitignore | 3 + .travis.yml | 2 + src/MessageSentReport.php | 131 ++++++++++++++++++++++++++++++++++++++ src/WebPush.php | 82 +++++++++--------------- 5 files changed, 183 insertions(+), 58 deletions(-) create mode 100644 src/MessageSentReport.php diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 17436c84..88823f47 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -2,14 +2,27 @@ WebPush is an open source library. Feel free to contribute by submitting a pull request or creating (and solving) issues! -## Running Tests +## Installing a mock push service + +Before running tests, you'll need to install the [web-push testing service](https://www.npmjs.com/package/web-push-testing-service): + +```bash +npm install web-push-testing-service -g +``` + +NOTE: You might need to make sure command `web-push-testing-service` runs OK on cli. In my case on OSX, I needed to add a bash alias after install: -First, you will need to create your own configuration file by copying -phpunit.dist.xml to phpunit.xml and filling in the fields you need for +```~/.bash_profile +alias web-push-testing-service='/usr/local/Cellar/node/7.4.0/bin/web-push-testing-service' +``` + +After that, please create your own configuration file by copying +`phpunit.dist.xml` to phpunit.xml and filling in the fields you need for testing (i.e. STANDARD_ENDPOINT, GCM_API_KEY etc.). -Then, download [phpunit](https://phpunit.de/) and test with one of the -following commands: +## Running Tests + +Then, download [phpunit](https://phpunit.de/) and test with one of the following commands: **For All Tests** `php phpunit.phar` diff --git a/.gitignore b/.gitignore index 4d00dcb4..a8936f69 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,6 @@ composer.lock phpunit.xml +# web-push-testing-service logs +cli.log +module.log \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index c9d34f52..ae6c83a9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,6 +8,8 @@ sudo: required cache: directories: - ~/.selenium-assistant + - node_modules # cache modules too + - $HOME/.composer/cache/files # and composer packages matrix: allow_failures: diff --git a/src/MessageSentReport.php b/src/MessageSentReport.php new file mode 100644 index 00000000..96c08846 --- /dev/null +++ b/src/MessageSentReport.php @@ -0,0 +1,131 @@ +success = $success; + $this->request = $request; + $this->response = $response; + $this->reason = $reason; + } + + /** + * @return bool + */ + public function isSuccess(): bool { + return $this->success; + } + + /** + * @param bool $success + * + * @return MessageSentReport + */ + public function setSuccess(bool $success): MessageSentReport { + $this->success = $success; + return $this; + } + + /** + * @return RequestInterface + */ + public function getRequest(): RequestInterface { + return $this->request; + } + + /** + * @param RequestInterface $request + * + * @return MessageSentReport + */ + public function setRequest(RequestInterface $request): MessageSentReport { + $this->request = $request; + return $this; + } + + /** + * @return ResponseInterface + */ + public function getResponse(): ResponseInterface { + return $this->response; + } + + /** + * @param ResponseInterface $response + * + * @return MessageSentReport + */ + public function setResponse(ResponseInterface $response): MessageSentReport { + $this->response = $response; + return $this; + } + + /** + * @return string + */ + public function getEndpoint(): string { + return $this->request->getUri()->__toString(); + } + + /** + * @return bool + */ + public function isSubscriptionExpired(): bool { + return \in_array($this->response->getStatusCode(), [404, 410], true); + } + + /** + * @return string + */ + public function getReason(): string { + return $this->reason; + } + + /** + * @param string $reason + * + * @return MessageSentReport + */ + public function setReason(string $reason): MessageSentReport { + $this->reason = $reason; + return $this; + } +} diff --git a/src/WebPush.php b/src/WebPush.php index 70a6e527..8cee9c26 100644 --- a/src/WebPush.php +++ b/src/WebPush.php @@ -17,7 +17,7 @@ use GuzzleHttp\Client; use GuzzleHttp\Exception\RequestException; use GuzzleHttp\Psr7\Request; -use GuzzleHttp\Promise; +use Psr\Http\Message\ResponseInterface; class WebPush { @@ -140,17 +140,14 @@ public function sendNotification(Subscription $subscription, ?string $payload = return true; } - /** - * Flush notifications. Triggers the requests. - * - * @param null|int $batchSize Defaults the value defined in defaultOptions during instantiation (which defaults to 1000). - * - * @return array|bool If there are no errors, return true. - * If there were no notifications in the queue, return false. - * Else return an array of information for each notification sent (success, statusCode, headers, content) - * - * @throws \ErrorException - */ + /** + * Flush notifications. Triggers the requests. + * + * @param null|int $batchSize Defaults the value defined in defaultOptions during instantiation (which defaults to 1000). + * + * @return bool|MessageSentReport|\Generator + * @throws \ErrorException + */ public function flush(?int $batchSize = null) { if (empty($this->notifications)) { @@ -162,52 +159,31 @@ public function flush(?int $batchSize = null) } $batches = array_chunk($this->notifications, $batchSize); - $return = []; - $completeSuccess = true; - foreach ($batches as $batch) { - // for each endpoint server type - $requests = $this->prepare($batch); - $promises = []; - foreach ($requests as $request) { - $promises[] = $this->client->sendAsync($request); - } - $results = Promise\settle($promises)->wait(); - - foreach ($results as $result) { - if ($result['state'] === "rejected") { - /** @var RequestException $reason **/ - $reason = $result['reason']; - - $error = [ - 'success' => false, - 'endpoint' => $reason->getRequest()->getUri(), - 'message' => $reason->getMessage(), - ]; - - $response = $reason->getResponse(); - if ($response !== null) { - $statusCode = $response->getStatusCode(); - $error['statusCode'] = $statusCode; - $error['reasonPhrase'] = $response->getReasonPhrase(); - $error['expired'] = in_array($statusCode, [404, 410]); - $error['content'] = $response->getBody(); - $error['headers'] = $response->getHeaders(); - } - $return[] = $error; - $completeSuccess = false; - } else { - $return[] = [ - 'success' => true, - ]; - } - } + foreach ($batches as $batch) { + // for each endpoint server type + $requests = $this->prepare($batch); + + foreach ($requests as $request) { + $result = null; + + $this->client->sendAsync($request) + ->then(function ($response) use ($request, &$result) { + /** @var ResponseInterface $response * */ + $result = new MessageSentReport($request, $response); + }) + ->otherwise(function ($reason) use (&$result) { + /** @var RequestException $reason **/ + $result = new MessageSentReport($reason->getRequest(), $reason->getResponse(), false, $reason->getMessage()); + }) + ->wait(false); + + yield $result; + } } // reset queue $this->notifications = null; - - return $completeSuccess ? true : $return; } /** From 56d8da1a03b73b7b75b8c3763b89f1044e14ea6f Mon Sep 17 00:00:00 2001 From: t1gor Date: Thu, 6 Sep 2018 12:27:08 +0300 Subject: [PATCH 2/7] WIP: add a test --- .gitignore | 4 +++- Vagrantfile | 0 src/WebPush.php | 30 +++++++---------------------- tests/WebPushTest.php | 44 +++++++++++++++++++++++++++++++------------ 4 files changed, 42 insertions(+), 36 deletions(-) create mode 100644 Vagrantfile diff --git a/.gitignore b/.gitignore index a8936f69..b3f67725 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,6 @@ phpunit.xml # web-push-testing-service logs cli.log -module.log \ No newline at end of file +module.log +.vagrant +Vagrantfile # temp, may be? \ No newline at end of file diff --git a/Vagrantfile b/Vagrantfile new file mode 100644 index 00000000..e69de29b diff --git a/src/WebPush.php b/src/WebPush.php index 8cee9c26..898cab34 100644 --- a/src/WebPush.php +++ b/src/WebPush.php @@ -121,23 +121,7 @@ public function sendNotification(Subscription $subscription, ?string $payload = $this->notifications[] = new Notification($subscription, $payload, $options, $auth); - if ($flush) { - $res = $this->flush(); - - // if there has been a problem with at least one notification - if (is_array($res)) { - // if there was only one notification, return the information directly - if (count($res) === 1) { - return $res[0]; - } - - return $res; - } - - return true; - } - - return true; + return false !== $flush ? $this->flush() : true; } /** @@ -145,13 +129,13 @@ public function sendNotification(Subscription $subscription, ?string $payload = * * @param null|int $batchSize Defaults the value defined in defaultOptions during instantiation (which defaults to 1000). * - * @return bool|MessageSentReport|\Generator + * @return \Generator * @throws \ErrorException */ - public function flush(?int $batchSize = null) + public function flush(?int $batchSize = null) : \Generator { if (empty($this->notifications)) { - return false; + yield from []; } if (null === $batchSize) { @@ -160,6 +144,9 @@ public function flush(?int $batchSize = null) $batches = array_chunk($this->notifications, $batchSize); + // reset queue + $this->notifications = []; + foreach ($batches as $batch) { // for each endpoint server type $requests = $this->prepare($batch); @@ -181,9 +168,6 @@ public function flush(?int $batchSize = null) yield $result; } } - - // reset queue - $this->notifications = null; } /** diff --git a/tests/WebPushTest.php b/tests/WebPushTest.php index e540c5fd..d1a75f6c 100644 --- a/tests/WebPushTest.php +++ b/tests/WebPushTest.php @@ -148,18 +148,38 @@ public function testSendNotificationWithTooBigPayload() /** * @throws ErrorException */ - public function testFlush() - { - $subscription = new Subscription(self::$endpoints['standard']); - - $this->webPush->sendNotification($subscription); - $this->assertTrue($this->webPush->flush()); - - // queue has been reset - $this->assertFalse($this->webPush->flush()); - - $this->webPush->sendNotification($subscription); - $this->assertTrue($this->webPush->flush()); + public function testFlush() { + $subscription = new Subscription(self::$endpoints['standard']); + + $this->webPush->sendNotification($subscription); + $this->assertNotEmpty(iterator_to_array($this->webPush->flush())); + + // queue has been reset + $this->assertEmpty(iterator_to_array($this->webPush->flush())); + + $this->webPush->sendNotification($subscription); + $this->assertNotEmpty(iterator_to_array($this->webPush->flush())); + + $sub = Subscription::create([ + 'endpoint' => 'https://fcm.googleapis.com/fcm/send/fCd2-8nXJhU:APA91bGi2uaqFXGft4qdolwyRUcUPCL1XV_jWy1tpCRqnu4sk7ojUpC5gnq1PTncbCdMq9RCVQIIFIU9BjzScvjrDqpsI7J-K_3xYW8xo1xSNCfge1RvJ6Xs8RGL_Sw7JtbCyG1_EVgWDc22on1r_jozD8vsFbB0Fg', + 'publicKey' => 'BME-1ZSAv2AyGjENQTzrXDj6vSnhAIdKso4n3NDY0lsd1DUgEzBw7ARMKjrYAm7JmJBPsilV5CWNH0mVPyJEt0Q', + 'authToken' => 'hUIGbmiypj9_EQea8AnCKA', + 'contentEncoding' => 'aes128gcm', + ]); + + // test multiple requests + $this->webPush->sendNotification($sub, json_encode(['test' => 1])); + $this->webPush->sendNotification($sub, json_encode(['test' => 2])); + $this->webPush->sendNotification($sub, json_encode(['test' => 3])); + + /** @var \Minishlink\WebPush\MessageSentReport $report */ + foreach ($this->webPush->flush() as $report) { + $this->assertFalse($report->isSuccess()); + $this->assertFalse($report->isSubscriptionExpired()); + $this->assertEquals(404, $report->getResponse()->getStatusCode()); + $this->assertNotEmpty($report->getReason()); + $this->assertNotFalse(filter_var($report->getEndpoint(), FILTER_VALIDATE_URL)); + } } /** From 93e6e06481d36bdc1876be01df421c4401515e34 Mon Sep 17 00:00:00 2001 From: Igor Timoshenkov Date: Thu, 6 Sep 2018 12:36:10 +0300 Subject: [PATCH 3/7] Docs update Change results handling example --- README.md | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 58e971e0..2c9a5b36 100644 --- a/README.md +++ b/README.md @@ -68,10 +68,26 @@ foreach ($notifications as $notification) { $notification['payload'] // optional (defaults null) ); } -$webPush->flush(); -// send one notification and flush directly -$webPush->sendNotification( +/** + * Check sent results + * @var MessageSentReport $report + */ +foreach ($webPush->flush() as $report) { + $endpoint = $report->getRequest()->getUri()->__toString(); + + if ($report->isSuccess()) { + echo "[v] Message sent successfully for subscription {$endpoint}."; + } else { + echo "[x] Message failed to sent for subscription {$endpoint}: {$report->getReason()}"; + } +} + +/** + * send one notification and flush directly + * @var \Generator $sent + */ +$sent = $webPush->sendNotification( $notifications[0]['subscription'], $notifications[0]['payload'], // optional (defaults null) true // optional (defaults false) From 5b344b2caccbd31d175879ce0b57637dba331b8b Mon Sep 17 00:00:00 2001 From: t1gor Date: Thu, 6 Sep 2018 12:59:56 +0300 Subject: [PATCH 4/7] Attempt to fix tests #1 --- tests/PushServiceTest.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tests/PushServiceTest.php b/tests/PushServiceTest.php index c7769614..57be00c3 100644 --- a/tests/PushServiceTest.php +++ b/tests/PushServiceTest.php @@ -178,7 +178,12 @@ protected function createClosureTest($browserId, $browserVersion, $options) try { $sendResp = $this->webPush->sendNotification($subscription, $payload, true); - $this->assertTrue($sendResp); + $this->assertInstanceOf(\Generator::class, $sendResp); + + /** @var \Minishlink\WebPush\MessageSentReport $report */ + foreach ($sendResp as $report) { + $this->assertTrue($report->isSuccess()); + } $dataString = json_encode([ 'testSuiteId' => self::$testSuiteId, From 57db806e41d05c10dce1f5e49c2dd8f4ee5ee416 Mon Sep 17 00:00:00 2001 From: t1gor Date: Mon, 5 Nov 2018 11:01:24 +0300 Subject: [PATCH 5/7] Change flush() signature for easier mocking --- src/WebPush.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/WebPush.php b/src/WebPush.php index 898cab34..357544a5 100644 --- a/src/WebPush.php +++ b/src/WebPush.php @@ -129,10 +129,10 @@ public function sendNotification(Subscription $subscription, ?string $payload = * * @param null|int $batchSize Defaults the value defined in defaultOptions during instantiation (which defaults to 1000). * - * @return \Generator + * @return \iterable * @throws \ErrorException */ - public function flush(?int $batchSize = null) : \Generator + public function flush(?int $batchSize = null) : \iterable { if (empty($this->notifications)) { yield from []; From 7ab177120bbde6a8db3efdf2140108ff8d0e8739 Mon Sep 17 00:00:00 2001 From: t1gor Date: Mon, 5 Nov 2018 11:41:23 +0300 Subject: [PATCH 6/7] Unqualify iterable --- src/WebPush.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/WebPush.php b/src/WebPush.php index 357544a5..ffd8f909 100644 --- a/src/WebPush.php +++ b/src/WebPush.php @@ -129,10 +129,10 @@ public function sendNotification(Subscription $subscription, ?string $payload = * * @param null|int $batchSize Defaults the value defined in defaultOptions during instantiation (which defaults to 1000). * - * @return \iterable + * @return iterable * @throws \ErrorException */ - public function flush(?int $batchSize = null) : \iterable + public function flush(?int $batchSize = null) : iterable { if (empty($this->notifications)) { yield from []; From 1c9b44bfacad11c5354bce9aa15f717077d969ec Mon Sep 17 00:00:00 2001 From: t1gor Date: Mon, 26 Nov 2018 12:12:45 +0300 Subject: [PATCH 7/7] Remove ignored vagrant file --- Vagrantfile | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 Vagrantfile diff --git a/Vagrantfile b/Vagrantfile deleted file mode 100644 index e69de29b..00000000