Skip to content

Commit a3e2c55

Browse files
committed
Add JobReleased event and tests
1 parent a5f5843 commit a3e2c55

File tree

9 files changed

+176
-14
lines changed

9 files changed

+176
-14
lines changed

src/CloudTasksJob.php

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@
33
namespace Stackkit\LaravelGoogleCloudTasksQueue;
44

55
use Illuminate\Container\Container;
6-
use Illuminate\Queue\Jobs\Job as LaravelJob;
76
use Illuminate\Contracts\Queue\Job as JobContract;
7+
use Illuminate\Queue\Jobs\Job as LaravelJob;
8+
use Stackkit\LaravelGoogleCloudTasksQueue\Events\JobReleased;
9+
use Stackkit\LaravelGoogleCloudTasksQueue\Events\JobReleasedAfterException;
810
use function Safe\json_encode;
911

1012
class CloudTasksJob extends LaravelJob implements JobContract
@@ -14,7 +16,7 @@ class CloudTasksJob extends LaravelJob implements JobContract
1416
*
1517
* @var array
1618
*/
17-
private array $job;
19+
public array $job;
1820

1921
private ?int $maxTries;
2022
public ?int $retryUntil = null;
@@ -112,16 +114,21 @@ public function release($delay = 0)
112114

113115
$this->cloudTasksQueue->release($this, $delay);
114116

117+
$properties = TaskHandler::getCommandProperties($this->job['data']['command']);
118+
$connection = $properties['connection'] ?? config('queue.default');
119+
115120
// The package uses the JobReleasedAfterException provided by Laravel to grab
116121
// the payload of the released job in tests to easily run and test a released
117122
// job. Because the event is only accessible in Laravel 9.x, we create an
118123
// identical event to hook into for Laravel versions older than 9.x
119124
if (version_compare(app()->version(), '9.0.0', '<')) {
120-
$properties = TaskHandler::getCommandProperties($this->job['data']['command']);
121-
122-
$connection = $properties['connection'] ?? config('queue.default');
125+
if (data_get($this->job, 'internal.errored')) {
126+
app('events')->dispatch(new JobReleasedAfterException($connection, $this));
127+
}
128+
}
123129

124-
app('events')->dispatch(new JobReleasedAfterException($connection, $this));
130+
if (! data_get($this->job, 'internal.errored')) {
131+
app('events')->dispatch(new JobReleased($connection, $this));
125132
}
126133
}
127134
}

src/CloudTasksQueue.php

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,9 @@
1212
use Illuminate\Contracts\Queue\Queue as QueueContract;
1313
use Illuminate\Queue\Queue as LaravelQueue;
1414
use Illuminate\Support\Str;
15-
use function Safe\json_encode;
15+
use Stackkit\LaravelGoogleCloudTasksQueue\Events\TaskCreated;
1616
use function Safe\json_decode;
17+
use function Safe\json_encode;
1718

1819
class CloudTasksQueue extends LaravelQueue implements QueueContract
1920
{
@@ -197,8 +198,6 @@ private function withAttempts(string $payload): string
197198

198199
if (!isset($decoded['internal']['attempts'])) {
199200
$decoded['internal']['attempts'] = 0;
200-
} else {
201-
$decoded['internal']['attempts']++;
202201
}
203202

204203
return json_encode($decoded);

src/CloudTasksServiceProvider.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use Illuminate\Queue\Events\JobProcessed;
99
use Illuminate\Queue\Events\JobProcessing;
1010
use Illuminate\Support\ServiceProvider as LaravelServiceProvider;
11+
use Stackkit\LaravelGoogleCloudTasksQueue\Events\TaskCreated;
1112
use function Safe\file_get_contents;
1213
use function Safe\json_decode;
1314

@@ -158,6 +159,8 @@ private function registerDashboard(): void
158159
});
159160

160161
app('events')->listen(JobProcessed::class, function (JobProcessed $event) {
162+
data_set($event->job->job, 'internal.processed', true);
163+
161164
if (!CloudTasks::dashboardEnabled()) {
162165
return;
163166
}
@@ -168,6 +171,8 @@ private function registerDashboard(): void
168171
});
169172

