Skip to content

Commit

Permalink
Merge branch 'release/2.0.0-rc.2'
Browse files Browse the repository at this point in the history
  • Loading branch information
lindyhopchris committed Jul 27, 2024
2 parents bdabbd9 + b548d1d commit 9cf825d
Show file tree
Hide file tree
Showing 14 changed files with 323 additions and 66 deletions.
23 changes: 22 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,28 @@ All notable changes to this project will be documented in this file. This projec

## Unreleased

### [2.0.0-rc.1] - 2024-05-07
## [2.0.0-rc.2] - 2024-07-27

### Added

- The `Uuid` identifier class now has a `getBytes()` method
- Can now get a nil UUID from the `Uuid::nil()` static method.

### Changed

- Made resolution of inner handlers lazy in all buses. In several the handler was immediately resolved, so that the
handler middleware could be calculated. Buses that support handler middleware now first pipe through the bus
middleware, then resolve the inner handler, then pipe through the handler middleware. This allows inner handler
constructor injected dependencies to be lazily resolved after the bus middleware has executed. This is important when
using the setup and teardown middleware for bootstrapping services that may be injected into the inner handler. Buses
that now lazily resolve inner handlers are:
- Command bus
- Query bus
- Inbound integration event bus
- Outbound integration event bus
- Queue bus

## [2.0.0-rc.1] - 2024-05-07

**Refer to the [Upgrade Guide.](./docs/guide/upgrade.md)**

Expand Down
27 changes: 23 additions & 4 deletions src/Application/Bus/CommandDispatcher.php
Original file line number Diff line number Diff line change
Expand Up @@ -67,11 +67,11 @@ public function through(array $pipes): void
*/
public function dispatch(Command $command): Result
{
$handler = $this->handlers->get($command::class);

$pipeline = PipelineBuilder::make($this->middleware)
->through([...$this->pipes, ...array_values($handler->middleware())])
->build(MiddlewareProcessor::wrap($handler));
->through($this->pipes)
->build(new MiddlewareProcessor(
fn (Command $passed): Result => $this->execute($passed),
));

$result = $pipeline->process($command);

Expand All @@ -97,4 +97,23 @@ public function queue(Command $command): void

$this->queue->push($command);
}

/**
* @param Command $command
* @return Result<mixed>
*/
private function execute(Command $command): Result
{
$handler = $this->handlers->get($command::class);

$pipeline = PipelineBuilder::make($this->middleware)
->through($handler->middleware())
->build(MiddlewareProcessor::wrap($handler));

$result = $pipeline->process($command);

assert($result instanceof Result, 'Expecting pipeline to return a result object.');

return $result;
}
}
21 changes: 20 additions & 1 deletion src/Application/Bus/QueryDispatcher.php
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,30 @@ public function through(array $pipes): void
* @inheritDoc
*/
public function dispatch(Query $query): Result
{
$pipeline = PipelineBuilder::make($this->middleware)
->through($this->pipes)
->build(new MiddlewareProcessor(
fn (Query $passed): Result => $this->execute($passed),
));

$result = $pipeline->process($query);

assert($result instanceof Result, 'Expecting pipeline to return a result object.');

return $result;
}

