Catch external requests in Laravel Telescope - laravel

Is it possible to catch external requests in Laravel Telescope. I'm new to telescope and I've done my research but I couldn't find any blog/article that mentioned this except this but it didn't work for me
I've Installed telescope on my app according to the documentation, Iv'e created a new watcher called GuzzleRequestWatcher and registered it under config/telescope.php, I've also created a test route that sends an http::post message to this. Telescope is catching my API request and recording it under requests as shown in the screenshot but I need it to see the URL the request is hitting not only the route for example rather than showing in Path '/api/v1/guzzle-test' I need it to show the URL I'm requesting 'http://httpbin.org/anything'.screenshot
<?php
declare(strict_types=1);
namespace App\Telescope\Watchers;
use Closure;
use GuzzleHttp\Client;
use GuzzleHttp\TransferStats;
use Illuminate\Foundation\Application;
use Illuminate\Support\Facades\Log;
use Laravel\Telescope\IncomingEntry;
use Laravel\Telescope\Telescope;
use Laravel\Telescope\Watchers\FetchesStackTrace;
use Laravel\Telescope\Watchers\Watcher;
final class GuzzleRequestWatcher extends Watcher
{
use FetchesStackTrace;
public function register($app)
{
$app->bind(Client::class, $this->buildClient($app));
}
private function buildClient(Application $app): Closure
{
return static function (Application $app): Client {
$config = $app['config']['guzzle'] ?? [];
if (Telescope::isRecording()) {
$config['on_stats'] = function (TransferStats $stats) {
$caller = $this->getCallerFromStackTrace();
Telescope::recordQuery(
IncomingEntry::make([
'connection' => 'guzzle',
'bindings' => [],
'sql' => (string) $stats->getEffectiveUri(),
'time' => number_format(
$stats->getTransferTime() * 1000,
2,
''
),
'slow' => $stats->getTransferTime() > 1,
'file' => $caller['file'],
'line' => $caller['line'],
'hash' => md5((string) $stats->getEffectiveUri()),
])
);
};
}
return new Client(
$config
);
};
}
}

Related

How with breeze to make feature test for forgot-password?

