Skip to content

Commit bb6c876

Browse files
Merge pull request #68 from stackkit/bugfix/dispatch-after-commit
Dispatch after commit & request validation
2 parents 1c8ee02 + 13d3475 commit bb6c876

6 files changed

+259
-52
lines changed

src/CloudTasksQueue.php

Lines changed: 54 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,25 @@ public function size($queue = null)
4242
return 0;
4343
}
4444

45+
/**
46+
* Fallback method for Laravel 6x and 7x
47+
*
48+
* @param \Closure|string|object $job
49+
* @param string $payload
50+
* @param string $queue
51+
* @param \DateTimeInterface|\DateInterval|int|null $delay
52+
* @param callable $callback
53+
* @return mixed
54+
*/
55+
protected function enqueueUsing($job, $payload, $queue, $delay, $callback)
56+
{
57+
if (method_exists(parent::class, 'enqueueUsing')) {
58+
return parent::enqueueUsing($job, $payload, $queue, $delay, $callback);
59+
}
60+
61+
return $callback($payload, $queue, $delay);
62+
}
63+
4564
/**
4665
* Push a new job onto the queue.
4766
*
@@ -52,9 +71,15 @@ public function size($queue = null)
5271
*/
5372
public function push($job, $data = '', $queue = null)
5473
{
55-
$this->pushToCloudTasks($queue, $this->createPayload(
56-
$job, $this->getQueue($queue), $data
57-
));
74+
return $this->enqueueUsing(
75+
$job,
76+
$this->createPayload($job, $this->getQueue($queue), $data),
77+
$queue,
78+
null,
79+
function ($payload, $queue) {
80+
return $this->pushRaw($payload, $queue);
81+
}
82+
);
5883
}
5984

6085
/**
@@ -63,11 +88,11 @@ public function push($job, $data = '', $queue = null)
6388
* @param string $payload
6489
* @param string|null $queue
6590
* @param array $options
66-
* @return void
91+
* @return string
6792
*/
6893
public function pushRaw($payload, $queue = null, array $options = [])
6994
{
70-
$this->pushToCloudTasks($queue, $payload);
95+
return $this->pushToCloudTasks($queue, $payload);
7196
}
7297

7398
/**
@@ -81,9 +106,15 @@ public function pushRaw($payload, $queue = null, array $options = [])
81106
*/
82107
public function later($delay, $job, $data = '', $queue = null)
83108
{
84-
$this->pushToCloudTasks($queue, $this->createPayload(
85-
$job, $this->getQueue($queue), $data
86-
), $delay);
109+
return $this->enqueueUsing(
110+
$job,
111+
$this->createPayload($job, $this->getQueue($queue), $data),
112+
$queue,
113+
$delay,
114+
function ($payload, $queue, $delay) {
115+
return $this->pushToCloudTasks($queue, $payload, $delay);
116+
}
117+
);
87118
}
88119

89120
/**
@@ -92,7 +123,7 @@ public function later($delay, $job, $data = '', $queue = null)
92123
* @param string|null $queue
93124
* @param string $payload
94125
* @param \DateTimeInterface|\DateInterval|int $delay
95-
* @return void
126+
* @return string
96127
*/
97128
protected function pushToCloudTasks($queue, $payload, $delay = 0)
98129
{
@@ -103,12 +134,13 @@ protected function pushToCloudTasks($queue, $payload, $delay = 0)
103134
$httpRequest = $this->createHttpRequest();
104135
$httpRequest->setUrl($this->getHandler());
105136
$httpRequest->setHttpMethod(HttpMethod::POST);
106-
$httpRequest->setBody(
107-
// Laravel 7+ jobs have a uuid, but Laravel 6 doesn't have it.
108-
// Since we are using and expecting the uuid in some places
109-
// we will add it manually here if it's not present yet.
110-
$this->withUuid($payload)
111-
);
137+
138+
// Laravel 7+ jobs have a uuid, but Laravel 6 doesn't have it.
139+
// Since we are using and expecting the uuid in some places
140+
// we will add it manually here if it's not present yet.
141+
[$payload, $uuid] = $this->withUuid($payload);
142+
143+
$httpRequest->setBody($payload);
112144

113145
$task = $this->createTask();
114146
$task->setHttpRequest($httpRequest);
@@ -128,9 +160,11 @@ protected function pushToCloudTasks($queue, $payload, $delay = 0)
128160
$createdTask = CloudTasksApi::createTask($queueName, $task);
129161

130162
event((new TaskCreated)->queue($queue)->task($task));
163+
164+
return $uuid;
131165
}
132166

133-
private function withUuid(string $payload): string
167+
private function withUuid(string $payload): array
134168
{
135169
/** @var array $decoded */
136170
$decoded = json_decode($payload, true);
@@ -139,7 +173,10 @@ private function withUuid(string $payload): string
139173
$decoded['uuid'] = (string) Str::uuid();
140174
}
141175

142-
return json_encode($decoded);
176+
return [
177+
json_encode($decoded),
178+
$decoded['uuid'],
179+
];
143180
}
144181

