Skip to content
This repository was archived by the owner on Sep 5, 2023. It is now read-only.
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions core/src/Module/Api/Request/RequestDTOFactory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<?php

namespace Harmony\Core\Module\Api\Request;

use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\Serializer\Encoder\JsonEncoder;
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
use Symfony\Component\Serializer\Serializer;

class RequestDTOFactory {
public function __construct(protected RequestStack $requestStack) {
}

public function __invoke(string $dtoName): RequestDTOInterface {
$request = $this->requestStack->getCurrentRequest();
$requestFromJson = $this->deserializeJsonToRequestDTO(
(string) $request?->getContent(),
$dtoName,
);

return $requestFromJson;
}

protected function deserializeJsonToRequestDTO(
string $jsonData,
string $classNameRequest,
): RequestDTOInterface {
$encoders = [new JsonEncoder()];
$normalizers = [new ObjectNormalizer()];
$serializer = new Serializer($normalizers, $encoders);

// Serializer throw an error on empty JSON
if (empty($jsonData)) {
$jsonData = "[]";
}

/** @var RequestDTOInterface $jsonRequest */
$jsonRequest = $serializer->deserialize(
$jsonData,
$classNameRequest,
"json",
);

return $jsonRequest;
}
}
6 changes: 6 additions & 0 deletions core/src/Module/Api/Request/RequestDTOInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?php

namespace Harmony\Core\Module\Api\Request;

interface RequestDTOInterface {
}
9 changes: 9 additions & 0 deletions core/src/Module/Api/Response/JsonResponse.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php

namespace Harmony\Core\Module\Api\Response;

/**
* @psalm-suppress PropertyNotSetInConstructor
*/
class JsonResponse extends \Symfony\Component\HttpFoundation\JsonResponse {
}
18 changes: 18 additions & 0 deletions core/src/Module/Api/Response/JsonResponse400.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php

namespace Harmony\Core\Module\Api\Response;

/**
* @psalm-suppress PropertyNotSetInConstructor
*/
class JsonResponse400 extends JsonResponse {
public function __construct(string $errors = "") {
parent::__construct(
[
"error" => "Bad Request",
"errors" => $errors,
],
400,
);
}
}
17 changes: 17 additions & 0 deletions core/src/Module/Api/Response/JsonResponse401.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

namespace Harmony\Core\Module\Api\Response;

/**
* @psalm-suppress PropertyNotSetInConstructor
*/
class JsonResponse401 extends JsonResponse {
public function __construct() {
parent::__construct(
[
"error" => "Auth Required",
],
401,
);
}
}
17 changes: 17 additions & 0 deletions core/src/Module/Api/Response/JsonResponse403.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

namespace Harmony\Core\Module\Api\Response;

/**
* @psalm-suppress PropertyNotSetInConstructor
*/
class JsonResponse403 extends JsonResponse {
public function __construct() {
parent::__construct(
[
"error" => "Forbidden",
],
403,
);
}
}
17 changes: 17 additions & 0 deletions core/src/Module/Api/Response/JsonResponse404.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

namespace Harmony\Core\Module\Api\Response;

/**
* @psalm-suppress PropertyNotSetInConstructor
*/
class JsonResponse404 extends JsonResponse {
public function __construct() {
parent::__construct(
[
"error" => "Not Found",
],
404,
);
}
}
8 changes: 8 additions & 0 deletions core/src/Module/Auth/Errors/UserNotAuthenticatedException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?php

namespace Harmony\Core\Module\Auth\Errors;

use Exception;

class UserNotAuthenticatedException extends Exception {
}
27 changes: 27 additions & 0 deletions core/src/Module/Doctrine/MongoDB/TraitCreatedAt.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

namespace Harmony\Core\Module\Doctrine\MongoDB;

use Carbon\CarbonImmutable;
use DateTimeImmutable;
use Doctrine\ODM\MongoDB\Event\LifecycleEventArgs;
use Doctrine\ODM\MongoDB\Mapping\Annotations as MongoDB;
use Doctrine\ODM\MongoDB\Mapping\Annotations\HasLifecycleCallbacks;
use Doctrine\ODM\MongoDB\Mapping\Annotations\PrePersist;

/**
* @HasLifecycleCallbacks
*/
trait TraitCreatedAt {
#[MongoDB\Field(type: "date_immutable")]
private ?DateTimeImmutable $created_at;

public function getCreatedAt(): ?DateTimeImmutable {
return $this->created_at;
}

#[PrePersist]
public function prePersistCreatedAt(LifecycleEventArgs $eventArgs): void {
$this->created_at = new CarbonImmutable();
}
}
9 changes: 9 additions & 0 deletions core/src/Module/Routing/Method.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php

namespace Harmony\Core\Module\Routing;

class Method {
public const GET = "GET";
public const POST = "POST";
public const PUT = "PUT";
}
15 changes: 15 additions & 0 deletions core/src/Module/Routing/Route.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php

namespace Harmony\Core\Module\Routing;