In laravel 9, breeze 1.11 app I want to make feature test for forgot-password functionality and in routes I found :
GET|HEAD
In laravel 9, breeze 1.11 app I want to make feature test for forgot-password functionality and in routes I found :
GET|HEAD forgot-password ... password.request › Auth\PasswordResetLinkController#create
POST forgot-password ........ password.email › Auth\PasswordResetLinkController#store
So I make :
test to check opened form :
public function testAdminForgetPasswordFormOpened()
{
$response = $this->get(route('password.request'));
$response->assertStatus(200);
$response->assertViewIs('auth.forgot-password');
$response->assertSessionHasNoErrors();
}
and it works ok. But I failed to check how token is sent when user submit form with email entered. I do :
public function testAdminGotPasswordResetLinkEmail()
{
Notification::fake();
$loggedAdmin = User::factory()->make();
$response = $this->post(route('password.email'), [
'email' => $loggedAdmin->email,
]);
$token = DB::table('password_resets')->first();
Notification::assertSentTo(
$loggedAdmin,
SubscriptionEmailingNotification::class,// that is my my Notification class
function ($notification) use ($token) { // https://laravel.com/docs/9.x/mocking#notification-fake
\Log::info(varDump($notification, ' -1 $notification::')); / I DO NOT SEE THESE LOG MESSAGES
\Log::info(varDump($token, ' -12 $token::'));
return Hash::check($notification->token, $token->token) === true;
}
);
}
But I got error :
1) Tests\Feature\AuthTest::testAdminGotPasswordResetLinkEmail
The expected [App\Notifications\SubscriptionEmailingNotification] notification was not sent.
Failed asserting that false is true.
/mnt/_work_sdb8/wwwroot/lar/MngProducts/vendor/laravel/framework/src/Illuminate/Support/Testing/Fakes/NotificationFake.php:83
/mnt/_work_sdb8/wwwroot/lar/MngProducts/vendor/laravel/framework/src/Illuminate/Support/Facades/Facade.php:338
/mnt/_work_sdb8/wwwroot/lar/MngProducts/tests/Feature/AuthTest.php:226
Looking how it works in breeze I see method :
$status = Password::sendResetLink(
$request->only('email')
);
I did not find how method above is implemented and which notification it uses ?
I suppose that some notification is used here, but not sure...
I found declaration of assertSentTo method as :
public static function assertSentTo($notifiable, $notification, $callback = null)
{
How that tests must be done ?
Thanks!
You shouldn't check the token in the email if you continue to use the Password::sendResetLink(...) method. Breeze already has its own tests for this. Proving the Password::sendResetLink(...) method is successfully called will be enough to confirm faultless integration. You can verify it by checking the "ResetPassword" notification:
Notification::assertSentTo($user, ResetPassword::class);
However, if you still want to check the token, you can use the sample code below, which took from Breeze's tests
use App\Models\User;
use Illuminate\Auth\Notifications\ResetPassword;
use Illuminate\Support\Facades\Notification;
// ...
public function test_password_can_be_reset_with_valid_token()
{
Notification::fake();
$user = User::factory()->create();
$this->post('/forgot-password', ['email' => $user->email]);
Notification::assertSentTo($user, ResetPassword::class, function ($notification) use ($user) {
$response = $this->post('/reset-password', [
'token' => $notification->token,
'email' => $user->email,
'password' => 'password',
'password_confirmation' => 'password',
]);
$response->assertSessionHasNoErrors();
return true;
});
}
Source: https://github.com/laravel/breeze/blob/v1.11.0/stubs/default/tests/Feature/PasswordResetTest.php#L50

Laravel API -Send Notifications to android and iOS client apps with Firebase FCM

I am working on a project which uses android and iOS as front and Backend as Laravel APIs.
So I want to send notifications to all users (include android/iOS) when some event happen like New Offer Created/New Stock available.. So to do this is have gone through some google searches and finally knew that the curl approach seems to be deprecated.. so Is there any better Way to integrate firebase FCM into my Laravel project.. if it so How?
I hope someone can answer this..
Thanks in Advance
Fcm for core already answered here How to send Notification to Android from php?
so in laravel you can
create a config file in config folder and name it as firebase.php
<?php
return [
'fcm_url'=>env('FCM_URL'),
'fcm_api_key'=>env('FCM_API_KEY'),
];
and in env file
FCM_URL=https://fcm.googleapis.com/fcm/send
FCM_API_KEY=
and in code you can create Trait class
<?php
namespace App\Traits;
use Illuminate\Support\Facades\Http;
trait Firebase
{
public function firebaseNotification($fcmNotification){
$fcmUrl =config('firebase.fcm_url');
$apiKey=config('firebase.fcm_api_key');
$http=Http::withHeaders([
'Authorization:key'=>$apiKey,
'Content-Type'=>'application/json'
]) ->post($fcmUrl,$fcmNotification);
return $http->json();
}
}
Then you can include this trait any class where you want to call
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use App\Providers\RouteServiceProvider;
use App\Traits\Firebase;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
class LoginController extends Controller
{
use Firebase,AuthenticatesUsers;
public function sendNotification(){
$token="";
$notification = [
'title' =>'title',
'body' => 'body of message.',
'icon' =>'myIcon',
'sound' => 'mySound'
];
$extraNotificationData = ["message" => $notification,"moredata" =>'dd'];
$fcmNotification = [
//'registration_ids' => $tokenList, //multple token array
'to' => $token, //single token
'notification' => $notification,
'data' => $extraNotificationData
];
return $this->firebaseNotification($fcmNotification);
}
}
Also i suggest to create events based to better code optimization
Also read
https://firebase.google.com/docs/cloud-messaging/http-server-ref
registration_ids for multiple users
This parameter specifies the recipient of a multicast message, a
message sent to more than one registration token.
The value should be an array of registration tokens to which to send
the multicast message. The array must contain at least 1 and at most
1000 registration tokens. To send a message to a single device, use
the to parameter.
Multicast messages are only allowed using the HTTP JSON format.
For new Version as mentioned in comment by #JEJ
https://firebase.google.com/docs/cloud-messaging/migrate-v1#python_1
So it will be
$http=Http::withHeaders([
'Authorization'=>'Bearer '.$apiKey,
'Content-Type'=>'application/json; UTF-8'
]) ->post($fcmUrl,$fcmNotification);
return $http->json();
and simple notification message for $fcmNotification
{
"message": {
"topic": "news",
"notification": {
"title": "Breaking News",
"body": "New news story available."
},
"data": {
"story_id": "story_12345"
}
}
}
for targeting multiple platforms
{
"message": {
"topic": "news",
"notification": {
"title": "Breaking News",
"body": "New news story available."
},
"data": {
"story_id": "story_12345"
},
"android": {
"notification": {
"click_action": "TOP_STORY_ACTIVITY"
}
},
"apns": {
"payload": {
"aps": {
"category" : "NEW_MESSAGE_CATEGORY"
}
}
}
}
}
Try Laravel FCM package
For your ease, writing steps here...
SETUP
Installation (terminal)
composer require brozot/laravel-fcm
config/app.php
providers
'providers' => [
// ...
LaravelFCM\FCMServiceProvider::class,
]
aliases
'aliases' => [
...
'FCM' => LaravelFCM\Facades\FCM::class,
]
Publish the package config file (terminal)
php artisan vendor:publish --provider="LaravelFCM\FCMServiceProvider"
USAGE
In your Controller,
import libraries
use LaravelFCM\Message\OptionsBuilder;
use LaravelFCM\Message\PayloadDataBuilder;
use LaravelFCM\Message\PayloadNotificationBuilder;
use FCM;
sending Downstream Message to device(s)
$optionBuilder = new OptionsBuilder();
$optionBuilder->setTimeToLive(60*20);
$notificationBuilder = new PayloadNotificationBuilder('my title');
$notificationBuilder->setBody('Hello world')->setSound('default');
$dataBuilder = new PayloadDataBuilder();
$dataBuilder->addData(['a_data' => 'my_data']);
$option = $optionBuilder->build();
$notification = $notificationBuilder->build();
$data = $dataBuilder->build();
$token = "a_registration_from_your_database" /* OR */ [ /* Array of tokens */ ];
$downstreamResponse = FCM::sendTo($token, $option, $notification, $data);
$downstreamResponse->numberSuccess();
$downstreamResponse->numberFailure();
$downstreamResponse->numberModification();
// return Array - you must remove all this tokens in your database
$downstreamResponse->tokensToDelete();
// return Array (key : oldToken, value : new token - you must change the token in your database)
$downstreamResponse->tokensToModify();
// return Array - you should try to resend the message to the tokens in the array
$downstreamResponse->tokensToRetry();
// return Array (key:token, value:error) - in production you should remove from your database the tokens
$downstreamResponse->tokensWithError();

Laravel dusk with browserstack to run tests on multiple devices and browsers

I am using this ("browserstack/browserstack-local": "^1.1") package to run dusk tests on BrowserStack. Now the requirement is to run tests on multiple and different devices with different browsers. Currently, I am following this approach to run tests.
private function browserStackCaps($local_identifier)
{
return [
'project' => config('app.name'),
'browserstack.local' => 'true',
'browser' => env('BROWSER'),
'device' => env('DEVICE'),
'acceptSslCert' => true,
'resolution' => '1920x1080'
];
}
The drawback of this approach is I have to change the device name and browser name in the .env file every time I need to run tests on a different device/browser. Is there any way I can run tests on the provided array? The array that contains devices and browser information.
I know this is old, but I found this page while searching for a solution. I ended up building one myself that would probably meet your use-case. The biggest hurdle that I had was $this->browse() in a normal Dusk test was using a single instance of Laravel\Dusk\Browser and the new capabilities were not being pulled in. This implementation adds a function called performTest to the DuskTestCase.php file. This function loops through a set of capabilities and instantiates a new instance of Laravel\Dusk\Browser for each test. This function works similarly to the existing browse function in Laravel Dusk. You call performTest by passing it a callable that accepts a single parameter which is an instance of Laravel\Dusk\Browser
Dusk Test Case
<?php
namespace Tests;
use Laravel\Dusk\TestCase as BaseTestCase;
use Facebook\WebDriver\Remote\RemoteWebDriver;
use Facebook\WebDriver\Remote\DesiredCapabilities;
abstract class DuskTestCase extends BaseTestCase
{
use CreatesApplication;
protected array $capabilities;
private const BROWSERS = [
'ios_14_iphone_xs_safari' => [
"os_version" => "14",
"device" => "iPhone XS",
"real_mobile" => "true",
"browserstack.local" => "true",
'acceptSslCerts' => 'true'
],
'mac_osx_catalina_safari' => [
"os" => "OS X",
"os_version" => "Catalina",
"browser" => "Safari",
"browser_version" => "13.0",
"browserstack.local" => "true",
"browserstack.selenium_version" => "3.14.0",
"resolution" => "1920x1080",
'acceptSslCerts' => 'true',
]
];
/**
* Create the RemoteWebDriver instance.
*
* #return \Facebook\WebDriver\Remote\RemoteWebDriver
*/
protected function driver()
{
$browserStackConnectionUrl = config('browserstack.connection_url');
return RemoteWebDriver::create(
$browserStackConnectionUrl, $this->capabilities
);
}
protected function performTest(Callable $test){
foreach(self::BROWSERS as $browserName => $capabilitySet){
try {
$this->capabilities = $capabilitySet;
$browser = $this->newBrowser($this->driver());
$test($browser);
$browser->quit();
fprintf(STDOUT, "\e[0;32m√ {$browserName}\r\n");
}
catch(\Exception $exception){
fprintf(STDOUT, "\e[0;31mX {$browserName}\r\n");
throw $exception;
}
}
}
}
Example Test
<?php
namespace Tests\Browser;
use Tests\DuskTestCase;
use Laravel\Dusk\Browser;
class ExampleTest extends DuskTestCase
{
public function testExample()
{
$this->performTest(function(Browser $browser){
$browser->visit('/')
->assertDontSee('Foobar');
});
}
}
config/browserstack.php
<?php
return [
'connection_url' => env('BROWSERSTACK_CONNECTION_URL')
];
you can implement this at your end. Fetch the list of Browsers and Devices you want to execute your tests on using the REST API and use the same.
REST API to be used:
curl -u "username:password"
https://api.browserstack.com/automate/browsers.json
Read more on this here:
https://www.browserstack.com/docs/automate/api-reference/selenium/browser#get-browser-list

Laravel 5 create middleware with oauth2 server check

I have implemented this oauth server http://bshaffer.github.io/oauth2-server-php-docs/
It has a Laravel implementation : http://bshaffer.github.io/oauth2-server-php-docs/cookbook/laravel/
This guide you and gives that code for routes :
App::singleton('oauth2', function() {
$storage = new OAuth2\Storage\Pdo(array('dsn' => 'mysql:dbname=oauth2;host=localhost', 'username' => 'root', 'password' => 'root'));
$server = new OAuth2\Server($storage);
$server->addGrantType(new OAuth2\GrantType\ClientCredentials($storage));
$server->addGrantType(new OAuth2\GrantType\UserCredentials($storage));
return $server;
});
Route::get('private', function()
{
$bridgedRequest = OAuth2\HttpFoundationBridge\Request::createFromRequest(Request::instance());
$bridgedResponse = new OAuth2\HttpFoundationBridge\Response();
// fix for laravel
$bridgedRequest->request = new \Symfony\Component\HttpFoundation\ParameterBag();
$rawHeaders = getallheaders();
if (isset($rawHeaders["Authorization"])) {
$authorizationHeader = $rawHeaders["Authorization"];
$bridgedRequest->headers->add([ 'Authorization' => $authorizationHeader]);
}
if (App::make('oauth2')->verifyResourceRequest($bridgedRequest, $bridgedResponse)) {
$token = App::make('oauth2')->getAccessTokenData($bridgedRequest);
return Response::json(array(
'private' => 'stuff',
'user_id' => $token['user_id'],
'client' => $token['client_id'],
'expires' => $token['expires'],
));
}
else {
return Response::json(array(
'error' => 'Unauthorized'
), $bridgedResponse->getStatusCode());
}
});
It works perfectly well like that. Now I want to transform that check function in the "private" route to a middleware I could apply to each necessary route. I created the middleware using
php artisan make:middleware AuthChecker
Added it to the kernel.php, and pasted the code of the verification function inside of it. And I immediately got an error :
FatalErrorException in AuthChecker.php line 17:
Class 'Oauth2\HttpFoundationBridge\Request' not found
So, I guess I will have to "use" things, but since I'm still a beginner I don't really know what to do...
Thanks ahead for your help !
[EDIT] the content of the middleware currently look like this :
namespace App\Http\Middleware;
use Closure;
class OauthCheck {
public function handle($request, Closure $next)
{
$bridgedRequest = OAuth2\HttpFoundationBridge\Request::createFromRequest($request);
$bridgedResponse = new OAuth2\HttpFoundationBridge\Response();
// fix for laravel
$bridgedRequest->request = new \Symfony\Component\HttpFoundation\ParameterBag();
$rawHeaders = getallheaders();
if (isset($rawHeaders["Authorization"])) {
$authorizationHeader = $rawHeaders["Authorization"];
$bridgedRequest->headers->add([ 'Authorization' => $authorizationHeader]);
}
if (App::make('oauth2')->verifyResourceRequest($bridgedRequest, $bridgedResponse)) {
$token = App::make('oauth2')->getAccessTokenData($bridgedRequest);
return Response::json(array(
'private' => 'stuff',
'user_id' => $token['user_id'],
'client' => $token['client_id'],
'expires' => $token['expires'],
));
return $next($request);
}
else {
return Response::json(array(
'error' => 'Unauthorized'
), $bridgedResponse->getStatusCode());
}
}
}
Thanks again
FatalErrorException in AuthChecker.php line 17:
Class 'Oauth2\HttpFoundationBridge\Request' not found
So you want to use the Request class from Oauth2\HttpFoundationBridge namespace to your OauthCheck class from App\Http\Middleware.
You can do it in either ways:
Import the class
namespace App\Http\Middleware;
use Oauth2\HttpFoundationBridge\Request;
class OauthCheck {
public function handle($request, Closure $next)
{
$bridgedRequest = Request::createFromRequest($request);
....
}
}
Use the class explicitly
namespace App\Http\Middleware;
class OauthCheck {
public function handle($request, Closure $next)
{
$bridgedRequest = \Oauth2\HttpFoundationBridge\Request::createFromRequest($request);
....
}
}
Take note of the backslash before Oauth2\HttpFoundationBridge\Request. If you just say $bridgedRequest = Oauth2\HttpFoundationBridge\Request, then PHP will look for App\Http\Middleware\Oauth2\HttpFoundationBridge\Request.

ZF2 - Saving a result of a function in cache

I made a view helper that checks if an external URL exists before outputting it. Some of those URLs are in my main layout, so that check is quite slowing down my site by calling all those urls all the times, to check if they exist. I would like to save the output of that function so that it only checks an URL if the same one hasn't been checked already in less than an hour, or a day. I believe I should use Zend Cache to do that? But I have no idea where to start, do you have any suggestions, easy solutions or some basic tutorial to learn? Thanks!
Add global config for cache service, like here:
config/autoload/global.php
'service_manager' => array(
'abstract_factories' => array(
'Zend\Cache\Service\StorageCacheAbstractServiceFactory',
)
),
config/autoload/cache.global.php
return array(
'caches' => array(
// Cache config
)
)
Use factory to create your View Helper:
Application/Module.php::getViewHelperConfig()
'LinkHelper' => function ($sm) {
$locator = $sm->getServiceLocator();
return new LinkHelper($locator->get('memcached'))
}
Use cache service in your View Helper:
LinkHelper.php
protected $cache;
public function __construct($cache)
{
$this->cache = $cache;
}
public function __invoke($url)
{
$cacheKey = md5($url);
if ($this->cache->hasItem($cacheKey) {
return $this->cache->getItem($cacheKey);
}
$link = ''; // Parse link
$this->cache->setItem($cacheKey, $link);
return $link;
}

Resources