Skip to content

Commit

Permalink
Merge pull request #10 from renoki-co/feature/actions
Browse files Browse the repository at this point in the history
[feature] Improved Code & CI
  • Loading branch information
rennokki authored Apr 29, 2020
2 parents 51d7942 + f3dd4dc commit 46f8b4e
Show file tree
Hide file tree
Showing 13 changed files with 276 additions and 63 deletions.
15 changes: 15 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
root = true

[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
indent_style = space
indent_size = 4
trim_trailing_whitespace = true

[*.md]
trim_trailing_whitespace = false

[*.yml]
indent_size = 2
35 changes: 35 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
name: CI

on:
push:
branches:
- '*'
tags:
- '*'
pull_request:
branches:
- '*'

jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
php: ['7.0', '7.1', '7.2', '7.3', '7.4']
name: PHP ${{ matrix.php }}
steps:
- uses: actions/checkout@v1
- uses: actions/cache@v1
name: Cache dependencies
with:
path: ~/.composer/cache/files
key: composer-php-${{ matrix.php }}-${{ hashFiles('composer.json') }}
- name: Install dependencies
run: |
composer install --no-interaction --prefer-source
- name: Run tests
run: |
phpunit --coverage-text --coverage-clover=coverage.xml
- uses: codecov/codecov-action@v1
with:
fail_ci_if_error: true
23 changes: 0 additions & 23 deletions .travis.yml

This file was deleted.

File renamed without changes.
47 changes: 21 additions & 26 deletions readme.md → README.md
Original file line number Diff line number Diff line change
@@ -1,35 +1,38 @@
[![Build Status](https://travis-ci.org/rennokki/laravel-sns-events.svg?branch=master)](https://travis-ci.org/rennokki/laravel-sns-events)
[![codecov](https://codecov.io/gh/rennokki/laravel-sns-events/branch/master/graph/badge.svg)](https://codecov.io/gh/rennokki/laravel-sns-events/branch/master)
![CI](https://github.com/renoki-co/laravel-sns-events/workflows/CI/badge.svg?branch=master)
[![codecov](https://codecov.io/gh/renoki-co/laravel-sns-events/branch/master/graph/badge.svg)](https://codecov.io/gh/renoki-co/laravel-sns-events/branch/master)
[![StyleCI](https://github.styleci.io/repos/189254977/shield?branch=master)](https://github.styleci.io/repos/189254977)
[![Latest Stable Version](https://poser.pugx.org/rennokki/laravel-sns-events/v/stable)](https://packagist.org/packages/rennokki/laravel-sns-events)
[![Total Downloads](https://poser.pugx.org/rennokki/laravel-sns-events/downloads)](https://packagist.org/packages/rennokki/laravel-sns-events)
[![Monthly Downloads](https://poser.pugx.org/rennokki/laravel-sns-events/d/monthly)](https://packagist.org/packages/rennokki/laravel-sns-events)
[![License](https://poser.pugx.org/rennokki/laravel-sns-events/license)](https://packagist.org/packages/rennokki/laravel-sns-events)

# Laravel SNS Events
Laravel SNS Events allow you to listen to SNS webhooks via Laravel Events. It implements a controller that is made to properly listen to SNS HTTP(s) webhooks and trigger events on which you can listen to in Laravel.
Laravel SNS Events allow you to listen to SNS webhooks via Laravel Events. It leverages a controller that is made to properly listen to SNS HTTP(s) webhooks and trigger events on which you can listen to in Laravel.

If you are not familiar with Laravel Events & Listeners, make sure you check the [documentation section on laravel.com](https://laravel.com/docs/5.8/events) because this package will need you to understand this concept.
If you are not familiar with Laravel Events & Listeners, make sure you check the [documentation section on Laravel Documentation](https://laravel.com/docs/master/events) because this package will need you to understand this concept.

## Install
```bash
$ composer require rennokki/laravel-sns-events
```

## Usage
The package comes with two event classes:
There are two classes that get triggered, depending on the request sent by AWS:

* `Rennokki\LaravelSnsEvents\Events\SnsEvent` - triggered on each SNS message
* `Rennokki\LaravelSnsEvents\Events\SnsSubscriptionConfirmation` - triggered when the subscription is confirmed

This package comes with a controller that will handle the response for you. Register the route in your `web.php`:
A controller that will handle the response for you should be registered in your routes:

```php
...

// you can choose any route
Route::any('/aws/sns', 'Rennokki\LaravelSnsEvents\Http\Controllers\SnsController@handle');
```

SNS sends data through POST, so you will need to whitelist your route in your `VerifyCsrfToken.php`:
SNS sends data as raw json, so you will need to whitelist your route in your `VerifyCsrfToken.php`:

```php
protected $except = [
...
Expand All @@ -39,9 +42,12 @@ protected $except = [

You will need an AWS account and register a SNS Topic and set up a subscription for HTTP(s) protocol that will point out to the route you just registered.

Make sure to enable RAW JSON format for your SNS Subscription.

If you have registered the route and created a SNS Topic, you should register the URL and click the confirmation button from the AWS Dashboard. In a short while, if you implemented the route well, you'll be seeing that your endpoint is registered.

To process the events, you should add the events in your `app/Providers/EventServiceProvider.php`:

```php
use Rennokki\LaravelSnsEvents\Events\SnsEvent;
use Rennokki\LaravelSnsEvents\Events\SnsSubscriptionConfirmation;
Expand All @@ -60,37 +66,26 @@ protected $listen = [
```

You will be able to access the SNS message from your listeners like this:

```php
class MyListener
{
...

public function handle($event)
{
// $event->message is an array
// $event->message is an array containing the payload sent
// $event->headers is an array containing the headers sent

$content = json_decode($event->message['Message'], true);

// ...
}
}
```

For example, synthetizing an AWS Polly Text-To-Speech would return in `$event->message` an array like this:
```
[
'taskId' => '...',
'taskStatus' => 'FAILED',
'taskStatusReason' => 'Error occurred while trying to upload file to S3. Please verify that the bucket existsin this region and you have permission to write objects to the specified bucket.',
'outputUri' => 's3://...',
'creationTime' => '2019-05-29T15:27:31.231Z',
'requestCharacters' => 58,
'snsTopicArn' => '...',
'outputFormat' => 'Mp3',
'sampleRate' => '22050',
'speechMarkTypes' => [],
'textType' => 'Text',
'voiceId' => 'Joanna',
]
```

## Testing

Run the tests with:

``` bash
Expand Down
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"authors": [
{
"name": "Alex Renoki",
"email": "[email protected]",
"homepage": "https://github.com/rennokki",
"role": "Developer"
}
Expand Down
11 changes: 10 additions & 1 deletion src/Events/SnsEvent.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,23 @@ class SnsEvent
*/
public $message;

/**
* The headers sent through the SNS request.
*
* @var array
*/
public $headers = [];

/**
* Create a new event instance.
*
* @param array $message
* @param array $headers
* @return void
*/
public function __construct($message)
public function __construct($message, $headers = [])
{
$this->message = $message;
$this->headers = $headers;
}
}
12 changes: 10 additions & 2 deletions src/Events/SnsSubscriptionConfirmation.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,21 @@ class SnsSubscriptionConfirmation
{
use Dispatchable, InteractsWithSockets, SerializesModels;

/**
* The headers sent through the SNS request.
*
* @var array
*/
public $headers = [];

/**
* Create a new event instance.
*
* @param array $headers
* @return void
*/
public function __construct()
public function __construct($headers = [])
{
//
$this->headers = $headers;
}
}
41 changes: 30 additions & 11 deletions src/Http/Controllers/SnsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace Rennokki\LaravelSnsEvents\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Routing\Controller;
use Rennokki\LaravelSnsEvents\Events\SnsEvent;
use Rennokki\LaravelSnsEvents\Events\SnsSubscriptionConfirmation;
Expand All @@ -11,22 +12,40 @@ class SnsController extends Controller
/**
* Handle the incoming SNS event.
*
* @param \Illuminate\Http\Request $request
* @return mixed
*/
public function handle()
public function handle(Request $request)
{
$message = json_decode(file_get_contents('php://input'), true);

if (isset($message['Type']) && $message['Type'] === 'SubscriptionConfirmation') {
file_get_contents($message['SubscribeURL']);

event(new SnsSubscriptionConfirmation);

return response('OK', 200);
$message = json_decode($this->getContent($request), true);

if (isset($message['Type'])) {
if ($message['Type'] === 'SubscriptionConfirmation') {
file_get_contents($message['SubscribeURL']);

event(new SnsSubscriptionConfirmation(
$request->headers->all()
));
}

if ($message['Type'] === 'Notification') {
event(new SnsEvent(
$message, $request->headers->all()
));
}
}

event(new SnsEvent($message));

return response('OK', 200);
}

/**
* Get the payload content from the request.
*
* @param \Illuminate\Http\Request $request
* @return null|string
*/
protected function getContent(Request $request)
{
return $request->getContent() ?: file_get_contents('php://input');
}
}
76 changes: 76 additions & 0 deletions tests/EventTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
<?php

namespace Rennokki\LaravelSnsEvents\Tests;

use Illuminate\Support\Facades\Event;
use Rennokki\LaravelSnsEvents\Events\SnsEvent;
use Rennokki\LaravelSnsEvents\Events\SnsSubscriptionConfirmation;

class EventTest extends TestCase
{
public function test_no_event_triggering_on_bad_request()
{
Event::fake();

$this
->json('GET', route('sns'))
->assertSee('OK');

Event::assertNotDispatched(SnsEvent::class);
Event::assertNotDispatched(SnsSubscriptionConfirmation::class);
}

public function test_subscription_confirmation()
{
Event::fake();

$this
->withHeaders([
'x-test-header' => 1,
])
->json('POST', route('sns'), $this->getSubscriptionConfirmationPayload())
->assertSee('OK');

Event::assertNotDispatched(SnsEvent::class);

Event::assertDispatched(SnsSubscriptionConfirmation::class, function ($event) {
$this->assertTrue(
isset($event->headers['x-test-header'])
);

return true;
});
}

public function test_notification_confirmation()
{
Event::fake();

$message = json_encode([
'test' => 1,
'sns' => true,
]);

$this
->withHeaders([
'x-test-header' => 1,
])
->json('POST', route('sns'), $this->getNotificationPayload($message))
->assertSee('OK');

Event::assertNotDispatched(SnsSubscriptionConfirmation::class);

Event::assertDispatched(SnsEvent::class, function ($event) {
$this->assertTrue(
isset($event->headers['x-test-header'])
);

$message = json_decode($event->message['Message'], true);

$this->assertEquals(1, $message['test']);
$this->assertEquals(true, $message['sns']);

return true;
});
}
}
Loading

0 comments on commit 46f8b4e

Please sign in to comment.