145182
/**

src/TaskHandler.php

Lines changed: 45 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
use Illuminate\Queue\Jobs\Job;
1010
use Illuminate\Queue\WorkerOptions;
1111
use Illuminate\Support\Str;
12+
use Illuminate\Validation\ValidationException;
13+
use Safe\Exceptions\JsonException;
1214
use stdClass;
1315
use UnexpectedValueException;
1416
use function Safe\json_decode;
@@ -40,9 +42,9 @@ public function __construct(CloudTasksClient $client)
4042
$this->client = $client;
4143
}
4244

43-
public function handle(?array $task = null): void
45+
public function handle(?string $task = null): void
4446
{
45-
$task = $task ?: $this->captureTask();
47+
$task = $this->captureTask($task);
4648

4749
$this->loadQueueConnectionConfiguration($task);
4850

@@ -53,6 +55,47 @@ public function handle(?array $task = null): void
5355
$this->handleTask($task);
5456
}
5557

58+
/**
59+
* @param string|array|null $task
60+
* @return array
61+
* @throws JsonException
62+
*/
63+
private function captureTask($task): array
64+
{
65+
$task = $task ?: (string) (request()->getContent());
66+
67+
try {
68+
$array = json_decode($task, true);
69+
} catch (JsonException $e) {
70+
$array = [];
71+
}
72+
73+
$validator = validator([
74+
'json' => $task,
75+
'task' => $array,
76+
'name_header' => request()->header('X-CloudTasks-Taskname'),
77+
'retry_count_header' => request()->header('X-CloudTasks-TaskRetryCount'),
78+
], [
79+
'json' => 'required|json',
80+
'task' => 'required|array',
81+
'task.data' => 'required|array',
82+
'name_header' => 'required|string',
83+
'retry_count_header' => 'required|numeric',
84+
]);
85+
86+
try {
87+
$validator->validate();
88+
} catch (ValidationException $e) {
89+
if (config('app.debug')) {
90+
throw $e;
91+
} else {
92+
abort(404);
93+
}
94+
}
95+
96+
return json_decode($task, true);
97+
}
98+
5699
private function loadQueueConnectionConfiguration(array $task): void
57100
{
58101
/**
@@ -71,26 +114,6 @@ private function setQueue(): void
71114
$this->queue = new CloudTasksQueue($this->config, $this->client);
72115
}
73116

74-
/**
75-
* @throws CloudTasksException
76-
*/
77-
private function captureTask(): array
78-
{
79-
$input = (string) (request()->getContent());
80-
81-
if (!$input) {
82-
throw new CloudTasksException('Could not read incoming task');
83-
}
84-
85-
$task = json_decode($input, true);
86-
87-
if (!is_array($task)) {
88-
throw new CloudTasksException('Could not decode incoming task');
89-
}
90-
91-
return $task;
92-
}
93-
94117
private function handleTask(array $task): void
95118
{
96119
$job = new CloudTasksJob($task, $this->queue);

tests/CloudTasksDashboardTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,7 @@ public function when_a_job_is_dispatched_it_will_be_added_to_the_dashboard()
259259
'name' => SimpleJob::class,
260260
]);
261261
$payload = \Safe\json_decode($task->getMetadata()['payload'], true);
262-
$this->assertSame($payload, $job->payload);
262+
$this->assertSame($payload, $job->payloadAsArray);
263263
}
264264

265265
/**

tests/QueueTest.php

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@
66

77
use Google\Cloud\Tasks\V2\HttpMethod;
88
use Google\Cloud\Tasks\V2\Task;
9+
use Illuminate\Queue\Events\JobQueued;
10+
use Illuminate\Support\Facades\DB;
11+
use Illuminate\Support\Facades\Event;
912
use Stackkit\LaravelGoogleCloudTasksQueue\CloudTasksApi;
1013
use Stackkit\LaravelGoogleCloudTasksQueue\TaskHandler;
1114
use Tests\Support\FailingJob;
@@ -154,4 +157,28 @@ public function it_posts_the_task_the_correct_queue()
154157
&& $queueName === 'projects/my-test-project/locations/europe-west6/queues/my-special-queue';
155158
});
156159
}
160+
161+
/**
162+
* @test
163+
*/
164+
public function it_can_dispatch_after_commit()
165+
{
166+
if (version_compare(app()->version(), '8.0.0', '<')) {
167+
$this->markTestSkipped('Not supported by Laravel 7.x and below.');
168+
}
169+
170+
// Arrange
171+
CloudTasksApi::fake();
172+
Event::fake();
173+
174+
// Act & Assert
175+
Event::assertNotDispatched(JobQueued::class);
176+
DB::beginTransaction();
177+
SimpleJob::dispatch()->afterCommit();
178+
Event::assertNotDispatched(JobQueued::class);
179+
DB::commit();
180+
Event::assertDispatched(JobQueued::class, function (JobQueued $event) {
181+
return $event->job instanceof SimpleJob;
182+
});
183+
}
157184
}

0 commit comments

Comments
 (0)