You've already forked Php-Template
feat: enhance service providers with provides method and integrate command bus in handlers
All checks were successful
🧪✨ Tests Workflow / 🛡️ 🔒 License Check (push) Successful in 2m40s
🧪✨ Tests Workflow / 📝 ✨ Code Lint (push) Successful in 2m42s
🧪✨ Tests Workflow / 🛡️ 🔒 Library Audit (push) Successful in 2m53s
🧪✨ Tests Workflow / 🧪 ✨ Database Migrations (push) Successful in 2m55s
🧪✨ Tests Workflow / 🐙 🔍 Code Sniffer (push) Successful in 2m42s
🧪✨ Tests Workflow / 🧪 ✅ Unit Tests (push) Successful in 1m24s
All checks were successful
🧪✨ Tests Workflow / 🛡️ 🔒 License Check (push) Successful in 2m40s
🧪✨ Tests Workflow / 📝 ✨ Code Lint (push) Successful in 2m42s
🧪✨ Tests Workflow / 🛡️ 🔒 Library Audit (push) Successful in 2m53s
🧪✨ Tests Workflow / 🧪 ✨ Database Migrations (push) Successful in 2m55s
🧪✨ Tests Workflow / 🐙 🔍 Code Sniffer (push) Successful in 2m42s
🧪✨ Tests Workflow / 🧪 ✅ Unit Tests (push) Successful in 1m24s
This commit is contained in:
@@ -101,7 +101,9 @@ services:
|
|||||||
- ..:/app
|
- ..:/app
|
||||||
build:
|
build:
|
||||||
args:
|
args:
|
||||||
KAFKA_ENABLED: "1"
|
KAFKA_ENABLED: "0"
|
||||||
|
UID: 0
|
||||||
|
USER: root
|
||||||
context: ..
|
context: ..
|
||||||
dockerfile: Dockerfile
|
dockerfile: Dockerfile
|
||||||
entrypoint: "/bin/sh -c 'while true; do sleep 30; done;'"
|
entrypoint: "/bin/sh -c 'while true; do sleep 30; done;'"
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# Use the RoadRunner image as a base for the first stage
|
# Use the RoadRunner image as a base for the first stage
|
||||||
FROM ghcr.io/roadrunner-server/roadrunner:2025.1.4 AS roadrunner
|
FROM ghcr.io/roadrunner-server/roadrunner:2025.1.6 AS roadrunner
|
||||||
|
|
||||||
# Use the official Composer image as the base for the library stage
|
# Use the official Composer image as the base for the library stage
|
||||||
FROM siteworxpro/composer AS library
|
FROM siteworxpro/composer AS library
|
||||||
@@ -15,6 +15,8 @@ RUN composer install --optimize-autoloader --ignore-platform-reqs --no-dev
|
|||||||
FROM siteworxpro/php:8.5.0-cli-alpine AS php
|
FROM siteworxpro/php:8.5.0-cli-alpine AS php
|
||||||
|
|
||||||
ARG KAFKA_ENABLED=0
|
ARG KAFKA_ENABLED=0
|
||||||
|
ARG USER=appuser
|
||||||
|
ARG UID=1000
|
||||||
|
|
||||||
# Move the production PHP configuration file to the default location
|
# Move the production PHP configuration file to the default location
|
||||||
RUN mv /usr/local/etc/php/php.ini-production /usr/local/etc/php/php.ini \
|
RUN mv /usr/local/etc/php/php.ini-production /usr/local/etc/php/php.ini \
|
||||||
@@ -49,8 +51,9 @@ ADD server.php cli.php grpc-worker.php .rr.yaml config.php ./
|
|||||||
|
|
||||||
EXPOSE 9501 9001
|
EXPOSE 9501 9001
|
||||||
|
|
||||||
RUN addgroup -g 1000 appuser && adduser -D -u 1000 -G appuser appuser && chown -R appuser:appuser /app
|
# Create a non-root user and set ownership of the /app directory
|
||||||
USER appuser
|
RUN if [ ! $UID -eq 0 ] ; then addgroup -g $UID $USER && adduser -D -u $UID -G $USER $USER && chown -R $USER:$USER /app ; fi
|
||||||
|
USER $USER
|
||||||
|
|
||||||
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s \
|
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s \
|
||||||
CMD curl -f http://localhost:9501/healthz || exit 1
|
CMD curl -f http://localhost:9501/healthz || exit 1
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ declare(strict_types=1);
|
|||||||
namespace Siteworxpro\App\Cli\Commands;
|
namespace Siteworxpro\App\Cli\Commands;
|
||||||
|
|
||||||
use Ahc\Cli\Input\Command;
|
use Ahc\Cli\Input\Command;
|
||||||
|
use Siteworxpro\App\CommandBus\Commands\ExampleCommand;
|
||||||
|
use Siteworxpro\App\Services\Facades\CommandBus;
|
||||||
|
|
||||||
class DemoCommand extends Command implements CommandInterface
|
class DemoCommand extends Command implements CommandInterface
|
||||||
{
|
{
|
||||||
@@ -28,18 +30,14 @@ class DemoCommand extends Command implements CommandInterface
|
|||||||
$pb->finish();
|
$pb->finish();
|
||||||
|
|
||||||
$this->writer()->boldBlue("Demo Command Executed!\n");
|
$this->writer()->boldBlue("Demo Command Executed!\n");
|
||||||
|
|
||||||
if ($this->values()['name']) {
|
|
||||||
$name = $this->values()['name'];
|
$name = $this->values()['name'];
|
||||||
$greet = $this->values()['greet'] ?? false;
|
$greet = $this->values()['greet'] ?? false;
|
||||||
} else {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($greet) {
|
if ($greet) {
|
||||||
$this->writer()->green("Hello, $name! Welcome to the CLI demo.\n");
|
$this->writer()->green("Hello, $name! Welcome to the CLI demo.\n");
|
||||||
} else {
|
} else {
|
||||||
$this->writer()->yellow("Name provided: {$name}\n");
|
$exampleCommand = new ExampleCommand($name);
|
||||||
|
$this->writer()->yellow(CommandBus::handle($exampleCommand));
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@@ -7,11 +7,13 @@ namespace Siteworxpro\App\Controllers;
|
|||||||
use Nyholm\Psr7\ServerRequest;
|
use Nyholm\Psr7\ServerRequest;
|
||||||
use Psr\Http\Message\ResponseInterface;
|
use Psr\Http\Message\ResponseInterface;
|
||||||
use Siteworxpro\App\Attributes\Guards;
|
use Siteworxpro\App\Attributes\Guards;
|
||||||
|
use Siteworxpro\App\CommandBus\Commands\ExampleCommand;
|
||||||
use Siteworxpro\App\Docs\TokenSecurity;
|
use Siteworxpro\App\Docs\TokenSecurity;
|
||||||
use Siteworxpro\App\Docs\UnauthorizedResponse;
|
use Siteworxpro\App\Docs\UnauthorizedResponse;
|
||||||
use Siteworxpro\App\Http\JsonResponseFactory;
|
use Siteworxpro\App\Http\JsonResponseFactory;
|
||||||
use OpenApi\Attributes as OA;
|
use OpenApi\Attributes as OA;
|
||||||
use Siteworxpro\App\Http\Responses\GenericResponse;
|
use Siteworxpro\App\Http\Responses\GenericResponse;
|
||||||
|
use Siteworxpro\App\Services\Facades\CommandBus;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class IndexController
|
* Class IndexController
|
||||||
@@ -37,7 +39,10 @@ class IndexController extends Controller
|
|||||||
#[UnauthorizedResponse]
|
#[UnauthorizedResponse]
|
||||||
public function get(ServerRequest $request): ResponseInterface
|
public function get(ServerRequest $request): ResponseInterface
|
||||||
{
|
{
|
||||||
return JsonResponseFactory::createJsonResponse(new GenericResponse('Server is running'));
|
$command = new ExampleCommand($request->getQueryParams()['name'] ?? 'Guest');
|
||||||
|
$greeting = CommandBus::handle($command);
|
||||||
|
|
||||||
|
return JsonResponseFactory::createJsonResponse(new GenericResponse('Server is running. ' . $greeting));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -7,14 +7,18 @@ namespace Siteworxpro\App\GrpcHandlers;
|
|||||||
use GRPC\Greeter\GreeterInterface;
|
use GRPC\Greeter\GreeterInterface;
|
||||||
use GRPC\Greeter\HelloReply;
|
use GRPC\Greeter\HelloReply;
|
||||||
use GRPC\Greeter\HelloRequest;
|
use GRPC\Greeter\HelloRequest;
|
||||||
|
use Siteworxpro\App\CommandBus\Commands\ExampleCommand;
|
||||||
|
use Siteworxpro\App\Services\Facades\CommandBus;
|
||||||
use Spiral\RoadRunner\GRPC;
|
use Spiral\RoadRunner\GRPC;
|
||||||
|
|
||||||
class GreeterHandler implements GreeterInterface
|
class GreeterHandler implements GreeterInterface
|
||||||
{
|
{
|
||||||
public function SayHello(GRPC\ContextInterface $ctx, HelloRequest $in): HelloReply // phpcs:ignore
|
public function SayHello(GRPC\ContextInterface $ctx, HelloRequest $in): HelloReply // phpcs:ignore
|
||||||
{
|
{
|
||||||
|
$command = new ExampleCommand($in->getName());
|
||||||
|
|
||||||
$reply = new HelloReply();
|
$reply = new HelloReply();
|
||||||
$reply->setMessage('Hello ' . $in->getName());
|
$reply->setMessage(CommandBus::handle($command));
|
||||||
|
|
||||||
return $reply;
|
return $reply;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,24 +55,6 @@ class Facade
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Convert the facade into a Mockery spy.
|
|
||||||
*
|
|
||||||
* @return HigherOrderTapProxy | MockInterface
|
|
||||||
*/
|
|
||||||
public static function spy(): HigherOrderTapProxy | MockInterface
|
|
||||||
{
|
|
||||||
if (! static::isMock()) {
|
|
||||||
$class = static::getMockableClass();
|
|
||||||
|
|
||||||
return tap($class ? Mockery::spy($class) : Mockery::spy(), function ($spy) {
|
|
||||||
static::swap($spy);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new \RuntimeException('Cannot spy on an existing mock instance.');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initiate a partial mock on the facade.
|
* Initiate a partial mock on the facade.
|
||||||
*
|
*
|
||||||
@@ -189,19 +171,6 @@ class Facade
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Determines whether a "fake" has been set as the facade instance.
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public static function isFake(): bool
|
|
||||||
{
|
|
||||||
$name = static::getFacadeAccessor();
|
|
||||||
|
|
||||||
return isset(static::$resolvedInstance[$name]) &&
|
|
||||||
static::$resolvedInstance[$name] instanceof Fake;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the root object behind the facade.
|
* Get the root object behind the facade.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -6,6 +6,10 @@ namespace Siteworxpro\App\Services\ServiceProviders;
|
|||||||
|
|
||||||
use Illuminate\Support\ServiceProvider;
|
use Illuminate\Support\ServiceProvider;
|
||||||
use Siteworxpro\App\Async\Brokers\Broker;
|
use Siteworxpro\App\Async\Brokers\Broker;
|
||||||
|
use Siteworxpro\App\Async\Brokers\Kafka;
|
||||||
|
use Siteworxpro\App\Async\Brokers\RabbitMQ;
|
||||||
|
use Siteworxpro\App\Async\Brokers\Redis;
|
||||||
|
use Siteworxpro\App\Async\Brokers\Sqs;
|
||||||
use Siteworxpro\App\Services\Facades\Config;
|
use Siteworxpro\App\Services\Facades\Config;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -18,6 +22,11 @@ use Siteworxpro\App\Services\Facades\Config;
|
|||||||
*/
|
*/
|
||||||
class BrokerServiceProvider extends ServiceProvider
|
class BrokerServiceProvider extends ServiceProvider
|
||||||
{
|
{
|
||||||
|
public function provides(): array
|
||||||
|
{
|
||||||
|
return [Kafka::class, RabbitMQ::class, Redis::class, Sqs::class];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register services.
|
* Register services.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -18,6 +18,11 @@ use Siteworxpro\App\CommandBus\AttributeLocator;
|
|||||||
*/
|
*/
|
||||||
class CommandBusProvider extends ServiceProvider
|
class CommandBusProvider extends ServiceProvider
|
||||||
{
|
{
|
||||||
|
public function provides(): array
|
||||||
|
{
|
||||||
|
return [CommandBus::class];
|
||||||
|
}
|
||||||
|
|
||||||
public function register(): void
|
public function register(): void
|
||||||
{
|
{
|
||||||
$this->app->singleton(CommandBus::class, function () {
|
$this->app->singleton(CommandBus::class, function () {
|
||||||
|
|||||||
@@ -14,6 +14,11 @@ use Siteworxpro\App\Events\Dispatcher;
|
|||||||
*/
|
*/
|
||||||
class DispatcherServiceProvider extends ServiceProvider
|
class DispatcherServiceProvider extends ServiceProvider
|
||||||
{
|
{
|
||||||
|
public function provides(): array
|
||||||
|
{
|
||||||
|
return [Dispatcher::class];
|
||||||
|
}
|
||||||
|
|
||||||
public function register(): void
|
public function register(): void
|
||||||
{
|
{
|
||||||
$this->app->singleton(Dispatcher::class, function () {
|
$this->app->singleton(Dispatcher::class, function () {
|
||||||
|
|||||||
@@ -15,6 +15,11 @@ use Siteworxpro\App\Services\Facades\Config;
|
|||||||
*/
|
*/
|
||||||
class LoggerServiceProvider extends ServiceProvider
|
class LoggerServiceProvider extends ServiceProvider
|
||||||
{
|
{
|
||||||
|
public function provides(): array
|
||||||
|
{
|
||||||
|
return [Logger::class];
|
||||||
|
}
|
||||||
|
|
||||||
public function register(): void
|
public function register(): void
|
||||||
{
|
{
|
||||||
$this->app->singleton(Logger::class, function () {
|
$this->app->singleton(Logger::class, function () {
|
||||||
|
|||||||
@@ -17,6 +17,11 @@ use Siteworxpro\App\Services\Facades\Config;
|
|||||||
*/
|
*/
|
||||||
class RedisServiceProvider extends ServiceProvider
|
class RedisServiceProvider extends ServiceProvider
|
||||||
{
|
{
|
||||||
|
public function provides(): array
|
||||||
|
{
|
||||||
|
return [Client::class];
|
||||||
|
}
|
||||||
|
|
||||||
public function register(): void
|
public function register(): void
|
||||||
{
|
{
|
||||||
$this->app->singleton(Client::class, function () {
|
$this->app->singleton(Client::class, function () {
|
||||||
|
|||||||
@@ -4,23 +4,31 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace Siteworxpro\Tests\Controllers;
|
namespace Siteworxpro\Tests\Controllers;
|
||||||
|
|
||||||
|
use League\Tactician\CommandBus;
|
||||||
use Siteworxpro\App\Controllers\IndexController;
|
use Siteworxpro\App\Controllers\IndexController;
|
||||||
|
|
||||||
class IndexControllerTest extends AbstractController
|
class IndexControllerTest extends AbstractController
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @throws \JsonException
|
* @throws \JsonException|\ReflectionException
|
||||||
*/
|
*/
|
||||||
public function testGet(): void
|
public function testGet(): void
|
||||||
{
|
{
|
||||||
$this->assertTrue(true);
|
$this->assertTrue(true);
|
||||||
|
|
||||||
|
$this->getContainer()->bind(CommandBus::class, function () {
|
||||||
|
return \Mockery::mock(CommandBus::class)
|
||||||
|
->shouldReceive('handle')
|
||||||
|
->andReturn('Hello World')
|
||||||
|
->getMock();
|
||||||
|
});
|
||||||
|
|
||||||
$controller = new IndexController();
|
$controller = new IndexController();
|
||||||
|
|
||||||
$response = $controller->get($this->getMockRequest());
|
$response = $controller->get($this->getMockRequest());
|
||||||
|
|
||||||
$this->assertEquals(200, $response->getStatusCode());
|
$this->assertEquals(200, $response->getStatusCode());
|
||||||
$this->assertEquals('{"message":"Server is running"}', (string)$response->getBody());
|
$this->assertEquals('{"message":"Server is running. Hello World"}', (string)$response->getBody());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -4,19 +4,32 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace Siteworxpro\Tests\GrpcHandlers;
|
namespace Siteworxpro\Tests\GrpcHandlers;
|
||||||
|
|
||||||
|
use GRPC\Greeter\HelloRequest;
|
||||||
|
use League\Tactician\CommandBus;
|
||||||
|
use Siteworxpro\App\GrpcHandlers\GreeterHandler;
|
||||||
use Siteworxpro\Tests\Unit;
|
use Siteworxpro\Tests\Unit;
|
||||||
use Spiral\RoadRunner\GRPC\ContextInterface;
|
use Spiral\RoadRunner\GRPC\ContextInterface;
|
||||||
|
|
||||||
class GreeterHandlerTest extends Unit
|
class GreeterHandlerTest extends Unit
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* @throws \ReflectionException
|
||||||
|
*/
|
||||||
public function testSayHello(): void
|
public function testSayHello(): void
|
||||||
{
|
{
|
||||||
$request = new \GRPC\Greeter\HelloRequest();
|
$this->getContainer()->bind(CommandBus::class, function () {
|
||||||
|
return \Mockery::mock(CommandBus::class)
|
||||||
|
->shouldReceive('handle')
|
||||||
|
->andReturn('Hello World')
|
||||||
|
->getMock();
|
||||||
|
});
|
||||||
|
|
||||||
|
$request = new HelloRequest();
|
||||||
$request->setName('World');
|
$request->setName('World');
|
||||||
|
|
||||||
$context = \Mockery::mock(ContextInterface::class);
|
$context = \Mockery::mock(ContextInterface::class);
|
||||||
|
|
||||||
$handler = new \Siteworxpro\App\GrpcHandlers\GreeterHandler();
|
$handler = new GreeterHandler();
|
||||||
$response = $handler->SayHello($context, $request);
|
$response = $handler->SayHello($context, $request);
|
||||||
$this->assertEquals('Hello World', $response->getMessage());
|
$this->assertEquals('Hello World', $response->getMessage());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,8 +28,9 @@ abstract class AbstractServiceProvider extends Unit
|
|||||||
$this->assertInstanceOf($providerClass, $provider);
|
$this->assertInstanceOf($providerClass, $provider);
|
||||||
$provider->register();
|
$provider->register();
|
||||||
|
|
||||||
$bindings = $provider->bindings;
|
$abstract = $provider->provides()[0];
|
||||||
foreach ($bindings as $abstract => $concrete) {
|
$concrete = get_class($container->make($abstract));
|
||||||
|
|
||||||
$this->assertTrue($container->bound($abstract), "The $abstract is not bound in the container.");
|
$this->assertTrue($container->bound($abstract), "The $abstract is not bound in the container.");
|
||||||
$this->assertNotNull($container->make($abstract), "The $abstract could not be resolved.");
|
$this->assertNotNull($container->make($abstract), "The $abstract could not be resolved.");
|
||||||
|
|
||||||
@@ -40,4 +41,3 @@ abstract class AbstractServiceProvider extends Unit
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|||||||
15
tests/ServiceProviders/CommandBusServiceProviderTest.php
Normal file
15
tests/ServiceProviders/CommandBusServiceProviderTest.php
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Siteworxpro\Tests\ServiceProviders;
|
||||||
|
|
||||||
|
use Siteworxpro\App\Services\ServiceProviders\CommandBusProvider;
|
||||||
|
|
||||||
|
class CommandBusServiceProviderTest extends AbstractServiceProvider
|
||||||
|
{
|
||||||
|
protected function getProviderClass(): string
|
||||||
|
{
|
||||||
|
return CommandBusProvider::class;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,6 +8,7 @@ use Illuminate\Container\Container;
|
|||||||
use Mockery;
|
use Mockery;
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
use Siteworx\Config\Config as SWConfig;
|
use Siteworx\Config\Config as SWConfig;
|
||||||
|
use Siteworxpro\App\Kernel;
|
||||||
use Siteworxpro\App\Services\Facade;
|
use Siteworxpro\App\Services\Facade;
|
||||||
use Siteworxpro\App\Services\Facades\Config;
|
use Siteworxpro\App\Services\Facades\Config;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user