Skip to content

Commit 446a1a5

Browse files
[12.x] Add SQS FIFO and fair queue messageGroup() method support (#57421)
* Add messageGroup method support to normal queue job objects. * Add messageGroup method support to mailable queue job objects. * Add messageGroup method support to notification queue job objects.
1 parent 909102c commit 446a1a5

File tree

7 files changed

+179
-8
lines changed

7 files changed

+179
-8
lines changed

src/Illuminate/Mail/Mailable.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,7 @@ public function later($delay, Queue $queue)
262262
*/
263263
protected function newQueuedJob()
264264
{
265-
$messageGroup = $this->messageGroup ?? null;
265+
$messageGroup = $this->messageGroup ?? (method_exists($this, 'messageGroup') ? $this->messageGroup() : null);
266266

267267
/** @phpstan-ignore callable.nonNativeMethod (false positive since method_exists guard is used) */
268268
$deduplicator = $this->deduplicator ?? (method_exists($this, 'deduplicationId') ? $this->deduplicationId(...) : null);

src/Illuminate/Notifications/NotificationSender.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -247,7 +247,7 @@ protected function queueNotification($notifiables, $notification)
247247
$delay = $notification->withDelay($notifiable, $channel) ?? null;
248248
}
249249

250-
$messageGroup = $notification->messageGroup ?? null;
250+
$messageGroup = $notification->messageGroup ?? (method_exists($notification, 'messageGroup') ? $notification->messageGroup() : null);
251251

252252
if (method_exists($notification, 'withMessageGroups')) {
253253
$messageGroup = $notification->withMessageGroups($notifiable, $channel) ?? null;

src/Illuminate/Queue/SqsQueue.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,7 @@ protected function getQueueableOptions($job, $queue, $payload, $delay = null): a
242242
$messageGroupId = null;
243243

244244
if ($isObject) {
245-
$messageGroupId = transform($job->messageGroup ?? null, $transformToString);
245+
$messageGroupId = transform($job->messageGroup ?? (method_exists($job, 'messageGroup') ? $job->messageGroup() : null), $transformToString);
246246
} elseif ($isFifo) {
247247
$messageGroupId = transform($queue, $transformToString);
248248
}

tests/Mail/MailableQueuedTest.php

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,17 +88,42 @@ public function testQueuedMailableWithAttachmentFromDiskSent(): void
8888
$queueFake->assertPushedOn(null, SendQueuedMailable::class);
8989
}
9090

91-
public function testQueuedMailableForwardsMessageGroupToQueueJob(): void
91+
public function testQueuedMailableForwardsMessageGroupFromMethodToQueueJob(): void
9292
{
9393
$mockedMessageGroupId = 'group-1';
9494

95+
$mailable = $this->getMockBuilder(MailableQueueableStubWithMessageGroup::class)->onlyMethods(['messageGroup'])->getMock();
96+
$mailable->expects($this->once())->method('messageGroup')->willReturn($mockedMessageGroupId);
97+
98+
$queueFake = new QueueFake(new Application);
99+
$mailer = $this->getMockBuilder(Mailer::class)
100+
->setConstructorArgs($this->getMocks())
101+
->onlyMethods(['createMessage', 'to'])
102+
->getMock();
103+
$mailer->setQueue($queueFake);
104+
$queueFake->assertNothingPushed();
105+
$mailer->send($mailable);
106+
$queueFake->assertPushedOn(null, SendQueuedMailable::class);
107+
108+
$pushedJob = $queueFake->pushed(SendQueuedMailable::class)->first();
109+
$this->assertEquals($mockedMessageGroupId, $pushedJob->messageGroup);
110+
}
111+
112+
public function testQueuedMailableForwardsMessageGroupFromPropertyOverridingMethodToQueueJob(): void
113+
{
114+
$mockedMessageGroupId = 'group-1';
115+
116+
// Ensure the messageGroup method is not called when a messageGroup property is provided.
117+
$mailable = $this->getMockBuilder(MailableQueueableStubWithMessageGroup::class)->onlyMethods(['messageGroup'])->getMock();
118+
$mailable->expects($this->never())->method('messageGroup')->willReturn('this-should-not-be-used');
119+
$mailable->onGroup($mockedMessageGroupId);
120+
95121
$queueFake = new QueueFake(new Application);
96122
$mailer = $this->getMockBuilder(Mailer::class)
97123
->setConstructorArgs($this->getMocks())
98124
->onlyMethods(['createMessage', 'to'])
99125
->getMock();
100126
$mailer->setQueue($queueFake);
101-
$mailable = (new MailableQueueableStub)->onGroup($mockedMessageGroupId);
102127
$queueFake->assertNothingPushed();
103128
$mailer->send($mailable);
104129
$queueFake->assertPushedOn(null, SendQueuedMailable::class);
@@ -166,6 +191,26 @@ public function build(): self
166191
}
167192
}
168193

