Laravel testing on demand notification - php

I have a slack notification class that sends a message inside our company slack account, in a specific channel, every time an user performs the activation process.
The system works, but it's manually tested and that's not cool.
The notification is sent by a listener attached to an UserHasBeenActivated event, the listener is the following:
public function handle(UserHasBeenActivated $event)
{
Notification::route("slack", config("services.slack.user.url"))
->notify(new UserActivated($event->user));
}
Pretty straight forward. The problem here is that the notification is on demand thus it's difficult to test... because there isn't any sort of documentation on how to test on demand notifications!
At the moment I'm stuck here:
public function it_sends_a_notification_when_an_user_is_activated()
{
Notification::fake();
$user = factory(User::class)->states("deleted")->create();
$user->activate();
Notification::assertSentTo(
$user,
UserActivated::class
);
}
Of course this test fails, the activate() method is what triggers the Event UserHasBeenActivated and sequentially all the listeners, and one of them sends the corresponding notification.
Do you know how to test on demand Notifications? Is there any hidden API that am I missing?

For the newcomers
Laravel introduces in v5.5.14 the ability to test anonymous notification by using the provided Notification::fake() system.
More about this new feature here: https://github.com/laravel/framework/pull/21379

Related

Symfony back ground activity after to send response error session

I'm using Symfony 4.4 with Redis for the session.
I have some controllers and I wish to update the DB on back ground after to send a reply to user.
So I have written a code like this:
class GetCatController extends AbstractController
{
public function getCatController(LoggerInterface $logger, ManagerRegistry $doctrine, SessionInterface $session, ValidatorInterface $validator)
{
[...]
$replyToSend = new JsonResponse($reply, 200);
$replyToSend->send();
//My back ground activity like to do some query on the db.
[...]
return null;
}
But I have some problems about the sessions.
Is there a better way to create and run background activity sending before the reply to user?
There are two decent ways to do this.
If you are running PHP under php-fpm (not mod_php), you can dispatch & catch an event, kernel.terminate In Symfony pre-4.4, this is called PostResponseEvent (TerminateEvent from 4.4/5.0).
The better choice may be with Symfony Messenger. Here, you would create a message object, with all the information needed to perform the task, and send it to a background queue (Redis is supported as a queue). A worker then consumes that message, and does the task.

Send response but keep long running script going to prevent timeout?

I am wondering how to deal with this. I have a webhook endpoint which responds to a webhook call from Github.
It starts a long running process in where it clones the repository from which the webhook call was made.
/**
* The webhook endpoint.
*
* #param Request $request
* #return mixed
* #throws \Exception
*/
public function webhook(Request $request)
{
// The type of GitHub event that we receive.
$event = $request->header('X-GitHub-Event');
$url = $this->createCloneUrl();
$this->cloneGitRepo($url);
return new Response('Webhook received succesfully', 200);
}
The problem with this is that Github gives an error when the response is not provided soon enough.
We couldn’t deliver this payload: Service Timeout
This is rightfully so because my cloneGitRepo method is simply blocking the execution of the response and it is taking too long.
How can I still deliver a response to acknowledge to Github that the webhook call has been made successfully and start my long running process?
I am using Laravel for all of this with Redis, maybe something can be accomplished there? I am open to all suggestions.
What you're looking for is a queued job. Laravel makes this very easy with Laravel Queues.
With queues, you setup a queue driver (database, redis, Amazon SQS, etc), and then you have one to many queue workers that are continuously running. When you put a job on the queue from your webhook method, it will be picked up by one of your queue workers and run in a separate process. The act of dispatching a queued job to a queue is very quick, though, so your webhook method will return quickly while the real work is being done by the queue worker.
The linked documentation has all the details, but the general process will be:
Setup a queue connection. You mention you're already using redis, I would start with that.
Use php artisan make:job CloneGitRepo to create a CloneGitRepo job class.
It should implement the Illuminate\Contracts\Queue\ShouldQueue interface so that Laravel knows to send this job to a queue when it is dispatched.
Make sure to define properties on the class for any data you pass into the constructor. This is necessary so the worker can rebuild the job correctly when it is pulled off the queue.
The queue worker will call the handle() method to process the job. Any dependencies can be type hinted here and they will be injected from the IoC container.
To dispatch the job to the queue, you can either use the global dispatch() helper function, or call the static dispatch() method on the job itself.
dispatch(new CloneGitRepo($url));
CloneGitRepo::dispatch($url);
So, your webhook would look like:
public function webhook(Request $request)
{
// The type of GitHub event that we receive.
$event = $request->header('X-GitHub-Event');
$url = $this->createCloneUrl();
CloneGitRepo::dispatch($url);
return new Response('Webhook received succesfully', 200);
}

Laravel: Load a page and then send a mail

i am trying to load page first and then activate the $mailer to send email. because when i click on to go to next page its taking time, because its sending emails and then it is loading so,
what is the best way to do it. or any way. because i cant figure it out.
here is snippet
public function sInterest($project_id, AppMailer $mailer)
{
$project = Project::findOrFail($project_id);
if($project->investment){
$mailer->sendInterestNotificationI($user, $project);
$mailer->sendInterestNotificationD($project, $user);
$mailer->sendInterestNotificationA($project, $user);
return view('projects.offer', compact('project'));
}
}
is there a way $mailer to activate after returning a page?
In your AppMailer sendInterestNotification*() methods, replace sync email delivery with queued email delivery (see Queueing Mail in Laravel documentation)
Then page will be returned instantly, emails will be put in the corresponding queue. You will have to edit .env file to change the QUEUE driver and to start a queue listener as a separate process, detailed documentation is given on Laravel website
There is no way you can return a page to browser and then run some extra commands in your controller.

ZF2 second round of bootstrapping

How would I be able to subscribe to events in a way such that a second round of bootstrapping would be possible in one of my modules, making sure all modules have had their onBootstrap() methods already called?
I have already tried subscribing to the same onBootstrap() event from within my Module's onBootstrap(), but with a lower priority. That didn't work; apparently the events that are to be triggered are determined before triggering any, and therefore you cannot subscribe to the same event that is currently being triggered and expect it to work.
I also wanted to try to subscribe to loadModules.post within init(), and then subscribe to EVENT_BOOTSTRAP, but I realized I couldn't find any way to access $mvcEvent, and in turn, $application, and in turn, the application event manager where the subscription needs to take place on.
I had to do exactly this just the other day.
You're right that you can't attach in the onBootstrap event, you need to attach a listener in the init method instead, and you need to use the shared manager to listen to the MvcEvent being triggered by the application
public function init(ModuleManager $modules)
{
// attach to the end of the bootstrap event
$modules->getEventManager()
->getSharedManager()
->attach('Zend\Mvc\Application', MvcEvent::EVENT_BOOTSTRAP, function ($e) {
// do something after everything else has bootstrapped
}, -1000);
}

How to implement "Push to an Existing Site" using ZeroMQ and ClankBundle?

I'm currently using ClankBundle in my Symfony2 app and need to implement the Push to an Existing Site feature mentioned in the Ratchet documentation. However, I found no clues on how to accomplish this functionality using ClankBundle !
I have successfully implemented the Topic Handler Setup from the ClankBundle documentation, but need a way to add support for ZeroMQ.
Could you please demonstrate (preferably with code) how would one use ZeroMQ with Clankbundle?
I've found a workaround that might be useful to you too.
In my setup I have Periodic service that needs to broadcast new information to topic and I have custom topic handler.
First off inject custom topic handler in periodic service and save all subscribed topics in topic handler, something like this:
public function onSubscribe(Conn $conn, $topic)
{
if (!array_key_exists($topic->getId(), self::$subscribedTopics)) {
self::$subscribedTopics[$topic->getId()] = $topic;
}
$topic->broadcast('connected');
}
public function getSubscribedTopics() {
return self::$subscribedTopics;
}
In periodic fetch those topics and broadcast on them:
public function tick()
{
$subscribedTopics = $this->topic->getSubscribedTopics();
foreach($subscribedTopics as $subscribedTopic) {
$subscribedTopic->broadcast('yey');
}
}

Categories