/**
* @param Query $query
* @return Result<mixed>
*/
private function execute(Query $query): Result
{
$handler = $this->handlers->get($query::class);

$pipeline = PipelineBuilder::make($this->middleware)
->through([...$this->pipes, ...array_values($handler->middleware())])
->through($handler->middleware())
->build(MiddlewareProcessor::wrap($handler));

$result = $pipeline->process($query);
Expand Down
17 changes: 16 additions & 1 deletion src/Application/InboundEventBus/EventDispatcher.php
Original file line number Diff line number Diff line change
Expand Up @@ -54,11 +54,26 @@ public function through(array $pipes): void
* @inheritDoc
*/
public function dispatch(IntegrationEvent $event): void
{
$pipeline = PipelineBuilder::make($this->middleware)
->through($this->pipes)
->build(new MiddlewareProcessor(function (IntegrationEvent $passed): void {
$this->execute($passed);
}));

$pipeline->process($event);
}

/**
* @param IntegrationEvent $event
* @return void
*/
private function execute(IntegrationEvent $event): void
{
$handler = $this->handlers->get($event::class);

$pipeline = PipelineBuilder::make($this->middleware)
->through([...$this->pipes, ...$handler->middleware()])
->through($handler->middleware())
->build(MiddlewareProcessor::call($handler));

$pipeline->process($event);
Expand Down
4 changes: 2 additions & 2 deletions src/Contracts/Toolkit/Identifiers/IdentifierFactory.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
<?php

declare(strict_types=1);
/*
* Copyright 2024 Cloud Creativity Limited
*
Expand All @@ -9,6 +7,8 @@
* https://opensource.org/licenses/MIT.
*/

declare(strict_types=1);

namespace CloudCreativity\Modules\Contracts\Toolkit\Identifiers;

interface IdentifierFactory
Expand Down
7 changes: 4 additions & 3 deletions src/Infrastructure/OutboundEventBus/ComponentPublisher.php
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,12 @@ public function through(array $pipes): void
*/
public function publish(IntegrationEvent $event): void
{
$handler = $this->handlers->get($event::class);

$pipeline = PipelineBuilder::make($this->middleware)
->through($this->pipes)
->build(MiddlewareProcessor::call($handler));
->build(new MiddlewareProcessor(function (IntegrationEvent $passed): void {
$handler = $this->handlers->get($passed::class);
$handler($passed);
}));

$pipeline->process($event);
}
Expand Down
7 changes: 4 additions & 3 deletions src/Infrastructure/Queue/ComponentQueue.php
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,12 @@ public function through(array $pipes): void
*/
public function push(Command $command): void
{
$enqueuer = $this->enqueuers->get($command::class);

$pipeline = PipelineBuilder::make($this->middleware)
->through($this->pipes)
->build(MiddlewareProcessor::call($enqueuer));
->build(new MiddlewareProcessor(function (Command $passed): void {
$enqueuer = $this->enqueuers->get($passed::class);
$enqueuer($passed);
}));

$pipeline->process($command);
}
Expand Down
31 changes: 25 additions & 6 deletions src/Toolkit/Identifiers/Uuid.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@
use CloudCreativity\Modules\Contracts\Toolkit\Identifiers\Identifier;
use CloudCreativity\Modules\Contracts\Toolkit\Identifiers\UuidFactory as IUuidFactory;
use JsonSerializable;
use Ramsey\Uuid\UuidInterface as BaseUuid;
use Ramsey\Uuid\Uuid as BaseUuid;
use Ramsey\Uuid\UuidInterface as IBaseUuid;

final class Uuid implements Identifier, JsonSerializable
{
Expand Down Expand Up @@ -45,15 +46,15 @@ public static function getFactory(): IUuidFactory
}

/**
* @param Identifier|BaseUuid|string $value
* @param Identifier|IBaseUuid|string $value
* @return self
*/
public static function from(Identifier|BaseUuid|string $value): self
public static function from(Identifier|IBaseUuid|string $value): self
{
$factory = self::getFactory();

return match(true) {
$value instanceof Identifier, $value instanceof BaseUuid => $factory->from($value),
$value instanceof Identifier, $value instanceof IBaseUuid => $factory->from($value),
is_string($value) => $factory->fromString($value),
};
}
Expand All @@ -68,12 +69,22 @@ public static function random(): self
return self::getFactory()->uuid4();
}

/**
* Create a nil UUID.
*
* @return self
*/
public static function nil(): self
{
return self::from(BaseUuid::NIL);
}

/**
* Uuid constructor.
*
* @param BaseUuid $value
* @param IBaseUuid $value
*/
public function __construct(public readonly BaseUuid $value)
public function __construct(public readonly IBaseUuid $value)
{
}

Expand All @@ -94,6 +105,14 @@ public function toString(): string
return $this->value->toString();
}

