Skip to content

Commit fe17347

Browse files
committed
Require laravel horizon and add failed jobs command
1 parent 3bc9e26 commit fe17347

File tree

7 files changed

+137
-5
lines changed

7 files changed

+137
-5
lines changed

.gitignore

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1+
/composer.lock
12
/vendor
2-
composer.lock
3+
.vscode
34
.idea
4-

composer.json

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,14 @@
1616
],
1717
"require": {
1818
"php": "^8.3",
19+
"ext-redis": "*",
1920
"laravel/framework": "^10.0",
20-
"spatie/laravel-health": "^1.20"
21+
"laravel/horizon": "^5.0",
22+
"spatie/laravel-health": "^1.0"
2123
},
2224
"require-dev": {
23-
"orchestra/testbench": "^v8.21",
25+
"laravel/pint": "^1.0",
26+
"orchestra/testbench": "^8.0",
2427
"pestphp/pest-plugin-laravel": "^2.0"
2528
},
2629
"suggest": {

phpunit.xml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
<?xml version="1.0" encoding="UTF-8"?>
22
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3-
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/10.3/phpunit.xsd"
43
bootstrap="vendor/autoload.php"
54
stopOnFailure="true"
65
cacheResult="false"
@@ -16,4 +15,7 @@
1615
<directory suffix=".php">./src</directory>
1716
</include>
1817
</source>
18+
<php>
19+
<server name="REDIS_CLIENT" value="phpredis"/>
20+
</php>
1921
</phpunit>

src/Console/Commands/FailedJobs.php

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
<?php
2+
3+
namespace Codebarista\LaravelEssentials\Console\Commands;
4+
5+
use Illuminate\Console\Command;
6+
use Illuminate\Contracts\Console\Isolatable;
7+
use Illuminate\Contracts\Console\PromptsForMissingInput;
8+
use Illuminate\Support\Str;
9+
use Laravel\Horizon\Jobs\RetryFailedJob;
10+
use Laravel\Horizon\Repositories\RedisJobRepository;
11+
12+
use function dispatch;
13+
14+
class FailedJobs extends Command implements Isolatable, PromptsForMissingInput
15+
{
16+
/**
17+
* @var string
18+
*/
19+
protected $signature = 'codebarista:failed-jobs {action : The action: retry or delete}';
20+
21+
/**
22+
* @var string
23+
*/
24+
protected $description = 'Retry or delete all failed jobs.';
25+
26+
public function handle(RedisJobRepository $repository): int
27+
{
28+
return match ($this->argument('action')) {
29+
'delete' => $this->deleteFailedJobs($repository),
30+
'retry' => $this->retryFailedJobs($repository),
31+
default => self::INVALID,
32+
};
33+
}
34+
35+
private function deleteFailedJobs(RedisJobRepository $repository): int
36+
{
37+
if (! $this->confirm('Really delete all failed jobs?')) {
38+
$this->components->info('Process terminated by user');
39+
40+
return self::SUCCESS;
41+
}
42+
43+
$i = $repository->getFailed()->count();
44+
45+
// What a hack...but it works
46+
$repository->recentFailedJobExpires = 0;
47+
$repository->trimRecentJobs();
48+
49+
$repository->failedJobExpires = 0;
50+
$repository->trimFailedJobs();
51+
52+
$this->components->info(
53+
sprintf('%d %s deleted.', $i, Str::plural(' job', $i))
54+
);
55+
56+
return self::SUCCESS;
57+
}
58+
59+
private function retryFailedJobs(RedisJobRepository $repository): int
60+
{
61+
if (! $this->confirm('Really retry all failed jobs?')) {
62+
$this->components->info('Process terminated by user');
63+
64+
return self::SUCCESS;
65+
}
66+
67+
$i = $repository->getFailed()->each(function ($job) {
68+
dispatch(new RetryFailedJob($job->id));
69+
})->count();
70+
71+
$this->components->info(
72+
sprintf('%d %s retried.', $i, Str::plural(' job', $i))
73+
);
74+
75+
return self::SUCCESS;
76+
}
77+
}

src/EssentialsServiceProvider.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace Codebarista\LaravelEssentials;
44

5+
use Codebarista\LaravelEssentials\Console\Commands\FailedJobs;
56
use Illuminate\Support\Arr;
67
use Illuminate\Support\ServiceProvider;
78
use Illuminate\Support\Str;
@@ -12,6 +13,9 @@ public function register(): void
1213
{
1314
$this->registerStringMacros();
1415
$this->registerArrayMacros();
16+
$this->commands([
17+
FailedJobs::class,
18+
]);
1519
}
1620

1721
public function boot(): void

tests/Feature/FailedJobsTest.php

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<?php
2+
3+
it('returns invalid', function () {
4+
$this->artisan('codebarista:failed-jobs')
5+
->expectsQuestion('What is the action: retry or delete?', 'invalid')
6+
->assertExitCode(2);
7+
});
8+
9+
it('aborts retry failed jobs', function () {
10+
$this->artisan('codebarista:failed-jobs')
11+
->expectsQuestion('What is the action: retry or delete?', 'retry')
12+
->expectsConfirmation('Really retry all failed jobs?')
13+
->expectsOutputToContain('Process terminated by user')
14+
->assertExitCode(0);
15+
});
16+
17+
it('aborts delete failed jobs', function () {
18+
$this->artisan('codebarista:failed-jobs')
19+
->expectsQuestion('What is the action: retry or delete?', 'delete')
20+
->expectsConfirmation('Really delete all failed jobs?')
21+
->expectsOutputToContain('Process terminated by user')
22+
->assertExitCode(0);
23+
});
24+
25+
it('retries failed jobs', function () {
26+
$this->artisan('codebarista:failed-jobs')
27+
->expectsQuestion('What is the action: retry or delete?', 'retry')
28+
->expectsConfirmation('Really retry all failed jobs?', 'yes')
29+
->expectsOutputToContain('jobs retried')
30+
->assertExitCode(0);
31+
});
32+
33+
it('deletes failed jobs', function () {
34+
$this->artisan('codebarista:failed-jobs')
35+
->expectsQuestion('What is the action: retry or delete?', 'delete')
36+
->expectsConfirmation('Really delete all failed jobs?', 'yes')
37+
->expectsOutputToContain('jobs deleted')
38+
->assertExitCode(0);
39+
});

tests/PackageTestCase.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
namespace Codebarista\LaravelEssentials\Tests;
66

77
use Codebarista\LaravelEssentials\EssentialsServiceProvider;
8+
use Laravel\Horizon\HorizonServiceProvider;
89
use Orchestra\Testbench\TestCase;
910

1011
abstract class PackageTestCase extends TestCase
@@ -13,6 +14,12 @@ protected function getPackageProviders($app): array
1314
{
1415
return [
1516
EssentialsServiceProvider::class,
17+
HorizonServiceProvider::class,
1618
];
1719
}
20+
21+
protected function getEnvironmentSetUp($app): void
22+
{
23+
$app['config']->set('queue.default', 'redis');
24+
}
1825
}

0 commit comments

Comments
 (0)