Skip to content

Commit 467c664

Browse files
authored
feat(console): add make:middleware command (#804)
1 parent 0f47a80 commit 467c664

File tree

7 files changed

+277
-0
lines changed

7 files changed

+277
-0
lines changed
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Tempest\Console\Commands;
6+
7+
use InvalidArgumentException;
8+
use Tempest\Console\ConsoleArgument;
9+
use Tempest\Console\ConsoleCommand;
10+
use Tempest\Console\Enums\MiddlewareType;
11+
use Tempest\Console\Stubs\CommandBusMiddlewareStub;
12+
use Tempest\Console\Stubs\ConsoleMiddlewareStub;
13+
use Tempest\Console\Stubs\EventBusMiddlewareStub;
14+
use Tempest\Console\Stubs\HttpMiddlewareStub;
15+
use Tempest\Core\PublishesFiles;
16+
use Tempest\Generation\DataObjects\StubFile;
17+
use Tempest\Generation\Exceptions\FileGenerationAbortedException;
18+
use Tempest\Generation\Exceptions\FileGenerationFailedException;
19+
20+
final class MakeMiddlewareCommand
21+
{
22+
use PublishesFiles;
23+
24+
#[ConsoleCommand(
25+
name: 'make:middleware',
26+
description: 'Creates a new middleware class',
27+
aliases: ['middleware:make', 'middleware:create', 'create:middleware'],
28+
)]
29+
public function __invoke(
30+
#[ConsoleArgument(
31+
help: 'The name of the middleware class to create',
32+
)]
33+
string $className,
34+
#[ConsoleArgument(
35+
name: 'type',
36+
help: 'The type of the middleware to create',
37+
)]
38+
MiddlewareType $middlewareType,
39+
): void {
40+
try {
41+
$stubFile = $this->getStubFileFromMiddlewareType($middlewareType);
42+
$suggestedPath = $this->getSuggestedPath($className);
43+
$targetPath = $this->promptTargetPath($suggestedPath);
44+
$shouldOverride = $this->askForOverride($targetPath);
45+
46+
$this->stubFileGenerator->generateClassFile(
47+
stubFile: $stubFile,
48+
targetPath: $targetPath,
49+
shouldOverride: $shouldOverride,
50+
);
51+
52+
$this->success(sprintf('Middleware successfully created at "%s".', $targetPath));
53+
} catch (FileGenerationAbortedException|FileGenerationFailedException|InvalidArgumentException $e) {
54+
$this->error($e->getMessage());
55+
}
56+
}
57+
58+
private function getStubFileFromMiddlewareType(MiddlewareType $middlewareType): StubFile
59+
{
60+
return match ($middlewareType) {
61+
MiddlewareType::CONSOLE => StubFile::from(ConsoleMiddlewareStub::class),
62+
MiddlewareType::HTTP => StubFile::from(HttpMiddlewareStub::class),
63+
MiddlewareType::EVENT_BUS => StubFile::from(EventBusMiddlewareStub::class),
64+
MiddlewareType::COMMAND_BUS => StubFile::from(CommandBusMiddlewareStub::class), // @phpstan-ignore match.alwaysTrue (Because this is a guardrail for the future implementations)
65+
default => throw new InvalidArgumentException(sprintf('The "%s" middleware type has no supported stub file.', $middlewareType->value)),
66+
};
67+
}
68+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Tempest\Console\Enums;
6+
7+
use Tempest\CommandBus\CommandBusMiddleware;
8+
use Tempest\Console\ConsoleMiddleware;
9+
use Tempest\EventBus\EventBusMiddleware;
10+
use Tempest\Router\HttpMiddleware;
11+
12+
/**
13+
* Represents available middleware types in Tempest.
14+
*/
15+
enum MiddlewareType: string
16+
{
17+
case CONSOLE = 'console';
18+
case HTTP = 'http';
19+
case EVENT_BUS = 'event-bus';
20+
case COMMAND_BUS = 'command-bus';
21+
22+
/**
23+
* Get the related interface for the middleware type.
24+
*
25+
* @return class-string
26+
*/
27+
public function relatedInterface(): string
28+
{
29+
return match ($this) {
30+
self::CONSOLE => ConsoleMiddleware::class,
31+
self::HTTP => HttpMiddleware::class,
32+
self::EVENT_BUS => EventBusMiddleware::class,
33+
self::COMMAND_BUS => CommandBusMiddleware::class,
34+
};
35+
}
36+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Tempest\Console\Stubs;
6+
7+
use Tempest\CommandBus\CommandBusMiddleware;
8+
use Tempest\CommandBus\CommandBusMiddlewareCallable;
9+
10+
final class CommandBusMiddlewareStub implements CommandBusMiddleware
11+
{
12+
public function __invoke(object $command, CommandBusMiddlewareCallable $next): void
13+
{
14+
$next($command);
15+
}
16+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Tempest\Console\Stubs;
6+
7+
use Tempest\Console\ConsoleMiddleware;
8+
use Tempest\Console\ConsoleMiddlewareCallable;
9+
use Tempest\Console\ExitCode;
10+
use Tempest\Console\HasConsole;
11+
use Tempest\Console\Initializers\Invocation;
12+
13+
final class ConsoleMiddlewareStub implements ConsoleMiddleware
14+
{
15+
use HasConsole;
16+
17+
public function __invoke(Invocation $invocation, ConsoleMiddlewareCallable $next): ExitCode|int
18+
{
19+
return $next($invocation);
20+
}
21+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Tempest\Console\Stubs;
6+
7+
use Tempest\EventBus\EventBusMiddleware;
8+
use Tempest\EventBus\EventBusMiddlewareCallable;
9+
10+
final class EventBusMiddlewareStub implements EventBusMiddleware
11+
{
12+
public function __invoke(object $event, EventBusMiddlewareCallable $next): void
13+
{
14+
$next($event);
15+
}
16+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Tempest\Console\Stubs;
6+
7+
use Tempest\Router\HttpMiddleware;
8+
use Tempest\Router\HttpMiddlewareCallable;
9+
use Tempest\Router\Request;
10+
use Tempest\Router\Response;
11+
12+
final class HttpMiddlewareStub implements HttpMiddleware
13+
{
14+
public function __invoke(Request $request, HttpMiddlewareCallable $next): Response
15+
{
16+
return $next($request);
17+
}
18+
}
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Tests\Tempest\Integration\Console\Commands;
6+
7+
use PHPUnit\Framework\Attributes\DataProvider;
8+
use PHPUnit\Framework\Attributes\Test;
9+
use Tempest\Console\Enums\MiddlewareType;
10+
use Tempest\Core\ComposerNamespace;
11+
use Tests\Tempest\Integration\FrameworkIntegrationTestCase;
12+
use function Tempest\Support\str;
13+
14+
/**
15+
* @internal
16+
*/
17+
final class MakeMiddlewareCommandTest extends FrameworkIntegrationTestCase
18+
{
19+
protected function setUp(): void
20+
{
21+
parent::setUp();
22+
23+
$this->installer->configure(
24+
__DIR__ . '/install',
25+
new ComposerNamespace('App\\', __DIR__ . '/install/App'),
26+
);
27+
}
28+
29+
protected function tearDown(): void
30+
{
31+
$this->installer->clean();
32+
33+
parent::tearDown();
34+
}
35+
36+
#[DataProvider('command_input_provider')]
37+
#[Test]
38+
public function make_command(
39+
string $commandArgs,
40+
string $expectedPath,
41+
string $expectedNamespace,
42+
): void {
43+
$this->console
44+
->call("make:middleware {$commandArgs}")
45+
->submit();
46+
47+
$this->installer
48+
->assertFileExists($expectedPath)
49+
->assertFileContains($expectedPath, 'namespace ' . $expectedNamespace . ';');
50+
}
51+
52+
public static function command_input_provider(): array
53+
{
54+
return [
55+
'make_with_defaults' => [
56+
'commandArgs' => 'BookMiddleware http',
57+
'expectedPath' => 'App/BookMiddleware.php',
58+
'expectedNamespace' => 'App',
59+
],
60+
'make_with_other_namespace' => [
61+
'commandArgs' => 'Middlewares\\BookMiddleware http',
62+
'expectedPath' => 'App/Middlewares/BookMiddleware.php',
63+
'expectedNamespace' => 'App\\Middlewares',
64+
],
65+
'make_with_input_path' => [
66+
'commandArgs' => 'Middlewares/BookMiddleware http',
67+
'expectedPath' => 'App/Middlewares/BookMiddleware.php',
68+
'expectedNamespace' => 'App\\Middlewares',
69+
],
70+
];
71+
}
72+
73+
#[DataProvider('middleware_type_provider')]
74+
#[Test]
75+
public function make_command_with_each_type(
76+
MiddlewareType $middlewareType,
77+
string $middlewareInterface,
78+
): void {
79+
$this->console
80+
->call("make:middleware TestMiddleware {$middlewareType->value}")
81+
->submit();
82+
83+
$filepath = 'App/TestMiddleware.php';
84+
$middlewareInterface = str($middlewareInterface)->classBasename()->toString();
85+
$this->installer
86+
->assertFileExists($filepath)
87+
->assertFileContains($filepath, 'implements ' . $middlewareInterface);
88+
}
89+
90+
public static function middleware_type_provider(): array
91+
{
92+
$cases = MiddlewareType::cases();
93+
94+
return array_combine(
95+
keys: array_map(fn (MiddlewareType $case) => $case->value, $cases),
96+
values: array_map(fn (MiddlewareType $case) => [
97+
'middlewareType' => $case,
98+
'middlewareInterface' => $case->relatedInterface(),
99+
], $cases),
100+
);
101+
}
102+
}

0 commit comments

Comments
 (0)