/**
* @return string
*/
public function getBytes(): string
{
return $this->value->getBytes();
}

/**
* @inheritDoc
*/
Expand Down
60 changes: 49 additions & 11 deletions tests/Unit/Application/Bus/CommandDispatcherTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
use CloudCreativity\Modules\Contracts\Application\Messages\Command;
use CloudCreativity\Modules\Contracts\Application\Ports\Driven\Queue\Queue;
use CloudCreativity\Modules\Contracts\Toolkit\Pipeline\PipeContainer;
use CloudCreativity\Modules\Contracts\Toolkit\Result\Result;
use CloudCreativity\Modules\Toolkit\Result\Result;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;

Expand All @@ -43,6 +43,11 @@ class CommandDispatcherTest extends TestCase
*/
private CommandDispatcher $dispatcher;

/**
* @var array<string>
*/
private array $sequence = [];

/**
* @return void
*/
Expand All @@ -59,6 +64,15 @@ protected function setUp(): void
);
}

/**
* @return void
*/
protected function tearDown(): void
{
unset($this->handlers, $this->middleware, $this->queue, $this->dispatcher, $this->sequence);
parent::tearDown();
}

/**
* @return void
*/
Expand All @@ -78,7 +92,7 @@ public function test(): void
->expects($this->once())
->method('__invoke')
->with($this->identicalTo($command))
->willReturn($expected = $this->createMock(Result::class));
->willReturn($expected = Result::ok());

$actual = $this->dispatcher->dispatch($command);

Expand All @@ -96,32 +110,48 @@ public function testWithMiddleware(): void
$command2 = new TestCommand();
$command3 = new TestCommand();
$command4 = new TestCommand();
$handler = $this->createMock(CommandHandler::class);

$middleware1 = function (TestCommand $command, \Closure $next) use ($command1, $command2) {
$this->assertSame($command1, $command);
return $next($command2);
$this->sequence[] = 'before1';
$result = $next($command2);
$this->sequence[] = 'after1';
return $result;
};

$middleware2 = function (TestCommand $command, \Closure $next) use ($command2, $command3) {
$this->assertSame($command2, $command);
return $next($command3);
$this->sequence[] = 'before2';
$result = $next($command3);
$this->sequence[] = 'after2';
return $result;
};

$middleware3 = function (TestCommand $command, \Closure $next) use ($command3, $command4) {
$this->assertSame($command3, $command);
return $next($command4);
$this->sequence[] = 'before3';
$result = $next($command4);
$this->sequence[] = 'after3';
return $result;
};

$this->handlers
->method('get')
->with($command1::class)
->willReturn($handler = $this->createMock(CommandHandler::class));
->willReturnCallback(function () use ($handler) {
$this->assertSame(['before1'], $this->sequence);
return $handler;
});

$this->middleware
->expects($this->once())
->expects($this->exactly(2))
->method('get')
->with('MySecondMiddleware')
->willReturn($middleware2);
->willReturnCallback(fn (string $name) => match ($name) {
'MyFirstMiddleware' => $middleware1,
'MySecondMiddleware' => $middleware2,
default => $this->fail('Unexpected middleware: ' . $name),
});

$handler
->expects($this->once())
Expand All @@ -132,12 +162,20 @@ public function testWithMiddleware(): void
->expects($this->once())
->method('__invoke')
->with($this->identicalTo($command4))
->willReturn($expected = $this->createMock(Result::class));
->willReturn($expected = Result::ok());

$this->dispatcher->through([$middleware1]);
$this->dispatcher->through(['MyFirstMiddleware']);
$actual = $this->dispatcher->dispatch($command1);

$this->assertSame($expected, $actual);
$this->assertSame([
'before1',
'before2',
'before3',
'after3',
'after2',
'after1',
], $this->sequence);
}

/**
Expand Down
Loading

0 comments on commit 9cf825d

Please sign in to comment.