170173
app('events')->listen(JobExceptionOccurred::class, function (JobExceptionOccurred $event) {
174+
data_set($event->job->job, 'internal.errored', true);
175+
171176
if (!CloudTasks::dashboardEnabled()) {
172177
return;
173178
}

src/Events/JobReleased.php

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Stackkit\LaravelGoogleCloudTasksQueue\Events;
6+
7+
use Illuminate\Contracts\Queue\Job;
8+
9+
class JobReleased
10+
{
11+
/**
12+
* The connection name.
13+
*
14+
* @var string
15+
*/
16+
public string $connectionName;
17+
18+
/**
19+
* The job instance.
20+
*
21+
* @var Job
22+
*/
23+
public Job $job;
24+
25+
/**
26+
* Create a new event instance.
27+
*
28+
* @param string $connectionName
29+
* @param Job $job
30+
* @return void
31+
*/
32+
public function __construct(string $connectionName, Job $job)
33+
{
34+
$this->job = $job;
35+
$this->connectionName = $connectionName;
36+
}
37+
}

src/JobReleasedAfterException.php renamed to src/Events/JobReleasedAfterException.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
declare(strict_types=1);
44

5-
namespace Stackkit\LaravelGoogleCloudTasksQueue;
5+
namespace Stackkit\LaravelGoogleCloudTasksQueue\Events;
66

77
use Illuminate\Contracts\Queue\Job;
88

src/TaskCreated.php renamed to src/Events/TaskCreated.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
declare(strict_types=1);
44

5-
namespace Stackkit\LaravelGoogleCloudTasksQueue;
5+
namespace Stackkit\LaravelGoogleCloudTasksQueue\Events;
66

77
use Google\Cloud\Tasks\V2\Task;
88

tests/QueueTest.php

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,15 @@
77
use Google\Cloud\Tasks\V2\HttpMethod;
88
use Google\Cloud\Tasks\V2\Task;
99
use Illuminate\Queue\Events\JobQueued;
10+
use Illuminate\Support\Carbon;
1011
use Illuminate\Support\Facades\DB;
1112
use Illuminate\Support\Facades\Event;
1213
use Stackkit\LaravelGoogleCloudTasksQueue\CloudTasksApi;
14+
use Stackkit\LaravelGoogleCloudTasksQueue\Events\JobReleased;
15+
use Stackkit\LaravelGoogleCloudTasksQueue\OpenIdVerificator;
1316
use Stackkit\LaravelGoogleCloudTasksQueue\TaskHandler;
1417
use Tests\Support\FailingJob;
18+
use Tests\Support\JobThatWillBeReleased;
1519
use Tests\Support\SimpleJob;
1620

1721
class QueueTest extends TestCase
@@ -210,4 +214,76 @@ public function it_can_dispatch_after_commit_through_config()
210214
return $event->job instanceof SimpleJob;
211215
});
212216
}
217+
218+
/**
219+
* @test
220+
*/
221+
public function jobs_can_be_released()
222+
{
223+
// Arrange
224+
CloudTasksApi::fake();
225+
OpenIdVerificator::fake();
226+
Event::fake([
227+
$this->getJobReleasedAfterExceptionEvent(),
228+
JobReleased::class,
229+
]);
230+
231+
// Act
232+
$this->dispatch(new JobThatWillBeReleased())->run();
233+
234+
// Assert
235+
Event::assertNotDispatched($this->getJobReleasedAfterExceptionEvent());
236+
CloudTasksApi::assertDeletedTaskCount(1);
237+
$releasedJob = null;
238+
Event::assertDispatched(JobReleased::class, function (JobReleased $event) use (&$releasedJob) {
239+
$releasedJob = $event->job;
240+
return true;
241+
});
242+
CloudTasksApi::assertTaskCreated(function (Task $task) {
243+
$body = $task->getHttpRequest()->getBody();
244+
$decoded = json_decode($body, true);
245+
return $decoded['data']['commandName'] === 'Tests\\Support\\JobThatWillBeReleased'
246+
&& $decoded['internal']['attempts'] === 1;
247+
});
248+
249+
$this->runFromPayload($releasedJob->getRawBody());
250+
251+
CloudTasksApi::assertDeletedTaskCount(2);
252+
CloudTasksApi::assertTaskCreated(function (Task $task) {
253+
$body = $task->getHttpRequest()->getBody();
254+
$decoded = json_decode($body, true);
255+
return $decoded['data']['commandName'] === 'Tests\\Support\\JobThatWillBeReleased'
256+
&& $decoded['internal']['attempts'] === 2;
257+
});
258+
}
259+
260+
/**
261+
* @test
262+
*/
263+
public function jobs_can_be_released_with_a_delay()
264+
{
265+
// Arrange
266+
CloudTasksApi::fake();
267+
OpenIdVerificator::fake();
268+
Event::fake([
269+
$this->getJobReleasedAfterExceptionEvent(),
270+
JobReleased::class,
271+
]);
272+
Carbon::setTestNow(now()->addDay());
273+
274+
// Act
275+
$this->dispatch(new JobThatWillBeReleased(15))->run();
276+
277+
// Assert
278+
CloudTasksApi::assertTaskCreated(function (Task $task) {
279+
$body = $task->getHttpRequest()->getBody();
280+
$decoded = json_decode($body, true);
281+
282+
$scheduleTime = $task->getScheduleTime() ? $task->getScheduleTime()->getSeconds() : null;
283+
284+
return $decoded['data']['commandName'] === 'Tests\\Support\\JobThatWillBeReleased'
285+
&& $decoded['internal']['attempts'] === 1
286+
&& $scheduleTime === now()->getTimestamp() + 15;
287+
});
288+
}
213289
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<?php
2+
3+
namespace Tests\Support;
4+
5+
use Illuminate\Bus\Queueable;
6+
use Illuminate\Contracts\Queue\ShouldQueue;
7+
use Illuminate\Foundation\Bus\Dispatchable;
8+
use Illuminate\Queue\InteractsWithQueue;
9+
use Illuminate\Queue\SerializesModels;
10+
11+
class JobThatWillBeReleased implements ShouldQueue
12+
{
13+
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
14+
15+
private int $releaseDelay;
16+
17+
/**
18+
* Create a new job instance.
19+
*
20+
* @return void
21+
*/
22+
public function __construct(int $releaseDelay = 0)
23+
{
24+
$this->releaseDelay = $releaseDelay;
25+
}
26+
27+
/**
28+
* Execute the job.
29+
*
30+
* @return void
31+
*/
32+
public function handle()
33+
{
34+
logger('JobThatWillBeReleased:beforeRelease');
35+
$this->release($this->releaseDelay);
36+
logger('JobThatWillBeReleased:afterRelease');
37+
}
38+
}

tests/TestCase.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,14 @@
55
use Closure;
66
use Firebase\JWT\JWT;
77
use Google\ApiCore\ApiException;
8+
use Google\Cloud\Tasks\V2\CloudTasksClient;
89
use Google\Cloud\Tasks\V2\Task;
910
use Illuminate\Foundation\Testing\DatabaseTransactions;
1011
use Illuminate\Queue\Events\JobReleasedAfterException;
1112
use Illuminate\Support\Facades\DB;
12-
use Google\Cloud\Tasks\V2\CloudTasksClient;
1313
use Illuminate\Support\Facades\Event;
14-
use Stackkit\LaravelGoogleCloudTasksQueue\JobReleasedAfterException as PackageJobReleasedAfterException;
15-
use Stackkit\LaravelGoogleCloudTasksQueue\TaskCreated;
14+
use Stackkit\LaravelGoogleCloudTasksQueue\Events\JobReleasedAfterException as PackageJobReleasedAfterException;
15+
use Stackkit\LaravelGoogleCloudTasksQueue\Events\TaskCreated;
1616
use Stackkit\LaravelGoogleCloudTasksQueue\TaskHandler;
1717

1818
class TestCase extends \Orchestra\Testbench\TestCase

0 commit comments

Comments
 (0)