Skip to content

Commit

Permalink
chore: Refactor upgrade subsystem
Browse files Browse the repository at this point in the history
Use attributes to detect and load upgrade handlers.

Signed-off-by: Rubén D <[email protected]>
  • Loading branch information
nuxsmin committed May 3, 2024
1 parent 0304221 commit 10dedaa
Show file tree
Hide file tree
Showing 20 changed files with 524 additions and 488 deletions.
12 changes: 6 additions & 6 deletions app/modules/api/Bootstrap.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
use SP\Domain\Api\Services\JsonRpcResponse;
use SP\Domain\Core\Bootstrap\BootstrapInterface;
use SP\Domain\Core\Bootstrap\ModuleInterface;
use SP\Domain\Http\Code;

use function SP\logger;
use function SP\processException;
Expand Down Expand Up @@ -73,18 +74,17 @@ private function manageApiRequest(): Closure
try {
logger('API route');

$apiRequest = $this->createObjectFor(ApiRequestService::class);
$response->headers()->set('Content-type', 'application/json; charset=utf-8');

$apiRequest = $this->buildInstanceFor(ApiRequestService::class);
[$controllerName, $actionName] = explode('/', $apiRequest->getMethod());

$controllerClass = self::getClassFor($controllerName, $actionName);

$method = $actionName . 'Action';

if (!method_exists($controllerClass, $method)) {
logger($controllerClass . '::' . $method);

$response->headers()->set('Content-type', 'application/json; charset=utf-8');
$response->code(Code::NOT_FOUND->value);

return $response->body(
JsonRpcResponse::getResponseError(
Expand All @@ -103,11 +103,11 @@ private function manageApiRequest(): Closure

logger('Routing call: ' . $controllerClass . '::' . $method);

return call_user_func([$this->createObjectFor($controllerClass), $method]);
return call_user_func([$this->buildInstanceFor($controllerClass), $method]);
} catch (Exception $e) {
processException($e);

$response->headers()->set('Content-type', 'application/json; charset=utf-8');
$response->code(Code::INTERNAL_SERVER_ERROR->value);

return $response->body(JsonRpcResponse::getResponseException($e, 0));
} finally {
Expand Down
16 changes: 10 additions & 6 deletions app/modules/web/Bootstrap.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,13 @@
use Klein\Response;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use RuntimeException;
use SP\Core\Bootstrap\BootstrapBase;
use SP\Core\Bootstrap\RouteContext;
use SP\Domain\Common\Providers\Filter;
use SP\Domain\Core\Bootstrap\BootstrapInterface;
use SP\Domain\Core\Bootstrap\ModuleInterface;
use SP\Domain\Core\Exceptions\SessionTimeout;
use SP\Domain\Http\Code;

use function SP\__;
use function SP\logger;
Expand Down Expand Up @@ -87,9 +87,10 @@ private function manageWebRequest(): Closure
if (!method_exists($controllerClass, $routeContextData->getMethodName())) {
logger($controllerClass . '::' . $routeContextData->getMethodName());

$response->code(404);
$response->code(Code::NOT_FOUND->value);
$response->append(self::OOPS_MESSAGE);

throw new RuntimeException(self::OOPS_MESSAGE);
return $response;
}

$this->context->setTrasientKey(self::CONTEXT_ACTION_NAME, $routeContextData->getActionName());
Expand All @@ -109,10 +110,8 @@ private function manageWebRequest(): Closure
)
);

$controller = $this->createObjectFor($controllerClass);

return call_user_func_array(
[$controller, $routeContextData->getMethodName()],
[$this->buildInstanceFor($controllerClass), $routeContextData->getMethodName()],
$routeContextData->getMethodParams()
);
} catch (SessionTimeout) {
Expand All @@ -125,7 +124,12 @@ private function manageWebRequest(): Closure
if (DEBUG) {
echo $e->getTraceAsString();
}

$response->code(Code::INTERNAL_SERVER_ERROR->value);
$response->append($e->getMessage());
}

return $response;
};
}
}
18 changes: 7 additions & 11 deletions lib/SP/Core/Bootstrap/BootstrapBase.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,6 @@
use SP\Domain\Core\Exceptions\ConfigException;
use SP\Domain\Core\Exceptions\InitializationException;
use SP\Domain\Http\Ports\RequestService;
use SP\Domain\Upgrade\Services\UpgradeConfig;
use SP\Infrastructure\File\FileException;
use Symfony\Component\Debug\Debug;
use Throwable;

