Skip to content

Commit

Permalink
feat(console): add make:middleware command (#804)
Browse files Browse the repository at this point in the history
  • Loading branch information
gturpin-dev authored Dec 11, 2024
1 parent 0f47a80 commit 467c664
Show file tree
Hide file tree
Showing 7 changed files with 277 additions and 0 deletions.
68 changes: 68 additions & 0 deletions src/Tempest/Console/src/Commands/MakeMiddlewareCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
<?php

declare(strict_types=1);

namespace Tempest\Console\Commands;

use InvalidArgumentException;
use Tempest\Console\ConsoleArgument;
use Tempest\Console\ConsoleCommand;
use Tempest\Console\Enums\MiddlewareType;
use Tempest\Console\Stubs\CommandBusMiddlewareStub;
use Tempest\Console\Stubs\ConsoleMiddlewareStub;
use Tempest\Console\Stubs\EventBusMiddlewareStub;
use Tempest\Console\Stubs\HttpMiddlewareStub;
use Tempest\Core\PublishesFiles;
use Tempest\Generation\DataObjects\StubFile;
use Tempest\Generation\Exceptions\FileGenerationAbortedException;
use Tempest\Generation\Exceptions\FileGenerationFailedException;

final class MakeMiddlewareCommand
{
use PublishesFiles;

#[ConsoleCommand(
name: 'make:middleware',
description: 'Creates a new middleware class',
aliases: ['middleware:make', 'middleware:create', 'create:middleware'],
)]
public function __invoke(
#[ConsoleArgument(
help: 'The name of the middleware class to create',
)]
string $className,
#[ConsoleArgument(
name: 'type',
help: 'The type of the middleware to create',
)]
MiddlewareType $middlewareType,
): void {
try {
$stubFile = $this->getStubFileFromMiddlewareType($middlewareType);
$suggestedPath = $this->getSuggestedPath($className);
$targetPath = $this->promptTargetPath($suggestedPath);
$shouldOverride = $this->askForOverride($targetPath);

$this->stubFileGenerator->generateClassFile(
stubFile: $stubFile,
targetPath: $targetPath,
shouldOverride: $shouldOverride,
);

$this->success(sprintf('Middleware successfully created at "%s".', $targetPath));
} catch (FileGenerationAbortedException|FileGenerationFailedException|InvalidArgumentException $e) {
$this->error($e->getMessage());
}
}

private function getStubFileFromMiddlewareType(MiddlewareType $middlewareType): StubFile
{
return match ($middlewareType) {
MiddlewareType::CONSOLE => StubFile::from(ConsoleMiddlewareStub::class),
MiddlewareType::HTTP => StubFile::from(HttpMiddlewareStub::class),
MiddlewareType::EVENT_BUS => StubFile::from(EventBusMiddlewareStub::class),
MiddlewareType::COMMAND_BUS => StubFile::from(CommandBusMiddlewareStub::class), // @phpstan-ignore match.alwaysTrue (Because this is a guardrail for the future implementations)
default => throw new InvalidArgumentException(sprintf('The "%s" middleware type has no supported stub file.', $middlewareType->value)),
};
}
}
36 changes: 36 additions & 0 deletions src/Tempest/Console/src/Enums/MiddlewareType.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php

declare(strict_types=1);

namespace Tempest\Console\Enums;

use Tempest\CommandBus\CommandBusMiddleware;
use Tempest\Console\ConsoleMiddleware;
use Tempest\EventBus\EventBusMiddleware;
use Tempest\Router\HttpMiddleware;

/**
* Represents available middleware types in Tempest.
*/
enum MiddlewareType: string
{
case CONSOLE = 'console';
case HTTP = 'http';
case EVENT_BUS = 'event-bus';
case COMMAND_BUS = 'command-bus';

/**
* Get the related interface for the middleware type.
*
* @return class-string
*/
public function relatedInterface(): string
{
return match ($this) {
self::CONSOLE => ConsoleMiddleware::class,
self::HTTP => HttpMiddleware::class,
self::EVENT_BUS => EventBusMiddleware::class,
self::COMMAND_BUS => CommandBusMiddleware::class,
};
}
}
16 changes: 16 additions & 0 deletions src/Tempest/Console/src/Stubs/CommandBusMiddlewareStub.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

declare(strict_types=1);

namespace Tempest\Console\Stubs;

use Tempest\CommandBus\CommandBusMiddleware;
use Tempest\CommandBus\CommandBusMiddlewareCallable;

