Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions src/Illuminate/Contracts/Queue/HasContext.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

namespace Illuminate\Contracts\Queue;

interface HasContext
{
/**
* Get the context to include in the job payload.
*
* @return array
*/
public function context(): array;
}
5 changes: 4 additions & 1 deletion src/Illuminate/Queue/Jobs/InspectedJob.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,15 @@ class InspectedJob
* @param string|null $uuid The unique identifier for the job.
* @param string|null $name The display name of the job.
* @param int $attempts The number of times the job has been attempted.
* @param array|null $context The context provided by the job.
* @param \Illuminate\Support\Carbon|null $createdAt The date and time the job was created.
*/
public function __construct(
public readonly ?string $uuid,
public readonly ?string $name,
public readonly int $attempts,
public readonly ?Carbon $createdAt,
public readonly ?array $context = null,
public readonly ?Carbon $createdAt = null,
) {
}

Expand All @@ -37,6 +39,7 @@ public static function fromPayload(string $payload, ?int $attempts = null): stat
uuid: $decoded['uuid'] ?? null,
name: $decoded['displayName'] ?? null,
attempts: $attempts ?? $decoded['attempts'] ?? 0,
context: $decoded['context'] ?? null,
createdAt: isset($decoded['createdAt']) ? Carbon::createFromTimestamp($decoded['createdAt']) : null,
);
}
Expand Down
10 changes: 10 additions & 0 deletions src/Illuminate/Queue/Jobs/Job.php
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,16 @@ public function uuid()
return $this->payload()['uuid'] ?? null;
}

/**
* Get the job context.
*
* @return array|null
*/
public function context()
{
return $this->payload()['context'] ?? null;
}

/**
* Fire the job.
*
Expand Down
13 changes: 13 additions & 0 deletions src/Illuminate/Queue/Queue.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use Illuminate\Container\Container;
use Illuminate\Contracts\Cache\Repository as Cache;
use Illuminate\Contracts\Encryption\Encrypter;
use Illuminate\Contracts\Queue\HasContext;
use Illuminate\Contracts\Queue\ShouldBeEncrypted;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueueAfterCommit;
Expand Down Expand Up @@ -185,6 +186,7 @@ protected function createObjectPayload($job, $queue)
'command' => $job,
'batchId' => $job->batchId ?? null,
],
'context' => $this->getJobContext($job),
'createdAt' => Carbon::now()->getTimestamp(),
]);

Expand All @@ -208,6 +210,17 @@ protected function createObjectPayload($job, $queue)
]);
}

/**
* Get the context for the given job.
*
* @param object $job
* @return array|null
*/
protected function getJobContext($job)
{
return $job instanceof HasContext ? $job->context() : null;
}

/**
* Get the display name for the given job.
*
Expand Down
3 changes: 3 additions & 0 deletions src/Illuminate/Support/Testing/Fakes/QueueFake.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use Closure;
use Illuminate\Bus\UniqueLock;
use Illuminate\Contracts\Cache\Repository as Cache;
use Illuminate\Contracts\Queue\HasContext;
use Illuminate\Contracts\Queue\Queue;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Events\CallQueuedListener;
Expand Down Expand Up @@ -493,6 +494,7 @@ public function pendingJobs($queue = null): Collection
? (method_exists($data['job'], 'displayName') ? $data['job']->displayName() : get_class($data['job']))
: $data['job'],
attempts: 0,
context: $data['job'] instanceof HasContext ? $data['job']->context() : null,
createdAt: null,
));
}
Expand Down Expand Up @@ -534,6 +536,7 @@ public function allPendingJobs(): Collection
? (method_exists($data['job'], 'displayName') ? $data['job']->displayName() : get_class($data['job']))
: $data['job'],
attempts: 0,
context: $data['job'] instanceof HasContext ? $data['job']->context() : null,
createdAt: null,
));
}
Expand Down
20 changes: 20 additions & 0 deletions tests/Integration/Queue/RedisQueueTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use Illuminate\Container\Container;
use Illuminate\Contracts\Events\Dispatcher;
use Illuminate\Contracts\Queue\HasContext;
use Illuminate\Foundation\Testing\Concerns\InteractsWithRedis;
use Illuminate\Queue\Events\JobQueued;
use Illuminate\Queue\Events\JobQueueing;
Expand Down Expand Up @@ -727,6 +728,25 @@ public function testAllReservedJobs($driver)
$this->assertNotNull($reserved->first()->uuid);
$this->assertInstanceOf(Carbon::class, $reserved->first()->createdAt);
}

#[DataProvider('redisDriverProvider')]
public function testJobContext($driver)
{
$default = config('queue.connections.redis.queue', 'default');
$this->setQueue($driver, $default);

$this->queue->push(new RedisContextJob);

$this->assertSame(['task_id' => 42, 'type' => 'export'], $this->queue->pendingJobs()->first()->context);
}
}

class RedisContextJob implements HasContext
{
public function context(): array
{
return ['task_id' => 42, 'type' => 'export'];
}
}

class RedisQueueIntegrationTestJob
Expand Down
23 changes: 23 additions & 0 deletions tests/Queue/QueueDatabaseQueueUnitTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use Illuminate\Bus\Batchable;
use Illuminate\Container\Container;
use Illuminate\Contracts\Queue\HasContext;
use Illuminate\Database\Connection;
use Illuminate\Queue\DatabaseQueue;
use Illuminate\Queue\Jobs\InspectedJob;
Expand Down Expand Up @@ -356,6 +357,20 @@ public function testAllReservedJobs()
$this->assertSame(2, $jobs->last()->attempts);
}

public function testJobContext()
{
$queue = new DatabaseQueue($database = m::mock(Connection::class), 'table', 'default');
$queue->setContainer(m::spy(Container::class));

$database->shouldReceive('table')->with('table')->andReturn($query = m::mock(stdClass::class));
$query->shouldReceive('insertGetId')->once()->andReturnUsing(function ($array) {
$payload = json_decode($array['payload'], true);
$this->assertSame(['task_id' => 42, 'type' => 'export'], $payload['context']);
});

$queue->push(new ContextJob);
}

public function testGetLockForPoppingIsCached()
{
$database = m::mock(Connection::class);
Expand Down Expand Up @@ -390,3 +405,11 @@ class MyBatchableJob
{
use Batchable;
}

class ContextJob implements HasContext
{
public function context(): array
{
return ['task_id' => 42, 'type' => 'export'];
}
}
21 changes: 21 additions & 0 deletions tests/Support/SupportTestingQueueFakeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use BadMethodCallException;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\HasContext;
use Illuminate\Foundation\Application;
use Illuminate\Queue\CallQueuedClosure;
use Illuminate\Queue\Jobs\InspectedJob;
Expand Down Expand Up @@ -540,6 +541,13 @@ public function testAllPendingJobs()
$this->assertTrue($pending->contains(fn ($job) => $job->name === JobToFakeStub::class));
}

public function testContextIsIncluded()
{
$this->fake->push(new JobWithContextStub, '', 'foo');

$this->assertSame(['task_id' => 42, 'type' => 'export'], $this->fake->allPendingJobs()->first()->context);
}

public function testGetRawPushes()
{
$this->fake->pushRaw('some-payload', null, ['options' => 'yeah']);
Expand Down Expand Up @@ -618,6 +626,19 @@ public function handle()
}
}

class JobWithContextStub implements HasContext
{
public function handle()
{
//
}

public function context(): array
{
return ['task_id' => 42, 'type' => 'export'];
}
}

class JobWithChainStub
{
use Queueable;
Expand Down