194+
class MailableQueueableStubWithMessageGroup extends Mailable implements ShouldQueue
195+
{
196+
use Queueable;
197+
198+
public function build(): self
199+
{
200+
$this
201+
->subject('lorem ipsum')
202+
->html('foo bar baz')
203+
->to('foo@example.tld');
204+
205+
return $this;
206+
}
207+
208+
public function messageGroup(): string
209+
{
210+
return 'group-1';
211+
}
212+
}
213+
169214
class MailableQueueableStubWithDeduplication extends Mailable implements ShouldQueue
170215
{
171216
use Queueable;

tests/Notifications/NotificationChannelManagerTest.php

Lines changed: 51 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -178,15 +178,44 @@ public function testSendQueuedNotificationsCanBeOverrideViaContainer()
178178
$manager->send([new NotificationChannelManagerTestNotifiable], new NotificationChannelManagerTestQueuedNotification);
179179
}
180180

181-
public function testQueuedNotificationForwardsMessageGroupToQueueJob()
181+
public function testQueuedNotificationForwardsMessageGroupFromMethodToQueueJob()
182182
{
183183
$mockedMessageGroupId = 'group-1';
184184

185+
$notification = $this->getMockBuilder(NotificationChannelManagerTestQueuedNotificationWithMessageGroupMethod::class)->onlyMethods(['messageGroup'])->getMock();
186+
$notification->expects($this->exactly(2))->method('messageGroup')->willReturn($mockedMessageGroupId);
187+
188+
$container = new Container;
189+
$container->instance('config', ['app.name' => 'Name', 'app.logo' => 'Logo']);
190+
$container->instance(Dispatcher::class, $events = m::mock());
191+
$container->instance(Bus::class, $bus = m::mock());
192+
$bus->shouldReceive('dispatch')->twice()->withArgs(function ($job) use ($mockedMessageGroupId) {
193+
$this->assertInstanceOf(SendQueuedNotifications::class, $job);
194+
$this->assertEquals($mockedMessageGroupId, $job->messageGroup);
195+
196+
return true;
197+
});
198+
Container::setInstance($container);
199+
$manager = m::mock(ChannelManager::class.'[driver]', [$container]);
200+
$events->shouldReceive('listen')->once();
201+
202+
$manager->send([new NotificationChannelManagerTestNotifiable], $notification);
203+
}
204+
205+
public function testQueuedNotificationForwardsMessageGroupFromPropertyOverridingMethodToQueueJob()
206+
{
207+
$mockedMessageGroupId = 'group-1';
208+
209+
// Ensure the messageGroup method is not called when a messageGroup property is provided.
210+
$notification = $this->getMockBuilder(NotificationChannelManagerTestQueuedNotificationWithMessageGroupMethod::class)->onlyMethods(['messageGroup'])->getMock();
211+
$notification->expects($this->never())->method('messageGroup')->willReturn('this-should-not-be-used');
212+
$notification->onGroup($mockedMessageGroupId);
213+
185214
$container = new Container;
186215
$container->instance('config', ['app.name' => 'Name', 'app.logo' => 'Logo']);
187216
$container->instance(Dispatcher::class, $events = m::mock());
188217
$container->instance(Bus::class, $bus = m::mock());
189-
$bus->shouldReceive('dispatch')->once()->withArgs(function ($job) use ($mockedMessageGroupId) {
218+
$bus->shouldReceive('dispatch')->twice()->withArgs(function ($job) use ($mockedMessageGroupId) {
190219
$this->assertInstanceOf(SendQueuedNotifications::class, $job);
191220
$this->assertEquals($mockedMessageGroupId, $job->messageGroup);
192221

@@ -196,7 +225,6 @@ public function testQueuedNotificationForwardsMessageGroupToQueueJob()
196225
$manager = m::mock(ChannelManager::class.'[driver]', [$container]);
197226
$events->shouldReceive('listen')->once();
198227

199-
$notification = (new NotificationChannelManagerTestQueuedNotification)->onGroup($mockedMessageGroupId);
200228
$manager->send([new NotificationChannelManagerTestNotifiable], $notification);
201229
}
202230

@@ -443,6 +471,26 @@ public function message()
443471
}
444472
}
445473

474+
class NotificationChannelManagerTestQueuedNotificationWithMessageGroupMethod extends Notification implements ShouldQueue
475+
{
476+
use Queueable;
477+
478+
public function via()
479+
{
480+
return ['test', 'test2'];
481+
}
482+
483+
public function message()
484+
{
485+
return $this->line('test')->action('Text', 'url');
486+
}
487+
488+
public function messageGroup()
489+
{
490+
return 'group-1';
491+
}
492+
}
493+
446494
class NotificationChannelManagerTestQueuedNotificationWithMessageGroups extends Notification implements ShouldQueue
447495
{
448496
use Queueable;
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?php
2+
3+
namespace Illuminate\Tests\Queue\Fixtures;
4+
5+
use Illuminate\Contracts\Queue\ShouldQueue;
6+
use Illuminate\Foundation\Queue\Queueable;
7+
8+
class FakeSqsJobWithMessageGroup implements ShouldQueue
9+
{
10+
use Queueable;
11+
12+
public function handle(): void
13+
{
14+
//
15+
}
16+
17+
/**
18+
* Message group method called by SqsQueue.
19+
*
20+
* @return string
21+
*/
22+
public function messageGroup(): string
23+
{
24+
return 'group-1';
25+
}
26+
}

tests/Queue/QueueSqsQueueTest.php

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
use Illuminate\Support\Str;
1414
use Illuminate\Tests\Queue\Fixtures\FakeSqsJob;
1515
use Illuminate\Tests\Queue\Fixtures\FakeSqsJobWithDeduplication;
16+
use Illuminate\Tests\Queue\Fixtures\FakeSqsJobWithMessageGroup;
1617
use Laravel\SerializableClosure\SerializableClosure;
1718
use Mockery as m;
1819
use PHPUnit\Framework\TestCase;
@@ -363,6 +364,57 @@ public function testPushProperlyPushesJobObjectOntoSqsFifoQueue()
363364
Str::createUuidsNormally();
364365
}
365366

367+
public function testPushProperlyPushesJobObjectOntoSqsFifoQueueWithMessageGroupMethod()
368+
{
369+
Str::createUuidsUsing(fn () => $this->mockedDeduplicationId);
370+
371+
$job = $this->getMockBuilder(FakeSqsJobWithMessageGroup::class)->onlyMethods(['messageGroup'])->getMock();
372+
$job->expects($this->once())->method('messageGroup')->willReturn($this->mockedMessageGroupId);
373+
374+
$queue = $this->getMockBuilder(SqsQueue::class)->onlyMethods(['createPayload', 'getQueue'])->setConstructorArgs([$this->sqs, $this->fifoQueueName, $this->account])->getMock();
375+
$queue->setContainer($container = m::spy(Container::class));
376+
$queue->expects($this->once())->method('createPayload')->with($job, $this->fifoQueueName, $this->mockedData)->willReturn($this->mockedPayload);
377+
$queue->expects($this->once())->method('getQueue')->with($this->fifoQueueName)->willReturn($this->fifoQueueUrl);
378+
$this->sqs->shouldReceive('sendMessage')->once()->with([
379+
'QueueUrl' => $this->fifoQueueUrl,
380+
'MessageBody' => $this->mockedPayload,
381+
'MessageGroupId' => $this->mockedMessageGroupId,
382+
'MessageDeduplicationId' => $this->mockedDeduplicationId,
383+
])->andReturn($this->mockedSendMessageResponseModel);
384+
$id = $queue->push($job, $this->mockedData, $this->fifoQueueName);
385+
$this->assertEquals($this->mockedMessageId, $id);
386+
$container->shouldHaveReceived('bound')->with('events')->twice();
387+
388+
Str::createUuidsNormally();
389+
}
390+
391+
public function testPushProperlyPushesJobObjectOntoSqsFifoQueueWithMessageGroupPropertyOverridingMethod()
392+
{
393+
Str::createUuidsUsing(fn () => $this->mockedDeduplicationId);
394+
395+
$job = $this->getMockBuilder(FakeSqsJobWithMessageGroup::class)->onlyMethods(['messageGroup'])->getMock();
396+
397+
// Ensure the messageGroup method is not called when a messageGroup property is provided.
398+
$job->expects($this->never())->method('messageGroup')->willReturn('this-should-not-be-used');
399+
$job->onGroup($this->mockedMessageGroupId);
400+
401+
$queue = $this->getMockBuilder(SqsQueue::class)->onlyMethods(['createPayload', 'getQueue'])->setConstructorArgs([$this->sqs, $this->fifoQueueName, $this->account])->getMock();
402+
$queue->setContainer($container = m::spy(Container::class));
403+
$queue->expects($this->once())->method('createPayload')->with($job, $this->fifoQueueName, $this->mockedData)->willReturn($this->mockedPayload);
404+
$queue->expects($this->once())->method('getQueue')->with($this->fifoQueueName)->willReturn($this->fifoQueueUrl);
405+
$this->sqs->shouldReceive('sendMessage')->once()->with([
406+
'QueueUrl' => $this->fifoQueueUrl,
407+
'MessageBody' => $this->mockedPayload,
408+
'MessageGroupId' => $this->mockedMessageGroupId,
409+
'MessageDeduplicationId' => $this->mockedDeduplicationId,
410+
])->andReturn($this->mockedSendMessageResponseModel);
411+
$id = $queue->push($job, $this->mockedData, $this->fifoQueueName);
412+
$this->assertEquals($this->mockedMessageId, $id);
413+
$container->shouldHaveReceived('bound')->with('events')->twice();
414+
415+
Str::createUuidsNormally();
416+
}
417+
366418
public function testPushProperlyPushesJobObjectOntoSqsFifoQueueWithDeduplicationId()
367419
{
368420
$job = $this->getMockBuilder(FakeSqsJobWithDeduplication::class)->onlyMethods(['deduplicationId'])->getMock();

0 commit comments

Comments
 (0)