Spaces:
Running
Running
File size: 3,677 Bytes
b2dcf0f | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 | <?php
namespace App\Domain\Config;
use App\Domain\Audit\AuditService;
use App\Infrastructure\Ids\UuidGenerator;
use App\Shared\Exceptions\ApiException;
class ConfigService
{
public function __construct(
private readonly ConfigRepository $settings,
private readonly UuidGenerator $ids,
private readonly AuditService $audit,
) {
}
public function getNamespace(string $namespace): array
{
return [
'namespace' => $namespace,
'settings' => array_map(fn (array $row): array => $this->payload($row), $this->settings->listNamespace($namespace)),
];
}
public function update(string $namespace, string $key, mixed $value, string $actorUserId): array
{
$this->validateValue($namespace, $key, $value);
$id = $this->settings->createVersion($this->ids->generate(), $namespace, $key, $value);
$this->audit->record('CONFIG_SETTING_UPDATED', $actorUserId, null, 'configuration_settings', $id, 'SUCCESS', [
'namespace' => $namespace,
'key' => $key,
]);
return ['setting' => $this->payload((array) $this->settings->latest($namespace, $key))];
}
public function publish(string $namespace, string $key, string $actorUserId): array
{
$setting = $this->settings->latest($namespace, $key);
if ($setting === null) {
throw new ApiException('CONFIG_NOT_FOUND', 'Configuration setting was not found.', [], 404);
}
$this->settings->publish($setting->id, $actorUserId);
$this->audit->record('CONFIG_SETTING_PUBLISHED', $actorUserId, null, 'configuration_settings', $setting->id, 'SUCCESS', [
'namespace' => $namespace,
'key' => $key,
]);
return ['setting' => $this->payload((array) $this->settings->latest($namespace, $key))];
}
public function versions(string $namespace, string $key): array
{
return [
'namespace' => $namespace,
'key' => $key,
'versions' => array_map(fn (array $row): array => $this->payload($row), $this->settings->versions($namespace, $key)),
];
}
private function payload(array $row): array
{
return [
'id' => $row['id'],
'namespace' => $row['namespace'],
'key' => $row['setting_key'],
'value' => json_decode($row['setting_value'], true, flags: JSON_THROW_ON_ERROR),
'status' => $row['status'],
'version_number' => (int) $row['version_number'],
'published_at' => $row['published_at'],
'updated_at' => $row['updated_at'],
];
}
private function validateValue(string $namespace, string $key, mixed $value): void
{
if (str_contains(strtolower($key), 'secret') || str_contains(strtolower($key), 'password')) {
throw new ApiException('SECRET_VALUE_NOT_ALLOWED', 'Raw secrets must not be stored in configuration settings.', [], 422);
}
if (str_starts_with($namespace.'.'.$key, 'fees.') && (! is_numeric($value) || (float) $value < 0)) {
throw new ApiException('INVALID_CONFIG_VALUE', 'Fee values must be non-negative numbers.', [], 422);
}
if (str_contains($key, 'days') && (! is_int($value) || $value < 0)) {
throw new ApiException('INVALID_CONFIG_VALUE', 'Day values must be non-negative integers.', [], 422);
}
if (str_contains($key, 'minutes') && (! is_int($value) || $value < 1)) {
throw new ApiException('INVALID_CONFIG_VALUE', 'Minute values must be positive integers.', [], 422);
}
}
}
|