final class CommandBusMiddlewareStub implements CommandBusMiddleware
{
public function __invoke(object $command, CommandBusMiddlewareCallable $next): void
{
$next($command);
}
}
21 changes: 21 additions & 0 deletions src/Tempest/Console/src/Stubs/ConsoleMiddlewareStub.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php

declare(strict_types=1);

namespace Tempest\Console\Stubs;

use Tempest\Console\ConsoleMiddleware;
use Tempest\Console\ConsoleMiddlewareCallable;
use Tempest\Console\ExitCode;
use Tempest\Console\HasConsole;
use Tempest\Console\Initializers\Invocation;

final class ConsoleMiddlewareStub implements ConsoleMiddleware
{
use HasConsole;

public function __invoke(Invocation $invocation, ConsoleMiddlewareCallable $next): ExitCode|int
{
return $next($invocation);
}
}
16 changes: 16 additions & 0 deletions src/Tempest/Console/src/Stubs/EventBusMiddlewareStub.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

declare(strict_types=1);

namespace Tempest\Console\Stubs;

use Tempest\EventBus\EventBusMiddleware;
use Tempest\EventBus\EventBusMiddlewareCallable;

final class EventBusMiddlewareStub implements EventBusMiddleware
{
public function __invoke(object $event, EventBusMiddlewareCallable $next): void
{
$next($event);
}
}
18 changes: 18 additions & 0 deletions src/Tempest/Console/src/Stubs/HttpMiddlewareStub.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php

declare(strict_types=1);

namespace Tempest\Console\Stubs;

use Tempest\Router\HttpMiddleware;
use Tempest\Router\HttpMiddlewareCallable;
use Tempest\Router\Request;
use Tempest\Router\Response;

final class HttpMiddlewareStub implements HttpMiddleware
{
public function __invoke(Request $request, HttpMiddlewareCallable $next): Response
{
return $next($request);
}
}
102 changes: 102 additions & 0 deletions tests/Integration/Console/Commands/MakeMiddlewareCommandTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
<?php

declare(strict_types=1);

namespace Tests\Tempest\Integration\Console\Commands;

use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\Attributes\Test;
use Tempest\Console\Enums\MiddlewareType;
use Tempest\Core\ComposerNamespace;
use Tests\Tempest\Integration\FrameworkIntegrationTestCase;
use function Tempest\Support\str;

/**
* @internal
*/
final class MakeMiddlewareCommandTest extends FrameworkIntegrationTestCase
{
protected function setUp(): void
{
parent::setUp();

$this->installer->configure(
__DIR__ . '/install',
new ComposerNamespace('App\\', __DIR__ . '/install/App'),
);
}

protected function tearDown(): void
{
$this->installer->clean();

parent::tearDown();
}

#[DataProvider('command_input_provider')]
#[Test]
public function make_command(
string $commandArgs,
string $expectedPath,
string $expectedNamespace,
): void {
$this->console
->call("make:middleware {$commandArgs}")
->submit();

$this->installer
->assertFileExists($expectedPath)
->assertFileContains($expectedPath, 'namespace ' . $expectedNamespace . ';');
}

public static function command_input_provider(): array
{
return [
'make_with_defaults' => [
'commandArgs' => 'BookMiddleware http',
'expectedPath' => 'App/BookMiddleware.php',
'expectedNamespace' => 'App',
],
'make_with_other_namespace' => [
'commandArgs' => 'Middlewares\\BookMiddleware http',
'expectedPath' => 'App/Middlewares/BookMiddleware.php',
'expectedNamespace' => 'App\\Middlewares',
],
'make_with_input_path' => [
'commandArgs' => 'Middlewares/BookMiddleware http',
'expectedPath' => 'App/Middlewares/BookMiddleware.php',
'expectedNamespace' => 'App\\Middlewares',
],
];
}

#[DataProvider('middleware_type_provider')]
#[Test]
public function make_command_with_each_type(
MiddlewareType $middlewareType,
string $middlewareInterface,
): void {
$this->console
->call("make:middleware TestMiddleware {$middlewareType->value}")
->submit();

$filepath = 'App/TestMiddleware.php';
$middlewareInterface = str($middlewareInterface)->classBasename()->toString();
$this->installer
->assertFileExists($filepath)
->assertFileContains($filepath, 'implements ' . $middlewareInterface);
}

public static function middleware_type_provider(): array
{
$cases = MiddlewareType::cases();

return array_combine(
keys: array_map(fn (MiddlewareType $case) => $case->value, $cases),
values: array_map(fn (MiddlewareType $case) => [
'middlewareType' => $case,
'middlewareInterface' => $case->relatedInterface(),
], $cases),
);
}
}

0 comments on commit 467c664

Please sign in to comment.