class Route {
public function __construct(
public readonly string $name,
public readonly string $path,
public readonly string $controllerAction,
public readonly ?string $requestDTO = null,
/** @var string[] */
public readonly array $methods = [Method::GET],
) {
}
}
10 changes: 10 additions & 0 deletions core/src/Module/Routing/RoutesInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

namespace Harmony\Core\Module\Routing;

interface RoutesInterface {
/**
* @return Route[]
*/
public function getRoutes(): array;
}
165 changes: 165 additions & 0 deletions core/src/Module/Symfony/BaseKernel.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
<?php

namespace Harmony\Core\Module\Symfony;

use Harmony\Core\Module\Api\Request\RequestDTOFactory;
use Harmony\Core\Module\Routing\RoutesInterface;
use Harmony\Core\Module\Symfony\DependencyInjection\SymfonyModule;
use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
use Symfony\Component\HttpKernel\Kernel;
use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator;
use function Symfony\Component\DependencyInjection\Loader\Configurator\service;

abstract class BaseKernel extends Kernel {
use MicroKernelTrait;

/** @var \Harmony\Core\Module\Routing\Route[] */
protected array $routes = [];

/**
* @return SymfonyModule[]
*/
abstract protected function getModules(): array;

/**
* @return \Harmony\Core\Module\Routing\Route[]
*/
abstract protected function getRouters(): array;

abstract protected function getRootFolder(): string;

/**
* @psalm-suppress UndefinedClass
* @return \Harmony\Core\Module\Routing\Route[]
*/
protected function getRoutersModule(): array {
if (!empty($this->routes)) {
return $this->routes;
}

$registerRoutes = $this->getRouters();

foreach ($registerRoutes as $classWithRoutes) {
/** @var RoutesInterface $routesContainer */
$routesContainer = new $classWithRoutes();
$routes = $routesContainer->getRoutes();
array_push($this->routes, ...$routes);

unset($routesContainer, $routes);
}

return $this->routes;
}

/**
* @psalm-suppress UndefinedClass
*/
protected function configureContainer(
ContainerConfigurator $container,
): void {
$this->configureSymfonyContainer($container);

$registerModules = $this->getModules();

foreach ($registerModules as $moduleProvider) {
$module = new $moduleProvider();
$module->register($container->services());
unset($module);
}

$this->registerControllerActions($container);
$this->registerRequestDTOs($container);
}

protected function registerControllerActions(
ContainerConfigurator $container,
): void {
foreach ($this->getRoutersModule() as $route) {
$container
->services()
->set($route->controllerAction)
->autowire()
->tag("controller.service_arguments");

unset($route);
}
}

protected function registerRequestDTOs(
ContainerConfigurator $container,
): void {
$container
->services()
->set(RequestDTOFactory::class)
->autowire();

foreach ($this->getRoutersModule() as $route) {
if (empty($route->requestDTO)) {
continue;
}

$container
->services()
->set($route->requestDTO)
->factory(service(RequestDTOFactory::class))
->args([$route->requestDTO]);

unset($route);
}
}

protected function configureRoutes(RoutingConfigurator $routes): void {
$this->configureSymfonyRoutes($routes);

foreach ($this->getRoutersModule() as $route) {
$routes
->add($route->name, $route->path)
->controller($route->controllerAction)
->methods($route->methods);

unset($route);
}
}

/**
* @psalm-suppress UnresolvableInclude
*/
protected function configureSymfonyContainer(
ContainerConfigurator $container,
): void {
$rootFolder = $this->getRootFolder();

$container->import("../config/{packages}/*.yaml");
$container->import(
"../config/{packages}/" . $this->environment . "/*.yaml",
);

if (is_file($rootFolder . "/config/services.yaml")) {
$container->import("../config/services.yaml");
$container->import(
"../config/{services}_" . $this->environment . ".yaml",
);
} elseif (is_file($path = $rootFolder . "/config/services.php")) {
// @phpstan-ignore-next-line
require $path($container->withPath($path), $this);
}
}

/**
* @psalm-suppress UnresolvableInclude
*/
protected function configureSymfonyRoutes(RoutingConfigurator $routes): void {
$rootFolder = $this->getRootFolder();

$routes->import("../config/{routes}/" . $this->environment . "/*.yaml");
$routes->import("../config/{routes}/*.yaml");

if (is_file($rootFolder . "/config/routes.yaml")) {
$routes->import("../config/routes.yaml");
} elseif (is_file($path = $rootFolder . "/config/routes.php")) {
// @phpstan-ignore-next-line
require $path($routes->withPath($path), $this);
}
}
}
20 changes: 20 additions & 0 deletions core/src/Module/Symfony/Route.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

namespace Harmony\Core\Module\Symfony;

use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator;

abstract class Route {
public const GET = "GET";
public const POST = "POST";
public const PUT = "PUT";
public const DELETE = "DELETE";

protected RoutingConfigurator $routes;

public function __construct(RoutingConfigurator $routes) {
$this->routes = $routes;
}

abstract public function register(): void;
}