Skip to content

Commit

Permalink
Return results as a \Generator (#185)
Browse files Browse the repository at this point in the history
* WIP: initial concept of returning report objects

* WIP: add a test

* Docs update

Change results handling example

* Attempt to fix tests #1

* Change flush() signature for easier mocking

* Unqualify iterable

* Remove ignored vagrant file
  • Loading branch information
t1gor authored and Minishlink committed Nov 27, 2018
1 parent 3727db3 commit eac6697
Show file tree
Hide file tree
Showing 8 changed files with 248 additions and 96 deletions.
23 changes: 18 additions & 5 deletions .github/CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`
Expand Down
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,8 @@
composer.lock
phpunit.xml

# web-push-testing-service logs
cli.log
module.log
.vagrant
Vagrantfile # temp, may be?
2 changes: 2 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
22 changes: 19 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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<MessageSentReport> $sent
*/
$sent = $webPush->sendNotification(
$notifications[0]['subscription'],
$notifications[0]['payload'], // optional (defaults null)
true // optional (defaults false)
Expand Down
131 changes: 131 additions & 0 deletions src/MessageSentReport.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
<?php
/**
* @author Igor Timoshenkov [[email protected]]
* @started: 03.09.2018 9:21
*/

namespace Minishlink\WebPush;

use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;

/**
* Standardized response from sending a message
*/
class MessageSentReport {

/**
* @var boolean
*/
protected $success;

/**
* @var RequestInterface
*/
protected $request;

/**
* @var ResponseInterface
*/
protected $response;

/**
* @var string
*/
protected $reason;

/**
* @param RequestInterface $request
* @param ResponseInterface $response
* @param bool $success
* @param string $reason
*/
public function __construct(?RequestInterface $request = null, ?ResponseInterface $response = null, bool $success = true, $reason = 'OK') {
$this->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;
}
}
110 changes: 35 additions & 75 deletions src/WebPush.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand Down Expand Up @@ -121,93 +121,53 @@ 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;
}

/**
* 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
*/
public function flush(?int $batchSize = null)
/**
* Flush notifications. Triggers the requests.
*
* @param null|int $batchSize Defaults the value defined in defaultOptions during instantiation (which defaults to 1000).
*
* @return iterable
* @throws \ErrorException
*/
public function flush(?int $batchSize = null) : iterable
{
if (empty($this->notifications)) {
return false;
yield from [];
}

if (null === $batchSize) {
$batchSize = $this->defaultOptions['batchSize'];
}

$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,
];
}
}
}

// reset queue
$this->notifications = null;
// reset queue
$this->notifications = [];

return $completeSuccess ? true : $return;
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;
}
}
}

/**
Expand Down
7 changes: 6 additions & 1 deletion tests/PushServiceTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Loading

0 comments on commit eac6697

Please sign in to comment.