Open johnRivs opened 2 years ago
Hello @johnRivs ,
I'm with the same issue, maybe we can think together.
I tried cleanup from tinker with:
Storage::disk('google')->getDriver()->getAdapter()->getService()->getClient()->getCache()->clear()
And don't works, but let's think in some points.
I saw that GoogleAPI package did use the PSR CacheItemPoolInterface
to deal with the cache.
I don't sure if, when the process queue:worker
is running, it use the same cache of my tinker, I guess not.
I want mean that, I think is not about default cache defined of application with CACHE_DRIVER and the process running has your own cache.
I dont try yet, but if the JOB run the cleanup command with ->getCache()->clear()
, this will cleanup the process cache of driver? What do you think?
One bad way to fix it fast, is try run queue:restart
inside our job. Because, when the worker is restarted, works. Because, I guess, the service provider is registered from zero.
This is what I ended up doing.
I have a class responsible for getting a new access token:
<?php
namespace App\Services;
use Illuminate\Support\Facades\Cache;
class Google
{
static function refreshToken()
{
$credentials = app('googleClient')->refreshToken(config('filesystems.disks.google.refreshToken'));
Cache::put('google-drive-credentials', $credentials);
}
}
It's scheduled to run every 30 minutes:
<?php
namespace App\Console;
use App\Services\Google;
use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
class Kernel extends ConsoleKernel
{
function schedule(Schedule $schedule)
{
$schedule->call(function () {
Google::refreshToken();
})->everyThirtyMinutes();
}
}
In EventServiceProvider.php
, when a CompleteOrder
job is put into the queue, I register the driver. When it's done processing, I remove the driver:
<?php
namespace App\Providers;
use App\Jobs\CompleteOrder;
use League\Flysystem\Filesystem;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\Storage;
use Illuminate\Queue\Events\JobProcessed;
use Illuminate\Queue\Events\JobProcessing;
use Hypweb\Flysystem\GoogleDrive\GoogleDriveAdapter;
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
class EventServiceProvider extends ServiceProvider
{
function register()
{
$this->app->singleton('googleClient', function () {
$client = new \Google_Client();
$client->setClientId(config('filesystems.disks.google.clientId'));
$client->setClientSecret(config('filesystems.disks.google.clientSecret'));
return $client;
});
}
function boot()
{
$this->prepareGoogleStorageForQueue();
$this->registerGoogleStorage();
}
function prepareGoogleStorageForQueue()
{
Event::listen(JobProcessing::class, function($event) {
if ($event->job->resolveName() === CompleteOrder::class) $this->registerGoogleStorage();
});
Event::listen(JobProcessed::class, function($event) {
if ($event->job->resolveName() === CompleteOrder::class) Storage::forgetDisk('google');
});
}
function registerGoogleStorage()
{
Storage::extend('google', function() {
app('googleClient')->setAccessToken(cache('google-drive-credentials')['access_token']);
$service = new \Google_Service_Drive(app('googleClient'));
$adapter = new GoogleDriveAdapter($service);
return new Filesystem($adapter);
});
}
}
Hello my friend, Thanks for share your way.
I merged my way and your way and looks good to run without scheduled task to update token. I'm checking to ensure yet, but looks good.
I create one service to take about this:
<?php
namespace App\Services\Google;
use App\Providers\GoogleDriveAdapter;
use Storage;
class GoogelDriveStorageAuth
{
public static function reloadGoogleStorage()
{
$accessTokenExpired = Storage::disk('google')->getDriver()
?->getAdapter()
?->getService()
?->getClient()
->isAccessTokenExpired();
if ($accessTokenExpired) {
\Log::info('GoogelDriveStorageAuth: access token expired - Storage Reloaded');
Storage::forgetDisk('google');
self::registerStorage();
}
}
public static function registerStorage()
{
\Storage::extend('google', function ($app, $config) {
$app; // to fix grumphp settings -> variable not used
$client = new \Google_Client();
$client->setClientId($config['clientId']);
$client->setClientSecret($config['clientSecret']);
/**
* Cache access token to reduce requests to Google
*/
if (\Cache::has('google access token')) {
$token = \Cache::get('google access token');
$client->setAccessToken($token);
}
if ($client->isAccessTokenExpired()) {
$refreshToken = $config['refreshToken'];
if ($refreshToken) {
$token = $client->fetchAccessTokenWithRefreshToken($refreshToken);
$client->setAccessToken($token);
\Cache::add('google access token', $token);
} else {
/**
* Invalid refresh token and or credentials
*
* Check cloud wiki on how to create an app and get credentials and refresh token
*/
throw new \Exception("Invalid refresh token, check cloud wiki");
}
}
$service = new \Google_Service_Drive($client);
$options = [];
$adapter = new GoogleDriveAdapter($service, $config['folderId'], $options);
return new \League\Flysystem\Filesystem($adapter);
});
}
}
And, inside my Jobs, I put this line to, if necessary, reset the storage and re-auth:
GoogelDriveStorageAuth::reloadGoogleStorage();
Thanks
Yeah you can go that route as well. I chose to set up a scheduled task so I wouldn't have to add that line to every job or make some of my jobs extend another base job where I do that in the constructor. It felt more comfortable to write code anywhere assuming there's always a valid token available.
Oh yes! Great! Good point! Thanks
I'm using this adapter inside a queue. Once it's registered via
Storage::extend()
, it'll be reused as long as the queue is running. Unfortunately, the access token expires in 1 hour and I can't seem to refresh it.What I was trying to do was to make a singleton out of the Google client used by the adapter then set up a scheduler task every 30 minutes where I do this
app('googleClient')->refreshToken(config('filesystems.disks.google.refreshToken'));
then cache the credentials and finallyapp('googleClient')->setAccessToken(cache('the access token'))
on every job I put in the queue. Didn't work. I've also tried to refresh the token on every job and it doesn't work either.