Expand All @@ -61,9 +59,9 @@
*/
abstract class BootstrapBase implements BootstrapInterface
{
public const CONTEXT_ACTION_NAME = "_actionName";
public const CONTEXT_ACTION_NAME = '_actionName';

protected const OOPS_MESSAGE = "Oops, it looks like this content does not exist...";
protected const OOPS_MESSAGE = 'Oops, it looks like this content does not exist...';
public static mixed $LOCK;
public static bool $checkPhpVersion = false;

Expand Down Expand Up @@ -146,7 +144,6 @@ final protected function handleRequest(): void
/**
* @throws CheckException
* @throws ConfigException
* @throws FileException
* @throws InitializationException
*/
final protected function initializeCommon(): void
Expand Down Expand Up @@ -239,23 +236,22 @@ private function initPHPVars(): void
*/
private function initConfig(): void
{
UpgradeConfig::needsUpgrade($this->configData->getConfigVersion());

ConfigUtil::checkConfigDir();
}

/**
* @deprecated
* FIXME: delete
* @template T of object
* @param class-string<T> $class
* @return T&object
*/
final protected function createObjectFor(string $class): object
final protected function buildInstanceFor(string $class): object
{
try {
return $this->container->get($class);
} catch (NotFoundExceptionInterface|ContainerExceptionInterface $e) {
processException($e);

throw new RuntimeException($e);
throw new RuntimeException($e->getMessage());
}
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
<?php

declare(strict_types=1);
/*
/**
* sysPass
*
* @author nuxsmin
Expand All @@ -24,25 +22,19 @@
* along with sysPass. If not, see <http://www.gnu.org/licenses/>.
*/

namespace SP\Domain\Upgrade\Services;
declare(strict_types=1);

namespace SP\Domain\Common\Attributes;

use Attribute;

/**
* Class UpgradeApp
* Class UpgradeHandler
*/
final class UpgradeApp extends UpgradeBase
#[Attribute(Attribute::TARGET_CLASS | Attribute::IS_REPEATABLE)]
final class UpgradeVersion
{
protected static function getUpgrades(): array
{
return [];
}

protected function applyUpgrade(string $version): bool
{
return true;
}

protected function commitVersion(string $version): void
public function __construct(public readonly string $version)
{
$this->configData->setAppVersion($version);
}
}
17 changes: 11 additions & 6 deletions lib/SP/Domain/Common/Models/HydratableModel.php
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
<?php

declare(strict_types=1);
/*
* sysPass
Expand All @@ -25,6 +26,8 @@

namespace SP\Domain\Common\Models;

use SP\Domain\Core\Exceptions\SPException;

/**
* Interface HydratableModel
*/
Expand All @@ -33,18 +36,20 @@ interface HydratableModel
/**
* Deserialize the hydratable property and returns the object.
*
* @template T
* @param class-string<T> $class
* @template THydrate
* @param class-string<THydrate> $class
*
* @return T|null
* @return THydrate|null
* @throws SPException
*/
public function hydrate(string $class): ?object;

/**
* Serialize the object in the hydratable property
* @param object $object
*
* @return static A new instance of the model with the serialized property
* @param object $object
* @return static|null A new instance of the model with the serialized property or null if the property
* couldn't be serialized
*/
public function dehydrate(object $object): static;
public function dehydrate(object $object): static|null;
}
67 changes: 34 additions & 33 deletions lib/SP/Domain/Common/Models/SerializedModel.php
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
<?php

declare(strict_types=1);
/*
* sysPass
Expand Down Expand Up @@ -28,38 +29,41 @@
use ReflectionClass;
use SP\Domain\Common\Adapters\Serde;
use SP\Domain\Common\Attributes\Hydratable;
use SP\Domain\Core\Exceptions\SPException;

/**
* Trait SerializedModel
*/
trait SerializedModel
{
/**
* @template THydrate
* @param class-string<THydrate> $class
*
* @return THydrate|null
* @throws SPException
* @inheritDoc
*/
public function hydrate(string $class): ?object
{
$reflectionClass = new ReflectionClass($this);

foreach ($reflectionClass->getAttributes(Hydratable::class) as $attribute) {
/** @var Hydratable $instance */
$instance = $attribute->newInstance();
return $this->parseAttribute(
function (Hydratable $hydratable) use ($class) {
$valid = array_filter(
$hydratable->getTargetClass(),
static fn(string $targetClass) => is_a($class, $targetClass, true)
);

$valid = array_filter(
$instance->getTargetClass(),
static fn(string $targetClass) => is_a($class, $targetClass, true)
);
$property = $this->{$hydratable->getSourceProperty()};

$property = $this->{$instance->getSourceProperty()};
if (count($valid) > 0 && $property !== null) {
return Serde::deserialize($property, $class) ?: null;
}

if (count($valid) > 0 && $property !== null) {
return Serde::deserialize($property, $class) ?: null;
return null;
}
);
}

private function parseAttribute(callable $callback): mixed
{
$reflectionClass = new ReflectionClass($this);

foreach ($reflectionClass->getAttributes(Hydratable::class) as $attribute) {
return $callback($attribute->newInstance());
}

return null;
Expand All @@ -68,24 +72,21 @@ public function hydrate(string $class): ?object
/**
* @inheritDoc
*/
public function dehydrate(object $object): static
public function dehydrate(object $object): static|null
{
$reflectionClass = new ReflectionClass($this);
return $this->parseAttribute(
function (Hydratable $hydratable) use ($object) {
$valid = array_filter(
$hydratable->getTargetClass(),
static fn(string $targetClass) => is_a($object, $targetClass)
);

foreach ($reflectionClass->getAttributes(Hydratable::class) as $attribute) {
/** @var Hydratable $instance */
$instance = $attribute->newInstance();

$valid = array_filter(
$instance->getTargetClass(),
static fn(string $targetClass) => is_a($object, $targetClass, true)
);
if (count($valid) > 0) {
return $this->mutate([$hydratable->getSourceProperty() => Serde::serialize($object)]);
}

if (count($valid) > 0) {
return $this->mutate([$instance->getSourceProperty() => Serde::serialize($object)]);
return $this;
}
}

return $this;
);
}
}
44 changes: 44 additions & 0 deletions lib/SP/Domain/Http/Code.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<?php
/*
* sysPass
*
* @author nuxsmin
* @link https://syspass.org
* @copyright 2012-2024, Rubén Domínguez nuxsmin@$syspass.org
*
* This file is part of sysPass.
*
* sysPass is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* sysPass is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with sysPass. If not, see <http://www.gnu.org/licenses/>.
*/

namespace SP\Domain\Http;

/**
* Enum Code
*/
enum Code: int
{
case INTERNAL_SERVER_ERROR = 500;
case SERVICE_UNAVALIABLE = 503;
case BAD_REQUEST = 400;
case UNAUTHORIZED = 401;
case FORBIDDEN = 403;
case NOT_FOUND = 404;
case OK = 200;
case CREATED = 201;
case NO_CONTENT = 204;
case MOVED_PERMANENTLY = 301;
case FOUND = 302;
case NOT_MODIFIED = 304;
}
2 changes: 1 addition & 1 deletion lib/SP/Domain/Upgrade/Ports/UpgradeConfigService.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<?php
/*
/**
* sysPass
*
* @author nuxsmin
Expand Down
Loading

0 comments on commit 10dedaa

Please sign in to comment.